Java 网络编程:TCP 与 UDP 的「通信江湖」(基于TCP回显服务器)
上篇博客写了关于UDP的基础特性和基于UDP回显服务器,书接上回,今天,我们就来好好聊聊 TCP,从基础特性到工作机制最后构建回显服务器,揭开它 "靠谱" 的秘密。
UDP与TCP的最主要的核心区别就是UDP为无连接,不可靠传输,面向数据报,UDP为有连接,可靠传输,面向字节流。二者都为全双工,此篇不再重点介绍。
一、TCP 协议:面向连接的可靠传输
UDP的核心特征为:有连接,可靠传输,面向字节流和全双工
1.核心技术特征
- 有连接: 对于 TCP 来说,TCP 协议中,就已经保存了对端的信息
- 可靠传输: 确保数据无丢失、无重复、按序交付,尽可能的提高传输成功的概率
- 面向字节流:数据以连续字节为单位传输
- 全双工: 一个通信链路,支持双向通信,能读也能写
2. Java 中 TCP 的实现
API 介绍
ServerSocket :
ServerSocket 是创建TCP服务端Socket的API。
构造⽅法:
| ⽅法签名 | ⽅法说明 |
|---|---|
| ServerSocket(int port) | 创建⼀个服务端流套接字Socket,并绑定到指定端⼝ |
⽅法:
| ⽅法签名 | ⽅法说明 |
|---|---|
| Socket accept() | 开始监听指定端⼝(创建时绑定的端⼝),有客⼾端连接后,返回⼀个服务端Socket对象,并基于该Socket建⽴与客⼾端的连接,否则阻塞等待 |
| void close() | 关闭此套接字 |
TCP是"有连接",这里的accept是联通连接的关键
Socket :
Socket 是客⼾端Socket,或服务端中接收到客⼾端建⽴连接(accept⽅法)的请求后,返回的服
务端Socket。
不管是客⼾端还是服务端Socket,都是双⽅建⽴连接以后,保存的对端信息,及⽤来与对⽅收发数据
的。
构造⽅法:
| ⽅法签名 | ⽅法说明 |
|---|---|
| Socket(String host, int port) | 创建⼀个客⼾端流套接字Socket,并与对应IP的主机上,对应端⼝的进程建⽴连接 |
这两个参数是服务器IP和端口号
⽅法:
| ⽅法签名 | ⽅法说明 |
|---|---|
| InetAddress getInetAddress() | 返回套接字所连接的地址 |
| InputStream getInputStream() | 返回此套接字的输⼊流(字节流对象) |
| OutputStream getOutputStream() | 返回此套接字的输出流(字节流对象) |
二、构造TCP Echo Server
服务器主要思路:
- 读取请求并解析
- 根据请求计算响应
- 返回响应到客户端
- 打印日志
代码⽰例:
java
public class TcpEchoServer {
private ServerSocket serverSocket=null;
//这里和udp服务器类似,也是在构造对象的时候,绑定端口号
public TcpEchoServer(int port) throws IOException {
serverSocket=new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("启动服务器");
try {
while (true) {
//tcp 来说,需要先处理客户端发来的连接
//通过读写clientsocket,和客户端进行连接
//如果没有客户端发起连接,此时accept就会阻塞
Socket clientSocket = serverSocket.accept();
processConnection(clientSocket);
}
}finally{
serverSocket.close();
}
}
}
//处理一个客户端的连接
//可能要涉及到多个客户端的请求和响应
private void processConnection(Socket clientSocket) {
//打印日志
System.out.printf("[%s:%d]客户端上线!\n",clientSocket.getInetAddress(),clientSocket.getPort());
try(InputStream inputStream=clientSocket.getInputStream();
OutputStream outputStream=clientSocket.getOutputStream()){
//针对 InputStream 套了一层
Scanner scanner=new Scanner(inputStream);
//针对 OutputStram 套了一层
PrintWriter writer=new PrintWriter(outputStream);
//分成三个步骤
while (true){
//1.读取请求并解析,可以直接read,也可以借助Scanner 来辅助
if (!scanner.hasNext()){
System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress(),clientSocket.getPort());
//连接断开了
break;
}
String request=scanner.next();
//2.根据请求计算响应
String response=process(request);
//3.返回响应到客户端
//outputStream.write(response.getBytes());
writer.println(response);
//打印日志
System.out.printf("[%s:%d] rep: %s, resp: %s\n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TcpEchoServer server=new TcpEchoServer(9090);
server.start();
}
}
重点细节解析:
客户端主要思路:
- 从控制台读取用户输入
- 发送给服务器
- 读取服务器返回的响应
- 打印到控制台
代码⽰例:
java
public class TcpEchoClient {
private Socket socket=null;
public TcpEchoClient(String sergerIp,int serverPort) throws IOException {
//直接把字符串的IP,设置进来
//127.0.0.0这种字符串
socket=new Socket(sergerIp,serverPort);
}
public void start(){
Scanner scanner=new Scanner(System.in);
try(InputStream inputStream=socket.getInputStream();
OutputStream outputStream=socket.getOutputStream()){
//为了使用方便,套壳使用
Scanner scannerNet=new Scanner(inputStream);
PrintWriter writer=new PrintWriter(outputStream);
//从控制读取请求,发送给服务器
while (true){
//1.从控制台读取用户输入
String requset=scanner.next();
//2.发送给服务器
writer.println(requset);
//3.读取服务器返回的响应
String response=scannerNet.next();
//打印到控制台
System.out.println(response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient client=new TcpEchoClient("127.0.0.1",9090);
client.start();
}
}
重点细节解析:
三、小结:Java 网络编程的 "入门钥匙"
TCP 和 UDP 是 Java 网络编程的基础,记住这两点:
- 要 "靠谱" 选 TCP:需要确认、有序的场景(比如用户登录时传密码,不能丢);
- 要 "极速" 选 UDP:能接受偶尔丢包、追求速度的场景(比如游戏里的角色移动,丢一两个帧不影响)。
其实你再深入想一层:平时用的 HTTP 协议(上网刷网页)是基于 TCP 的,而直播用的 RTMP 协议很多是基于 UDP 的 ------ 原来这两个 "通信高手" 早就藏在我们每天的操作里啦!
如果想进一步玩起来,可以试试修改上面的代码:比如让 TCP 客户端和服务端互相聊天(加个循环读取输入),或者让 UDP 接收方给发送方回消息(类似 "已读" 确认),动手的过程中理解会更透彻~






