1绘盟、TCP
- 傳輸層的控制協(xié)議
- 面向連接的倍权、可靠的、基于字節(jié)流的傳輸層通信協(xié)議
- 與UDP一樣完成第四層傳輸層所指定的功能與職責(zé)
2囱修、應(yīng)用
- 聊天消息傳輸赎瑰、推送
- 單人語音、視頻聊天
- UDP能做的破镰,TCP幾乎都能做餐曼。需要考慮復(fù)雜性、性能問題
- 限制:無法廣播鲜漩、多播操作源譬;無法搜索客戶端
3、可靠性
- 連接可靠性:三次握手孕似,四次揮手
- 傳輸可靠性:排序踩娘、丟棄(超時(shí))、重發(fā)
4喉祭、Client
public class Client {
private static final int PORT = 20000;
private static final int LOCAL_PORT = 20001;
public static void main(String[] args) throws IOException {
Socket socket = createSocket();
initSocket(socket);
// 鏈接到本地20000端口(指連接服務(wù)器端口)养渴,超時(shí)時(shí)間3秒,超過則拋出超時(shí)異常
socket.connect(new InetSocketAddress(Inet4Address.getLocalHost(), PORT), 3000);
System.out.println("已發(fā)起服務(wù)器連接泛烙,并進(jìn)入后續(xù)流程~");
System.out.println("客戶端信息:" + socket.getLocalAddress() + " P:" + socket.getLocalPort());
System.out.println("服務(wù)器信息:" + socket.getInetAddress() + " P:" + socket.getPort());
try {
// 發(fā)送接收數(shù)據(jù)
todo(socket);
} catch (Exception e) {
System.out.println("異常關(guān)閉");
}
// 釋放資源
socket.close();
System.out.println("客戶端已退出~");
}
private static Socket createSocket() throws IOException {
Socket socket = new Socket();
// 綁定到本地20001端口(綁定客戶端端口理卑,不綁定的話會(huì)隨機(jī)分配一個(gè)端口給客戶端)
socket.bind(new InetSocketAddress(Inet4Address.getLocalHost(), LOCAL_PORT));
return socket;
}
private static void initSocket(Socket socket) throws SocketException {
// 設(shè)置讀取超時(shí)時(shí)間為2秒
socket.setSoTimeout(2000);
// 是否復(fù)用未完全關(guān)閉的Socket地址,對(duì)于指定bind操作后的套接字有效
socket.setReuseAddress(true);
// 是否開啟Nagle算法
socket.setTcpNoDelay(true);
// 是否需要在長(zhǎng)時(shí)無數(shù)據(jù)響應(yīng)時(shí)發(fā)送確認(rèn)數(shù)據(jù)(類似心跳包)蔽氨,時(shí)間大約為2小時(shí)
socket.setKeepAlive(true);
// 對(duì)于close關(guān)閉操作行為進(jìn)行怎樣的處理藐唠;默認(rèn)為false,0
// false鹉究、0:默認(rèn)情況中捆,關(guān)閉時(shí)立即返回,底層系統(tǒng)接管輸出流坊饶,將緩沖區(qū)內(nèi)的數(shù)據(jù)發(fā)送完成
// true泄伪、0:關(guān)閉時(shí)立即返回,緩沖區(qū)數(shù)據(jù)拋棄匿级,直接發(fā)送RST結(jié)束命令到對(duì)方蟋滴,并無需經(jīng)過2MSL等待
// true、200:關(guān)閉時(shí)最長(zhǎng)阻塞200毫秒痘绎,隨后按第二情況處理
socket.setSoLinger(true, 20);
// 是否讓緊急數(shù)據(jù)內(nèi)斂津函,默認(rèn)false;緊急數(shù)據(jù)通過 socket.sendUrgentData(1);發(fā)送
socket.setOOBInline(false);
// 設(shè)置接收發(fā)送緩沖器大小
socket.setReceiveBufferSize(64 * 1024 * 1024);
socket.setSendBufferSize(64 * 1024 * 1024);
// 設(shè)置性能參數(shù):短鏈接孤页,延遲尔苦,帶寬的相對(duì)重要性
socket.setPerformancePreferences(1, 1, 0);
}
private static void todo(Socket client) throws IOException {
// 得到Socket輸出流
OutputStream outputStream = client.getOutputStream();
// 得到Socket輸入流
InputStream inputStream = client.getInputStream();
byte[] buffer = new byte[256];
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
// byte
byteBuffer.put((byte) 126);
// char
char c = 'a';
byteBuffer.putChar(c);
// int
int i = 2323123;
byteBuffer.putInt(i);
// bool
boolean b = true;
byteBuffer.put(b ? (byte) 1 : (byte) 0);
// Long
long l = 298789739;
byteBuffer.putLong(l);
// float
float f = 12.345f;
byteBuffer.putFloat(f);
// double
double d = 13.31241248782973;
byteBuffer.putDouble(d);
// String
String str = "Hello你好!";
byteBuffer.put(str.getBytes());
// 發(fā)送到服務(wù)器
outputStream.write(buffer, 0, byteBuffer.position() + 1);
// 接收服務(wù)器返回
int read = inputStream.read(buffer);
System.out.println("收到數(shù)量:" + read);
// 資源釋放
outputStream.close();
inputStream.close();
}
}
5、Server
public class Server {
private static final int PORT = 20000;
public static void main(String[] args) throws IOException {
ServerSocket server = createServerSocket();
initServerSocket(server);
// 綁定到本地端口上(綁定調(diào)用相當(dāng)于服務(wù)已經(jīng)啟動(dòng)允坚,最大等待連接客戶端數(shù)為50魂那,超過則拋出異常)
server.bind(new InetSocketAddress(Inet4Address.getLocalHost(), PORT), 50);
System.out.println("服務(wù)器準(zhǔn)備就緒~");
System.out.println("服務(wù)器信息:" + server.getInetAddress() + " P:" + server.getLocalPort());
// 等待客戶端連接
for (; ; ) {
// 得到客戶端
Socket client = server.accept();
// 客戶端構(gòu)建異步線程
ClientHandler clientHandler = new ClientHandler(client);
// 啟動(dòng)線程
clientHandler.start();
}
}
private static ServerSocket createServerSocket() throws IOException {
// 創(chuàng)建基礎(chǔ)的ServerSocket
ServerSocket serverSocket = new ServerSocket();
// 綁定到本地端口20000上,并且設(shè)置當(dāng)前可允許等待鏈接的隊(duì)列為50個(gè)稠项,相當(dāng)于服務(wù)已經(jīng)啟動(dòng)
//serverSocket = new ServerSocket(PORT);
// 等效于上面的方案涯雅,隊(duì)列設(shè)置為50個(gè)
//serverSocket = new ServerSocket(PORT, 50);
// 與上面等同
// serverSocket = new ServerSocket(PORT, 50, Inet4Address.getLocalHost());
return serverSocket;
}
private static void initServerSocket(ServerSocket serverSocket) throws IOException {
// 是否復(fù)用未完全關(guān)閉的地址端口
serverSocket.setReuseAddress(true);
// 等效Socket#setReceiveBufferSize
serverSocket.setReceiveBufferSize(64 * 1024 * 1024);
// 設(shè)置serverSocket#accept超時(shí)時(shí)間
// serverSocket.setSoTimeout(2000);
// 設(shè)置性能參數(shù):短鏈接,延遲展运,帶寬的相對(duì)重要性
serverSocket.setPerformancePreferences(1, 1, 1);
}
/**
* 客戶端消息處理
*/
private static class ClientHandler extends Thread {
private Socket socket;
ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
super.run();
System.out.println("新客戶端連接:" + socket.getInetAddress() +
" P:" + socket.getPort());
try {
// 得到套接字流
OutputStream outputStream = socket.getOutputStream();
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[256];
int readCount = inputStream.read(buffer);
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, 0, readCount);
// byte
byte be = byteBuffer.get();
// char
char c = byteBuffer.getChar();
// int
int i = byteBuffer.getInt();
// bool
boolean b = byteBuffer.get() == 1;
// Long
long l = byteBuffer.getLong();
// float
float f = byteBuffer.getFloat();
// double
double d = byteBuffer.getDouble();
// String
int pos = byteBuffer.position();
String str = new String(buffer, pos, readCount - pos - 1);
System.out.println("收到數(shù)量:" + readCount + " 數(shù)據(jù):"
+ be + "\n"
+ c + "\n"
+ i + "\n"
+ b + "\n"
+ l + "\n"
+ f + "\n"
+ d + "\n"
+ str + "\n");
outputStream.write(buffer, 0, readCount);
outputStream.close();
inputStream.close();
} catch (Exception e) {
System.out.println("連接異常斷開");
} finally {
// 連接關(guān)閉
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("客戶端已退出:" + socket.getInetAddress() +
" P:" + socket.getPort());
}
}
}