1、TCP
- 传输层的控制协议
- 面向连接的、可靠的、基于字节流的传输层通信协议
- 与UDP一样完成第四层传输层所指定的功能与职责
2、应用
- 聊天消息传输、推送
- 单人语音、视频聊天
- UDP能做的,TCP几乎都能做。需要考虑复杂性、性能问题
- 限制:无法广播、多播操作;无法搜索客户端
3、可靠性
- 连接可靠性:三次握手,四次挥手
- 传输可靠性:排序、丢弃(超时)、重发
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端口(指连接服务器端口),超时时间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();
// 绑定到本地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(false);
// 设置接收发送缓冲器大小
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();
}
}
5、Server
public class Server {
private static final int PORT = 20000;
public static void main(String[] args) throws IOException {
ServerSocket server = createServerSocket();
initServerSocket(server);
// 绑定到本地端口上(绑定调用相当于服务已经启动,最大等待连接客户端数为50,超过则抛出异常)
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());
}
}
}
网友评论