TCP流套接字编程

本文详细介绍了Java中的TCP流套接字以及ServerSocket和SocketAPI的使用,包括ServerSocket的构造方法和常用方法,以及如何在服务端和客户端实现数据交互,以回显服务器为例展示了实际应用过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1.TCP流套接字:

2.ServerSocket API

(1) ServerSocket 构造方法:

(2)ServerSocket普通方法

3.Socket API

(1)Socket 构造方法:

(2)Socket普通方法

 4.1服务端代码

 4.2客户端代码

4.3运行结果 (回显服务器)


1.TCP流套接字

使用传输层 TCP 协议,TCP,即 Transmission Control Protocol (传输控制协议),传输层协议。
TCP 的特点(细节后续再学习):

  • 有连接
  • 可靠传输
  • 面向字节流
  • 有接收缓冲区,也有发送缓冲区
  • 大小不限
        对于字节流来说,可以简单的理解为,传输数据是基于IO 流,流式数据的特征就是在 IO 流没有关闭的情 况下,是无边界的数据,可以多次发送,也可以分开多次接收。

2.ServerSocket API

ServerSocket 是创建 TCP 服务端 Socket API, 
 

(1) ServerSocket 构造方法:

(2)ServerSocket普通方法

3.Socket API

        Socket 是客户端 Socket ,或服务端中接收到客户端建立连接( accept 方法)的请求后,返回的服务端 Socket。
        不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。

(1)Socket 构造方法:

(2)Socket普通方法

 4.1服务端代码

package network;

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;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoServer {
    private ServerSocket serverSocket = null;

    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");
        ExecutorService service = Executors.newCachedThreadPool();
        while (true) {
            // 通过 accept 方法, 把内核中已经建立好的连接拿到应用程序中.
            // 建立连接的细节流程都是内核自动完成的. 应用程序只需要 "捡现成" 的.
            Socket clientSocket = serverSocket.accept();
            // processConnection(clientSocket);
            // 此处不应该直接调用 processConnection, 会导致服务器不能处理多个客户端.
            // 创建新的线程来调用更合理的做法.
            // 这种做法可行, 不够好
//            Thread t = new Thread(() -> {
//                processConnection(clientSocket);
//            });
//            t.start();

            // 更好一点的办法, 是使用线程池.
            service.submit(new Runnable() {
                @Override
                public void run() {
                    processConnection(clientSocket);
                }
            });
        }
    }

    // 通过这个方法, 来处理当前的连接.
    public void processConnection(Socket clientSocket) {
        // 进入方法, 先打印一个日志, 表示当前有客户端连上了.
        System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress(), clientSocket.getPort());
        // 接下来进行数据的交互.
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) {
            // 使用 try ( ) 方式, 避免后续用完了流对象, 忘记关闭.
            // 由于客户端发来的数据, 可能是 "多条数据", 针对多条数据, 就循环的处理.
            while (true) {
                Scanner scanner = new Scanner(inputStream);
                if (!scanner.hasNext()) {
                    // 连接断开了. 此时循环就应该结束
                    System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress(), clientSocket.getPort());
                    break;
                }
                // 1. 读取请求并解析. 此处就以 next 来作为读取请求的方式. next 的规则是, 读到 "空白符" 就返回.
                String request = scanner.next();
                // 2. 根据请求, 计算响应.
                String response = process(request);
                // 3. 把响应写回到客户端.
                //    可以把 String 转成字节数组, 写入到 OutputStream
                //    也可以使用 PrintWriter 把 OutputStream 包裹一下, 来写入字符串.
                PrintWriter printWriter = new PrintWriter(outputStream);
                //    此处的 println 不是打印到控制台了, 而是写入到 outputStream 对应的流对象中, 也就是写入到 clientSocket 里面.
                //    自然这个数据也就通过网络发送出去了. (发给当前这个连接的另外一端)
                //    此处使用 println 带有 \n 也是为了后续 客户端这边 可以使用 scanner.next 来读取数据.
                printWriter.println(response);
                //    此处还要记得有个操作, 刷新缓冲区. 如果没有刷新操作, 可能数据仍然是在内存中, 没有被写入网卡.
                printWriter.flush();
                // 4. 打印一下这次请求交互过程的内容
                System.out.printf("[%s:%d] req=%s, resp=%s\n", clientSocket.getInetAddress(), clientSocket.getPort(),
                        request, response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 在这个地方, 进行 clientSocket 的关闭.
                // processConnection 就是在处理一个连接. 这个方法执行完毕, 这个连接也就处理完了.
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public String process(String request) {
        // 此处也是写的回显服务器. 响应和请求是一样的.
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(9090);
        server.start();
    }
}

 4.2客户端代码

package network;

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

public class TcpEchoClient {
    private Socket socket = null;

    public TcpEchoClient(String serverIp, int serverPort) throws IOException {
        // 需要在创建 Socket 的同时, 和服务器 "建立连接", 此时就得告诉 Socket 服务器在哪里~~
        // 具体建立连接的细节, 不需要咱们代码手动干预. 是内核自动负责的.
        // 当我们 new 这个对象的时候, 操作系统内核, 就开始进行 三次握手 具体细节, 完成建立连接的过程了.
        socket = new Socket(serverIp, serverPort);
    }

    public void start() {
        // tcp 的客户端行为和 udp 的客户端差不多.
        // 都是:
        // 3. 从服务器读取响应.
        // 4. 把响应显示到界面上.
        Scanner scanner = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()) {
            PrintWriter writer = new PrintWriter(outputStream);
            Scanner scannerNetwork = new Scanner(inputStream);
            while (true) {
                // 1. 从控制台读取用户输入的内容
                System.out.print("-> ");
                String request = scanner.next();
                // 2. 把字符串作为请求, 发送给服务器
                //    这里使用 println, 是为了让请求后面带上换行.
                //    也就是和服务器读取请求, scanner.next 呼应
                writer.println(request);
                writer.flush();
                // 3. 读取服务器返回的响应.
                String response = scannerNetwork.next();
                // 4. 在界面上显示内容了.
                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();
    }
}

4.3运行结果 (回显服务器)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爆浆曲奇饼~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值