IT评测·应用市场-qidao123.com

标题: 5.6.3 网络套接字章 TCP服务器客户端 [打印本页]

作者: 万万哇    时间: 2024-10-27 06:41
标题: 5.6.3 网络套接字章 TCP服务器客户端
1.0 ServerSocket 服务端 API

概念


ServerSocket 构造方法

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

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

概念


构造方法

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

方法署名方法阐明InetAddress getInetAddress()返回套接字所连接的地点InputStream getInputStream()返回此套接字的输入流OutputStream getOutputStream()返回此套接字的输出流 3.0 短连接和长连接

TCP程序涉及到两种写法

4.0 TCP的回显服务器

概念


存在的题目与解决


代码示例

  1. import java.io.IOException;
  2. import java.io.InputStream;
  3. import java.io.OutputStream;
  4. import java.io.PrintWriter;
  5. import java.net.ServerSocket;
  6. import java.net.Socket;
  7. import java.util.Scanner;
  8. import java.util.concurrent.ExecutorService;
  9. import java.util.concurrent.Executors;
  10. public class TcpEchoServer {
  11.     private ServerSocket serverSocket = null;
  12.     // 此处不应该创建固定线程数目的线程池.
  13.     private ExecutorService service = Executors.newCachedThreadPool();//线程池
  14.     //构造方法
  15.     // 这个操作就会绑定端口号
  16.     public TcpEchoServer(int port) throws IOException {
  17.         serverSocket = new ServerSocket(port);
  18.     }
  19.     // 启动服务器
  20.     public void start() throws IOException {
  21.         System.out.println("服务器启动!");//打印日志
  22.         //服务器启动后在while循环时,第一步不是接收客户端请求,而是与客户端连接
  23.         while (true) {
  24.             //把内核中已连接的对象获取到应用程序中了,这个过程类似于生产者消费者模型
  25.             //返回值是一个Socket
  26.             Socket clientSocket = serverSocket.accept();
  27. //***************************************************************************************************************
  28.             //问题2:
  29.             // 单个线程,不能处理多个服务器请求,只能一个结束才能处理下一个
  30.             // 多线程解决此问题,在主线程建立连接,在主线程建立连接完后,创建新线程专门用于处理客户端请求
  31. //            Thread t = new Thread(() -> {
  32. //                processConnection(clientSocket);
  33. //            });
  34. //            t.start();
  35.             //问题1:
  36.             // 这个写法,是能自动关闭close,只要实现 Closeable 接口就可以这么写.
  37.             //这么写会有其他问题.processConnection和主线程是不同线程,执行processConnection的过程中,主线程的try就执行完毕了,这就导致clientSocket没用完就被关闭了
  38. //            try(Socket clientSocket = serverSocket.accept();){
  39. //                Thread t = new Thread(() -> {
  40. //                    processConnection(clientSocket);
  41. //                });
  42. //                t.start();
  43. //
  44. //            };
  45.             // 使用线程池, 来解决上述问题
  46.             service.submit(new Runnable() {
  47.                 @Override
  48.                 public void run() {
  49.                     processConnection(clientSocket);
  50.                 }
  51.             });
  52. //***************************************************************************************************************
  53.         }
  54.     }
  55.     // 通过这个方法来处理一个连接的逻辑.
  56.     private void processConnection(Socket clientSocket) {
  57.         System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
  58.         // 接下来就可以1.读取请求, 2.根据请求计算响应, 3.返回响应三步走了.
  59.         // Socket 对象内部包含了两个字节流对象, 可以把这俩字节流对象获取到, 完成后续的读写工作
  60.         try (InputStream inputStream = clientSocket.getInputStream();
  61.              OutputStream outputStream = clientSocket.getOutputStream()) {
  62.             // 一次连接中, 可能会涉及到多次请求/响应
  63.             while (true) {
  64.                 // 1. 读取请求并解析. 为了读取方便, 直接使用 Scanner.(把字节流在内地转化为字符流)
  65.                 Scanner scanner = new Scanner(inputStream);
  66.                 //当没读到客户端数据的时候hasNext会一直阻塞,或者客户端退出,hashNext感知到客户端退出就break了
  67.                 if (!scanner.hasNext()) { //如果读到eof,就读完了,就退出循环
  68.                     // 读取完毕, 客户端下线.
  69.                     System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
  70.                     break;
  71.                 }
  72.                 // 这个代码暗含一个约定, 客户端发过来的请求得是文本数据, 同时还得带有空白符作为分割. (比如换行这种)
  73.                 //next就是一直读数据,直到读到空白符结束(空白符是一类字符,包括不限于: 换行'\n',回车'\r',空格,制表符,换页符,垂直制表符)
  74.                 //换行('\n')是让光标到下一行,回车('\r')是让光标回到行首,不会另起一行
  75.                 String request = scanner.next();
  76.                 // 2. 根据请求计算响应
  77.                 String response = process(request);
  78.                 // 3. 把响应写回给客户端. 把 OutputStream 使用 PrinterWriter 包裹一下, 方便进行发数据.
  79.                 PrintWriter writer = new PrintWriter(outputStream);
  80.                 //使用 PrintWriter 的 println 方法, 把响应返回给客户端.
  81.                 //此处用 println, 而不是 print 就是为了在结尾加上 \n . 方便客户端读取响应, 使用 scanner.next 读取.
  82.                 writer.println(response);
  83.                 //这里还需要加一个 "刷新缓冲区" 操作.
  84.                 //IO操作是比较有开销的相比于访问内存,进行IO次数越多,程序越慢
  85.                 //为了提高效率,减少IO次数,使用一块内存充当缓冲区,写数据先写到缓冲区里,攒一波再进行IO,flush就可以刷新缓冲区,确保数据真的通过网卡发了出去,而不是残留在内存缓冲区
  86.                 //上述说的是全缓冲,而换行('\n')刷新是行缓冲,行缓冲通常用于标准输入输出(控制台上打印和输入我们通常输入enter linux本质就是'\n',windows 是 '\r\n'),//但我们现在是往网卡/网络文件中写(全缓冲,不会受到'\n'影响)
  87.                 writer.flush();
  88.                 // 日志, 打印当前的请求详情.
  89.                 System.out.printf("[%s:%d] req: %s, resp: %s\n", clientSocket.getInetAddress().toString(), clientSocket.getPort(),
  90.                         request, response);
  91.             }
  92.         } catch (IOException e) {
  93.             e.printStackTrace();//捕获try的IO异常
  94.         } finally {
  95.             // 在 finally 中加上 close 操作, 确保当前 socket 被及时关闭!!
  96.             try {
  97.                 clientSocket.close();
  98.             } catch (IOException e) {
  99.                 e.printStackTrace();
  100.             }
  101.         }
  102.     }
  103.     public String process(String request) {
  104.         return request;
  105.     }
  106.     public static void main(String[] args) throws IOException {
  107.         TcpEchoServer server = new TcpEchoServer(9090);
  108.         server.start();
  109.     }
  110. }
