网络编程-TCP流套接字



目录

1.Java 流套接字编程模型

2.SeverSocket API

3.Socket API

4.TCP 中的长短连接

5. 代码示例:


1.Java 流套接字编程模型

网络编程-TCP流套接字


2.SeverSocket API

SeverSocket 是创建 TCP 服务端 Socket 的 API.

SeverSocket 的构造方法:

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

ServerSocket 方法:

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

3.Socket API

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

不管是客户端还是服务端 Socket , 都是双方建立连接后 , 保存的对端信息 , 及用来与对方收发数据的.

Socket 构造方法:

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

 Socket 方法:

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

4.TCP 中的长短连接

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

对比以上长短连接 , 两者区别如下:

扩展了解:

基于BIO(同步阻塞IO) 的长连接会一直占用系统资源. 对于并发要求很高的服务系统来说 , 这样的消耗是不能承受的.

实际应用时: 服务端一般是基于 NIO (即同步非阻塞IO) 来实现长连接 , 性能可以极大的提升.(IO多路复用)


5. 代码示例:

服务器:

public class TcpEchoSever {     private ServerSocket serverSocket = null;      public TcpEchoSever(int port) throws IOException {         this.serverSocket = new ServerSocket(port);     }      public void start() throws IOException {         System.out.println("启动服务器!");         while (true) {             //使用这个 clientSocket 和具体的客户端进行交流.             Socket clientSocket = serverSocket.accept();             processConnection(clientSocket);         }     }      //使用这个方法来处理一个连接     //这一个连接对应到一个客户端 , 但这里可能会涉及到多次交互     private void processConnection(Socket clientSocket) {         System.out.printf("[%s %d] 客户端上线!/n", clientSocket.getInetAddress().toString(), clientSocket.getPort());         // 基于上述 socket 对象和客户端机通信         try (InputStream inputStream = clientSocket.getInputStream();              OutputStream outputStream = clientSocket.getOutputStream()) {             //由于要处理多个请求响应 , 也需要使用循环来进行             while (true) {                 //1.读取请求                 Scanner scan = new Scanner(inputStream);                 if (!scan.hasNext()) {                     //没有下个数据 , 说明读完了(客户端关闭了连接)                     System.out.printf("[%s:%d] 客户端下线!/n", clientSocket.getInetAddress().toString(), clientSocket.getPort());                     break;                 }                 //此处使用的 next 返回结果中不包含空白符.                 String request = scan.next();                 //2.根据请求计算响应                 String response = process(request);                 //3.返回响应                 PrintWriter printWriter = new PrintWriter(outputStream);                 //此处使用 println 来写入 , 让结果带有一个 /n 来换行 , 方便对端接收解析                 printWriter.println(response);                 //flush 用来刷新缓冲区 , 保证当前写入的数据确实发出去了.                 printWriter.flush();                  System.out.printf("[%s:%d] req: %s; resp %s /n", clientSocket.getInetAddress().toString(), clientSocket.getPort(),                         request, response);             }         } catch (IOException e) {             e.printStackTrace();         } finally {             try {                 clientSocket.close();             } catch (IOException e) {                 throw new RuntimeException(e);             }         }     }      private String process(String request) {         return request;     }       public static void main(String[] args) throws IOException {         TcpEchoSever server = new TcpEchoSever(9090);         server.start();     } }

客户端:

public class TcpEchoClient {     private Socket socket = null;      public TcpEchoClient(String serverIp , int serverPort) throws IOException {         // Socket 构造方法 , 能够识别点分十进制格式的 IP 地址 , 比 DatagramPacket 更方便         // new 这个对象的同时就会和服务器进行 TCP 连接操作.         socket = new Socket(serverIp , serverPort);     }      public void start(){         System.out.println("客户端启动!");         Scanner scanner = new Scanner(System.in);         try(InputStream inputStream = socket.getInputStream();             OutputStream outputStream = socket.getOutputStream()) {             while(true){                 //1. 先从键盘上读取到用户输入的内容                 System.out.println(">");                 String request = scanner.next();                 if(request.equals("exit")){                     System.out.println("goodbye");                     break;                 }                 //2. 把读到的内容构造成请求发送给服务器                 PrintWriter printWriter = new PrintWriter(outputStream);                 printWriter.println(request);                 printWriter.flush();                 //3. 读取服务器的响应                 Scanner respScanner = new Scanner(inputStream);                 String response = respScanner.next();                 //4. 把响应显示到界面上                 System.out.println(response);             }         } catch (IOException e) {             throw new RuntimeException(e);         }     }      public static void main(String[] args) throws IOException {         TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1" , 9090);         tcpEchoClient.start();     } }

由这段代码可知 , 服务器每连接一个客户端 , 就会阻塞等待客户端发送请求 , 如果客户端不主动断开连接 , 该线程就会处于占用状态 , 因此单线程情况下 , 一个服务器只能连接一个客户端.

 public void start() throws IOException {         System.out.println("启动服务器!");         while (true) {             //使用这个 clientSocket 和具体的客户端进行交流.             Socket clientSocket = serverSocket.accept();             processConnection(clientSocket);         }     }

 为了满足连接多个客户端的需求 , 我们需要创建多个线程  , 但不断地创建/销毁线程 , 需要很大的开销 , 因此可以考虑使用线程池.

public void start() throws IOException {         System.out.println("启动服务器!");         ExecutorService threadPool = Executors.newCachedThreadPool();         while(true){             //使用这个 clientSocket 和具体的客户端进行交流.             Socket clientSocket = serverSocket.accept();             //多线程版本 //            Thread t = new Thread(()->{ //                processConnection(clientSocket); //            }); //            t.start();             //线程池版本             threadPool.submit(()->{                 processConnection(clientSocket);             });         }     }

运行结果:

网络编程-TCP流套接字

 网络编程-TCP流套接字

 网络编程-TCP流套接字

如果开启多个客户端?

网络编程-TCP流套接字

 网络编程-TCP流套接字

 网络编程-TCP流套接字

版权声明:本文内容由互联网用户撰写,该文观点仅代表作者本人。本站爱分享仅提供分享服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请立马联系本站,本站将立刻删除。
THE END
分享
二维码
< <上一篇
下一篇>>