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 接收方给发送方回消息(类似 "已读" 确认),动手的过程中理解会更透彻~