复制代码
5.0 TCP 客户端

题目与解决


代码示例

  1. import java.io.IOException;
  2. import java.io.InputStream;
  3. import java.io.OutputStream;
  4. import java.io.PrintWriter;
  5. import java.net.Socket;
  6. import java.util.Scanner;
  7. public class TcpEchoClient {
  8.     private Socket socket = null;
  9.     // 要和服务器通信, 就需要先知道, 服务器所在的位置.,这里的ip和端口号都是服务器的
  10.     public TcpEchoClient(String serverIp, int serverPort) throws IOException {
  11.         // 这个 new 操作完成之后, 就完成了 tcp 连接的建立.
  12.         //这一段代码本质做了相当多事,在网络原理中会讲述到
  13.         socket = new Socket(serverIp, serverPort);
  14.     }
  15.     public void start() {
  16.         System.out.println("客户端启动");
  17.         Scanner scannerConsole = new Scanner(System.in);
  18.         try (InputStream inputStream = socket.getInputStream();
  19.              OutputStream outputStream = socket.getOutputStream()) {
  20.             while (true) {
  21.                 // 1. 从控制台输入字符串.
  22.                 System.out.print("-> ");
  23.                 String request = scannerConsole.next();
  24.                 // 2. 把请求发送给服务器
  25.                 PrintWriter printWriter = new PrintWriter(outputStream);
  26.                 //    使用 println 带上换行. 后续服务器读取请求, 就可以使用 scanner.next 来获取了
  27.                 printWriter.println(request);
  28.                 //    不要忘记 flush, 确保数据是真的发送出去了!!
  29.                 printWriter.flush();
  30.                 // 3. 从服务器读取响应.
  31.                 Scanner scannerNetwork = new Scanner(inputStream);
  32.                 String response = scannerNetwork.next();
  33.                 // 4. 把响应打印出来
  34.                 System.out.println(response);
  35.             }
  36.         } catch (IOException e) {
  37.             e.printStackTrace();
  38.         }
  39.     }
  40.     public static void main(String[] args) throws IOException {
  41.         TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);
  42.         client.start();
  43.     }
  44. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4