• TCP流套接字编程


    目录

    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服务端代码

    1. package network;
    2. import java.io.IOException;
    3. import java.io.InputStream;
    4. import java.io.OutputStream;
    5. import java.io.PrintWriter;
    6. import java.net.ServerSocket;
    7. import java.net.Socket;
    8. import java.util.Scanner;
    9. import java.util.concurrent.ExecutorService;
    10. import java.util.concurrent.Executors;
    11. public class TcpEchoServer {
    12. private ServerSocket serverSocket = null;
    13. public TcpEchoServer(int port) throws IOException {
    14. serverSocket = new ServerSocket(port);
    15. }
    16. public void start() throws IOException {
    17. System.out.println("服务器启动!");
    18. ExecutorService service = Executors.newCachedThreadPool();
    19. while (true) {
    20. // 通过 accept 方法, 把内核中已经建立好的连接拿到应用程序中.
    21. // 建立连接的细节流程都是内核自动完成的. 应用程序只需要 "捡现成" 的.
    22. Socket clientSocket = serverSocket.accept();
    23. // processConnection(clientSocket);
    24. // 此处不应该直接调用 processConnection, 会导致服务器不能处理多个客户端.
    25. // 创建新的线程来调用更合理的做法.
    26. // 这种做法可行, 不够好
    27. // Thread t = new Thread(() -> {
    28. // processConnection(clientSocket);
    29. // });
    30. // t.start();
    31. // 更好一点的办法, 是使用线程池.
    32. service.submit(new Runnable() {
    33. @Override
    34. public void run() {
    35. processConnection(clientSocket);
    36. }
    37. });
    38. }
    39. }
    40. // 通过这个方法, 来处理当前的连接.
    41. public void processConnection(Socket clientSocket) {
    42. // 进入方法, 先打印一个日志, 表示当前有客户端连上了.
    43. System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress(), clientSocket.getPort());
    44. // 接下来进行数据的交互.
    45. try (InputStream inputStream = clientSocket.getInputStream();
    46. OutputStream outputStream = clientSocket.getOutputStream()) {
    47. // 使用 try ( ) 方式, 避免后续用完了流对象, 忘记关闭.
    48. // 由于客户端发来的数据, 可能是 "多条数据", 针对多条数据, 就循环的处理.
    49. while (true) {
    50. Scanner scanner = new Scanner(inputStream);
    51. if (!scanner.hasNext()) {
    52. // 连接断开了. 此时循环就应该结束
    53. System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress(), clientSocket.getPort());
    54. break;
    55. }
    56. // 1. 读取请求并解析. 此处就以 next 来作为读取请求的方式. next 的规则是, 读到 "空白符" 就返回.
    57. String request = scanner.next();
    58. // 2. 根据请求, 计算响应.
    59. String response = process(request);
    60. // 3. 把响应写回到客户端.
    61. // 可以把 String 转成字节数组, 写入到 OutputStream
    62. // 也可以使用 PrintWriter 把 OutputStream 包裹一下, 来写入字符串.
    63. PrintWriter printWriter = new PrintWriter(outputStream);
    64. // 此处的 println 不是打印到控制台了, 而是写入到 outputStream 对应的流对象中, 也就是写入到 clientSocket 里面.
    65. // 自然这个数据也就通过网络发送出去了. (发给当前这个连接的另外一端)
    66. // 此处使用 println 带有 \n 也是为了后续 客户端这边 可以使用 scanner.next 来读取数据.
    67. printWriter.println(response);
    68. // 此处还要记得有个操作, 刷新缓冲区. 如果没有刷新操作, 可能数据仍然是在内存中, 没有被写入网卡.
    69. printWriter.flush();
    70. // 4. 打印一下这次请求交互过程的内容
    71. System.out.printf("[%s:%d] req=%s, resp=%s\n", clientSocket.getInetAddress(), clientSocket.getPort(),
    72. request, response);
    73. }
    74. } catch (IOException e) {
    75. e.printStackTrace();
    76. } finally {
    77. try {
    78. // 在这个地方, 进行 clientSocket 的关闭.
    79. // processConnection 就是在处理一个连接. 这个方法执行完毕, 这个连接也就处理完了.
    80. clientSocket.close();
    81. } catch (IOException e) {
    82. e.printStackTrace();
    83. }
    84. }
    85. }
    86. public String process(String request) {
    87. // 此处也是写的回显服务器. 响应和请求是一样的.
    88. return request;
    89. }
    90. public static void main(String[] args) throws IOException {
    91. TcpEchoServer server = new TcpEchoServer(9090);
    92. server.start();
    93. }
    94. }

     4.2客户端代码

    1. package network;
    2. import java.io.IOException;
    3. import java.io.InputStream;
    4. import java.io.OutputStream;
    5. import java.io.PrintWriter;
    6. import java.net.Socket;
    7. import java.util.Scanner;
    8. public class TcpEchoClient {
    9. private Socket socket = null;
    10. public TcpEchoClient(String serverIp, int serverPort) throws IOException {
    11. // 需要在创建 Socket 的同时, 和服务器 "建立连接", 此时就得告诉 Socket 服务器在哪里~~
    12. // 具体建立连接的细节, 不需要咱们代码手动干预. 是内核自动负责的.
    13. // 当我们 new 这个对象的时候, 操作系统内核, 就开始进行 三次握手 具体细节, 完成建立连接的过程了.
    14. socket = new Socket(serverIp, serverPort);
    15. }
    16. public void start() {
    17. // tcp 的客户端行为和 udp 的客户端差不多.
    18. // 都是:
    19. // 3. 从服务器读取响应.
    20. // 4. 把响应显示到界面上.
    21. Scanner scanner = new Scanner(System.in);
    22. try (InputStream inputStream = socket.getInputStream();
    23. OutputStream outputStream = socket.getOutputStream()) {
    24. PrintWriter writer = new PrintWriter(outputStream);
    25. Scanner scannerNetwork = new Scanner(inputStream);
    26. while (true) {
    27. // 1. 从控制台读取用户输入的内容
    28. System.out.print("-> ");
    29. String request = scanner.next();
    30. // 2. 把字符串作为请求, 发送给服务器
    31. // 这里使用 println, 是为了让请求后面带上换行.
    32. // 也就是和服务器读取请求, scanner.next 呼应
    33. writer.println(request);
    34. writer.flush();
    35. // 3. 读取服务器返回的响应.
    36. String response = scannerNetwork.next();
    37. // 4. 在界面上显示内容了.
    38. System.out.println(response);
    39. }
    40. } catch (IOException e) {
    41. e.printStackTrace();
    42. }
    43. }
    44. public static void main(String[] args) throws IOException {
    45. TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);
    46. client.start();
    47. }
    48. }

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

     

  • 相关阅读:
    亚马逊关键词搜索API接口(item_search-按关键字搜索亚马逊商品接口),亚马逊API接口
    (三十二)大数据实战——Maxwell安装部署及其应用案例实战
    UE5制作场景时的小技巧和注意事项
    实验:贪心算法
    1300*B. Road Construction(构造&菊花图)
    ASEMI整流桥S25VB100,S25VB100参数,S25VB100应用
    常用的Java日志框架:Log4j、SLF4J和Logback
    【建议收藏】15755 字,讲透 MySQL 性能优化(包含 MySQL 架构、存储引擎、调优工具、SQL、索引、建议等等)
    git commit --amend 修改最近一次提交的 commit message
    数据结构与算法——线性表的链式表示与实现
  • 原文地址:https://blog.csdn.net/qq_61576108/article/details/136707538