前言
Socket TCP快速入門,demo練習
TCP
TCP機制
TCP鏈接,傳輸流程
TCP能做什麼
TCP核心API
- 客戶端創建流程
- 服務器端流程
擴展-Socket與進程之間的關係
- 基礎類型數據傳輸
案例:
package com.zcw.demo4;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.nio.ByteBuffer;
/**
* @ClassName : Client
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-07 18:03
*/
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端口,超時時間3秒,超過則拋出超時異常
socket.connect(new InetSocketAddress(Inet4Address.getLocalHost(), PORT), 3000);
System.out.println("已發起服務器連接,並進入後續流程~");
System.out.println("客戶端信息:" + socket.getLocalAddress() + " P:" + socket.getLocalPort());
System.out.println("服務器信息:" + socket.getInetAddress() + " P:" + socket.getPort());
try {
// 發送接收數據
todo(socket);
} catch (Exception e) {
System.out.println("異常關閉");
}
// 釋放資源
socket.close();
System.out.println("客戶端已退出~");
}
private static Socket createSocket() throws IOException {
/*
// 無代理模式,等效於空構造函數
Socket socket = new Socket(Proxy.NO_PROXY);
// 新建一份具有HTTP代理的套接字,傳輸數據將通過www.baidu.com:8080端口轉發
Proxy proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress(Inet4Address.getByName("www.baidu.com"), 8800));
socket = new Socket(proxy);
// 新建一個套接字,並且直接鏈接到本地20000的服務器上
socket = new Socket("localhost", PORT);
// 新建一個套接字,並且直接鏈接到本地20000的服務器上
socket = new Socket(Inet4Address.getLocalHost(), PORT);
// 新建一個套接字,並且直接鏈接到本地20000的服務器上,並且綁定到本地20001端口上
socket = new Socket("localhost", PORT, Inet4Address.getLocalHost(), LOCAL_PORT);
socket = new Socket(Inet4Address.getLocalHost(), PORT, Inet4Address.getLocalHost(), LOCAL_PORT);
*/
Socket socket = new Socket();
// 綁定到本地20001端口
socket.bind(new InetSocketAddress(Inet4Address.getLocalHost(), LOCAL_PORT));
return socket;
}
private static void initSocket(Socket socket) throws SocketException {
// 設置讀取超時時間爲2秒
socket.setSoTimeout(2000);
// 是否複用未完全關閉的Socket地址,對於指定bind操作後的套接字有效
socket.setReuseAddress(true);
// 是否開啓Nagle算法
socket.setTcpNoDelay(true);
// 是否需要在長時無數據響應時發送確認數據(類似心跳包),時間大約爲2小時
socket.setKeepAlive(true);
// 對於close關閉操作行爲進行怎樣的處理;默認爲false,0
// false、0:默認情況,關閉時立即返回,底層系統接管輸出流,將緩衝區內的數據發送完成
// true、0:關閉時立即返回,緩衝區數據拋棄,直接發送RST結束命令到對方,並無需經過2MSL等待
// true、200:關閉時最長阻塞200毫秒,隨後按第二情況處理
socket.setSoLinger(true, 20);
// 是否讓緊急數據內斂,默認false;緊急數據通過 socket.sendUrgentData(1);發送
socket.setOOBInline(true);
// 設置接收發送緩衝器大小
socket.setReceiveBufferSize(64 * 1024 * 1024);
socket.setSendBufferSize(64 * 1024 * 1024);
// 設置性能參數:短鏈接,延遲,帶寬的相對重要性
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());
// 發送到服務器
outputStream.write(buffer, 0, byteBuffer.position() + 1);
// 接收服務器返回
int read = inputStream.read(buffer);
System.out.println("收到數量:" + read);
// 資源釋放
outputStream.close();
inputStream.close();
}
}
package com.zcw.demo4;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
/**
* @ClassName : Server
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-07 18:06
*/
public class Server {
private static final int PORT = 20000;
public static void main(String[] args) throws IOException {
ServerSocket server = createServerSocket();
initServerSocket(server);
// 綁定到本地端口上
server.bind(new InetSocketAddress(Inet4Address.getLocalHost(), PORT), 50);
System.out.println("服務器準備就緒~");
System.out.println("服務器信息:" + server.getInetAddress() + " P:" + server.getLocalPort());
// 等待客戶端連接
for (; ; ) {
// 得到客戶端
Socket client = server.accept();
// 客戶端構建異步線程
ClientHandler clientHandler = new ClientHandler(client);
// 啓動線程
clientHandler.start();
}
}
private static ServerSocket createServerSocket() throws IOException {
// 創建基礎的ServerSocket
ServerSocket serverSocket = new ServerSocket();
// 綁定到本地端口20000上,並且設置當前可允許等待鏈接的隊列爲50個
//serverSocket = new ServerSocket(PORT);
// 等效於上面的方案,隊列設置爲50個
//serverSocket = new ServerSocket(PORT, 50);
// 與上面等同
// serverSocket = new ServerSocket(PORT, 50, Inet4Address.getLocalHost());
return serverSocket;
}
private static void initServerSocket(ServerSocket serverSocket) throws IOException {
// 是否複用未完全關閉的地址端口
serverSocket.setReuseAddress(true);
// 等效Socket#setReceiveBufferSize
serverSocket.setReceiveBufferSize(64 * 1024 * 1024);
// 設置serverSocket#accept超時時間
// serverSocket.setSoTimeout(2000);
// 設置性能參數:短鏈接,延遲,帶寬的相對重要性
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("收到數量:" + readCount + " 數據:"
+ 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 {
// 連接關閉
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("客戶端已退出:" + socket.getInetAddress() +
" P:" + socket.getPort());
}
}
}
- 測試:
package com.zcw.demo4;
/**
* @ClassName : Tools
* @Description :
* @Author : Zhaocunwei
* @Date: 2020-07-07 18:07
*/
public class Tools {
public static int byteArrayToInt(byte[] b) {
return b[3] & 0xFF |
(b[2] & 0xFF) << 8 |
(b[1] & 0xFF) << 16 |
(b[0] & 0xFF) << 24;
}
public static byte[] intToByteArray(int a) {
return new byte[]{
(byte) ((a >> 24) & 0xFF),
(byte) ((a >> 16) & 0xFF),
(byte) ((a >> 8) & 0xFF),
(byte) (a & 0xFF)
};
}
}