Java Socket通信之TCP协议

文章目录

    • 一、 Java流套接字通信模型
      • 1.TCP模型
      • 2.TCP Socket常见API
        • ServerSocket API
        • Socket API
    • 二、TCP流套接字编程
      • 1.回显服务器
      • 2.多线程服务器
    • 三、TCP中的长短连接
    • 四、协议
      • 1. 为什么需要协议?
      • 2. 封装/分用 VS 序列化/反序列化
      • 3. 自定义协议

紧接着 Java Socket通信之UDP协议 再来!

一、 Java流套接字通信模型

1.TCP模型

TCP的整个通信流程如下如所示:
服务器的任务:
①创建ServerSocket对象并绑定一个端口,成为listenSocket(类似于大街拉拢客人的中介)。
②listenSocket调用ServerSocket的accept()方法,把内核建立好的连接拿到代码中进行处理。如果接收到来自客户端的请求时,accept()会返回一个Socket实例,称为clientSocket(类似于中介把客人介绍给前台小姐姐,由她负责之后的业务流程,中介继续去拉拢客人),如果没有接收到请求,则一直处于阻塞等待状态。
③使用clientSocket的getInputStream和OutputStream得到字节流对象,可进行字节的读取和写入。
④当客户端断开连接后,服务器要及时关闭clientSocket,否则会出现文件资源泄露的情况。
客户端的任务:
①创建一个Socket对象,同时指定服务器的IP和Port (建立TCP连接三次握手,由内核完成,用户感知不到)。
②客户端通过Socket对象getInputStream和getOutputStream和服务器进行通信。
Java Socket通信之TCP协议_第1张图片
关于端口被占用问题:
通常情况下,两个进程无法绑定到同一个端口号!(除特殊情况:eg. Linux中 fork系统调用)
解决办法:如果占用端口的进程A不需要运行,就可以关闭A后,再启动需要绑定该端口的进程B;如果需要运行A进程,则可以修改进程B的绑定端口,换为其他没有使用的端口。

2.TCP Socket常见API

ServerSocket API

ServerSocket 是创建TCP服务端Socket的API;
ServerSocket构造方法:

方法 说明
ServerSocket(int port) 创建一个服务端流套接字socket,并绑定到响应端口上

ServerSocket方法:

方法 说明
Socket accept() 开始监听指定端口(创建时绑定的端口),如果有客户端连接,返回一个socket对象,如果没有,一直处于阻塞等待状态。(用户代码调用accept,才是真的把连接拿到用户代码中)
void close() 关闭此套接字(否则随着连接的增多,socket文件会出现文件资源泄露的情况)
Socket API

Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端socket。不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对方端信息,即用来与对方收发数据的。
注:Socket是服务器和客户端都需要使用;ServerSocket是只在服务器中使用的
Socket 构造方法:

方法 说明
Socket(String host, int post) 创建一个客户端流套接字socket,并与对应IP和port的进程建立连接

Socket 方法:TCP Socket 是基于字节流的,进行具体读写时和文件类似。

方法 说明
InetAddress getInetAddress() 返回套接字所连接的地址
InputStream getInputStream() 返回此套接字的输入流
OutputStream getOutputStream() 返回此套接字的输出流

二、TCP流套接字编程

1.回显服务器

服务器代码:

/**
 * 代码有几个值得注意的点:
 * ① 将构造好的响应写会给客户端时,要用println
 * ② 返回响应时要用flush()将缓冲区中的数据冲刷到内存中
 * ③ 当前简易的程序是一个服务器只能同时为一个客户端收发数据,如果再有一个客户端要通讯,需等待上一个客户端释放完成之后再通讯
 * 关键问题在于:如果第一个客户端没有退出,此时服务器的逻辑一直在processConnection内部打转,没有机会再次调用到accept,
 *             也没有办法处理第二个连接~
 * 解决办法:使用多线程!
 * 主线程里面循环调用accept()
 */
package TCPEcho;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TcpEchoServer {
   
    private ServerSocket listenSocket = null;

    //构造函数--端口绑定
    public TcpEchoServer(int port) throws IOException {
   
        listenSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
   
        //accept()相当于一个监听程序,TCP是一个有连接的操作,首先要建立连接
        //如果客户端没有发来请求,accept()阻塞等待
        //如果服务器接收到客户端发来的请求,accept()会返回一个Socket对象
        //客户端和服务器的进一步交互就交给clientSocket来完成了
        while (true) {
   
            Socket clientSocket = listenSocket.accept();
            processConnection(clientSocket);
        }
    }

    //处理一个请求,这个请求中可能涉及客户端和服务器的多次交互
    private void processConnection(Socket clientSocket) throws IOException {
   
        String log = String.format("[%s:%d]客户端已上线!",
                clientSocket.getInetAddress().toString(),clientSocket.getPort());
        System.out.println(log);

        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
   
            while(true) {
   
                //1.接收请求并解析
                //①可以使用inputStream的read()方法读取数据到byte[]中,然后在转成String
                //②第一种方法比较麻烦,还可以借助Scanner来完成这个工作
                Scanner scanner = new Scanner(inputStream);

                //如果连接关闭,才会触发到这个情况!(读取到EOF)
                if (!scanner.hasNext()) {
   
                    log = String.format("[%s:%d]客户端已下线!",
                            clientSocket.getInetAddress().toString(), clientSocket.getPort());
                    System.out.println(log);
                    break;
                }
                String request = scanner.next();

                //2.根据请求计算响应
                String response = process(request);

                //3.构造响应并返回
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                printWriter.flush();  //刷新

                log = String.format("[%s:%d],reque

你可能感兴趣的:(网络编程,网络,网络协议,tcp/ip)