• 【网络编程】TCP Socket编程


    流套接字: 使用传输层TCP协议
    TCP: 即Transmission Control Protocol(传输控制协议),传输层协议
    TCP的特点:

    1. 有连接
    2. 可靠传输
    3. 面向字节流
    4. 有接收缓冲区,也有发送缓冲区
    5. 大小不限

    1. ServerSocket

    ServerSocket 是创建TCP服务端Socket的API。

    注意: ServerSocket 只能用于 服务器端。

    构造方法:

    方法签名方法说明
    ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口

    方法:

    方法签名方法说明
    Socket accept()开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待
    void close()关闭此套接字

    2. Socket

    Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket。

    构造方法:

    方法签名方法说明
    Socket(String host, int port)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接

    注意:这里面的 host 和 port 是要连接的服务器的 IP 地址和端口号。

    方法:

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

    3. TCP的长短连接

    TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:

    短连接: 每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收数据。
    长连接: 不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以多次收发数据。

    两者区别如下:

    1. 建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时的,长连接效率更高。
    2. 主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求,也可以是服务端主动发。
    3. 两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于客户端与服务端通信频繁的场景,如聊天室,实时游戏等。

    4. Socket 通信模型

    在这里插入图片描述

    5. 代码示例:TCP 回显服务器

    服务器代码:

    class TcpEchoServer {
        public ServerSocket serverSocket;//专门用来接受请求并建立链接
        public Socket clientSocket;//专门用来处理请求
        public TcpEchoServer(int port) throws IOException {
            this.serverSocket=new ServerSocket(port);
        }
    
        public void start() throws IOException {
            System.out.println("服务器启动!");
            //也可以利用线程池
            ExecutorService threadsPool= Executors.newCachedThreadPool();
            while(true){
                //接受请求
                clientSocket=serverSocket.accept();
    //            //利用多线程才能让服务器同时处理多个客户端的请求
    //            Thread t=new Thread(()->{
    //                //建立链接并处理请求
    //                try {
    //                    createConnection(clientSocket);
    //                } catch (IOException e) {
    //                    throw new RuntimeException(e);
    //                }
    //            });
    //            t.start();
                //创建线程池相对于每次创建一个线程来说效率更高一些
                threadsPool.submit(()->{
                    try {
                        createConnection(clientSocket);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                });
            }
        }
    
        public void createConnection(Socket clientSocket) throws IOException {
            System.out.printf("[%s:%d]建立链接成功\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
            //三个步骤
            //1.读取客户端请求(根据打开的文件流确定了读取的是客户端发来的请求)
            //这里针对TCP的读写和对于文件的读写是一摸一样的
            //利用socket构造文件流
            try(InputStream inputStream=clientSocket.getInputStream()){//注意打开的流
                //直接利用scanner读取(利用原生的InputStream也是可以的,但Scanner更方便)
                Scanner scanner=new Scanner(inputStream);
                try(OutputStream outputStream=clientSocket.getOutputStream()){//注意打开的流
                    while(true){
                        
                        if(!scanner.hasNext()){
                            System.out.printf("[%s:%d]断开链接\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                            break;
                        }
                        //读取请求(TCP以字符流进行传输)
                        // 读到空白符/ 空格/换行才会停止
                        String request=scanner.next();
                        //2.根据请求计算响应
                        String response=process(request);
                        //3.返回响应(根据打开的文件流决定了是往客户端返回请求)
                        //为了方便用PrintWriter对OutputStream进行包裹
                        PrintWriter printWriter=new PrintWriter(outputStream);
                        // 因为使用 next,读到空白符/ 空格/换行才会停止,所以须使用 println 
                        printWriter.println(response);
                        printWriter.flush();
                        System.out.printf("[%d][req:%s resp:%s]\n",clientSocket.getPort(),request,response);
                    }
                }
            }finally {
                clientSocket.close();//记得及时关闭
            }
        }
    
        public String process(String request){
            return request;
        }
    
        public static void main(String[] args) throws IOException {
            TcpEchoServer tcpEchoServer=new TcpEchoServer(9090);
            tcpEchoServer.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    客户端代码:

    class TcpEchoClient {
        public Socket client;
        //TCP中客户端构造函数的ip和port指的是要链接的服务器的IP和port
        public TcpEchoClient(String serverIp, int serverPort) throws IOException {
            this.client = new Socket(serverIp, serverPort);
        }
    
        public void start() throws IOException {
            System.out.println("和服务器建立链接成功");
            Scanner scanner = new Scanner(System.in);
            //这里针对TCP的读写和对于文件的读写是一摸一样的
            //利用socket构造文件流
            try (InputStream inputStream = client.getInputStream()) {
                try (OutputStream outputStream = client.getOutputStream()) {
                    //接收从控制台输入的字符串
                    while (true) {
                        System.out.println("->");
                        String request = scanner.next();
                        //构造请求并发送请求(PrintWriter和Scanner对应)//注意文件流
                        PrintWriter printWriter = new PrintWriter(outputStream);
                        // 因为使用 next,读到空白符/ 空格/换行才会停止,所以须使用 println 
                        printWriter.println(request);
                        printWriter.flush();//如果不及时刷新,服务器可能不能及时接收到数据
                        //接收响应
                        Scanner respScanner = new Scanner(inputStream);
                        String response = respScanner.next();
                        //显示到控制台上
                        System.out.printf("[req:%s  resp:%s]\n", request, response);
                    }
                }
            }
        }
    
        public static void main(String[] args) throws IOException {
            TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1", 9090);
            tcpEchoClient.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    注意:当然要先启动服务器再启动客户端!

    好啦! 以上就是对 TCP Socket编程的讲解,希望能帮到你 !
    评论区欢迎指正 !

  • 相关阅读:
    【人工智能入门之机器学习决策树的学习(一)】
    Java方法的使用
    Halo 开源项目学习(二):实体类与数据表
    我的 “词义”库
    数据结构——排序
    KES数据库实践指南:探索KES数据库的事务隔离级别
    物理机服务器应该注意的事
    自动化测试基础篇:Selenium 框架设计(POM)
    【学习】软件测试中对bug定位的方法,如何区分是前端还是后端bug
    【宝塔面板建站】本地连接云服务器的数据库 以阿里云服务器为例子(保姆级图文)
  • 原文地址:https://blog.csdn.net/m0_61832361/article/details/132920215