鱼式疯言
从上面可以确定 Tcp的 有连接 的特点之一。
ServerSocket的构造方法 的连接是 端口号与进程的连接 , 相当于 “接好了电话线” 。
accept() 的连接是 服务器与客户端的连接 , 相当于 客户端那边给服务器 “打电话”, 而服务器这边就用 accept() 来接电话。完成连接。
用于客户端与服务器连接:
Socket 的构造方法, 在 实例化Socket 对象 时, 添加 IP地址
和 端口号
, 就意味着 建立了连接
。
操作 Socket对象 来获取各种 IP地址 和
输入和输出流对象
。
鱼式疯言
1.由于我们Tcp 特点
之一就是 面向字节流 , 并且是全双工
。2.
ServerSocket
类一般只适用于 服务器一端的端口号和进程的连接 , 而Socket
一般只适用于客户端一端
与 服务器 的连接, 但是都适用于 服务器和客户端 的 获取输入流与输出流 的操作。3.在 同一台主机 上,同一个协议 , 一个
端口号
直接连接一个进程
。4.无论是
ServerSocket 类
还是Socket 类
, 都是属于java.net
下的包的类。
回显服务器的普通实现代码展示
<1>. 服务器实现
package echoTCP; 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.Executor; import java.util.concurrent.Executors; public class MyTcpEchoServer { ServerSocket serverSocket = null; // 进行连接 public MyTcpEchoServer(int port) { try { // 进行连接 serverSocket = new ServerSocket(port); } catch (IOException e) { throw new RuntimeException(e); } } public void start() { // 开始交互 System.out.println("服务器开始运行..."); while(true) { // 使用线程池 Socket socket = null; try { socket = serverSocket.accept(); } catch (IOException e) { throw new RuntimeException(e); } // 进行接收请求并响应 ProcessServerConnect(socket); } } private void ProcessServerConnect(Socket socket) { try(InputStream inputSTream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream() ) { while(true) { System.out.printf("客户端上线 : [客户端ip = %s, 客户端端口号 = %d]\n" ,socket.getInetAddress(), socket.getPort()); // 得到请求 Scanner scanner = new Scanner(inputSTream); if(!scanner.hasNext()) { break; } // 得到请求 String request = scanner.nextLine(); // 计算响应 // 执行业务 String response = process(request); // 返回响应 // 让客户端得到业务的执行结果 PrintWriter printWriter = new PrintWriter(outputStream); // 开始返回 printWriter.println(response); // 进行刷新 printWriter.flush(); // 打印日志 System.out.printf("客户端下线 : [ip = %s , port = %d], 请求 = %s , 响应 = %s\n", socket.getInetAddress(), socket.getPort(),request,response); } } catch (IOException e) { throw new RuntimeException(e); } finally { System.out.printf("任务执行结束: [%s,%d]\n", socket.getInetAddress(), socket.getPort()); try { // 关闭 socket 流 socket.close(); } catch (IOException e) { throw new RuntimeException(e); } } } private String process(String request) { return request; } public static void main(String[] args) { MyTcpEchoServer myTcpEchoServer = new MyTcpEchoServer(9090); myTcpEchoServer.start(); } }
// 进行连接 serverSocket = new ServerSocket(port);
鱼式疯言
// 进行刷新 printWriter.flush();
2.小编在这里利用了 Scanner
和 printWriter
来包装输入和输出流。 但是小伙伴们也可以利用小编之前提及过的 IO流的操作方式 来进行哦, 效果是一样的 。
3.对于上述 回显服务器 来说, 服务端一端 accept()
只有当服务器每次发来请求时,才会进行 连接, 否则进行 阻塞等待
。
<2>. 客户端实现
1.实例化Socket 对象, 并把 IP地址
和 端口号
作为 Socket
构造方法的参数进行 客户端与服务端 的连接。
2.输入请求 并发送给 客户端
3.接收服务器返回的 响应并输出
4.宣布服务器执行结束, 并 关闭Socket对象
finally { System.out.printf("任务执行结束: [%s,%d]\n", socket.getInetAddress(), socket.getPort()); try { // 关闭 socket 流 socket.close(); } catch (IOException e) { throw new RuntimeException(e); } }
鱼式疯言
1.对应上述代码中的hasNext()
方法来说, 出现了换行
才会 break 结束 出去, 而当 服务器/ 客户端没有
发来数据时, 是会进入 阻塞等待 的, 不会break 直接跳出。
if(!scanner.hasNext()) { break; }
二. Tcp 回显服务器的多线程实现
1. 代码展示
package echoTCP; 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.Executor; import java.util.concurrent.Executors; public class MyTcpEchoServer { ServerSocket serverSocket = null; // 进行连接 public MyTcpEchoServer(int port) { try { // 进行连接 serverSocket = new ServerSocket(port); } catch (IOException e) { throw new RuntimeException(e); } } public void start() { // 开始交互 System.out.println("服务器开始运行..."); // 使用多线程 while(true) { Thread t = new Thread(()->{ Socket socket = null; try { socket = serverSocket.accept(); } catch (IOException e) { throw new RuntimeException(e); } // 进行接收请求并响应 ProcessServerConnect(socket); }); t.start(); } } private void ProcessServerConnect(Socket socket) { try(InputStream inputSTream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream() ) { while(true) { System.out.printf("客户端上线 : [客户端ip = %s,客户端端口号 = %d]\n",socket.getInetAddress(),socket.getPort()); // 得到请求 Scanner scanner = new Scanner(inputSTream); if(!scanner.hasNext()) { break; } // 得到请求 String request = scanner.nextLine(); // 计算响应 // 执行业务 String response = process(request); // 返回响应 // 让客户端得到业务的执行结果 PrintWriter printWriter = new PrintWriter(outputStream); // 开始返回 printWriter.println(response); // 进行刷新 printWriter.flush(); // 打印日志 System.out.printf("客户端下线 : [ip = %s , port = %d], 请求 = %s , 响应 = %s\n", socket.getInetAddress(),socket.getPort(),request,response); } } catch (IOException e) { throw new RuntimeException(e); } finally { System.out.printf("任务执行结束: [%s,%d]\n",socket.getInetAddress(),socket.getPort()); try { // 关闭 socket 流 socket.close(); } catch (IOException e) { throw new RuntimeException(e); } } } private String process(String request) { return request; } public static void main(String[] args) { MyTcpEchoServer myTcpEchoServer = new MyTcpEchoServer(9090); myTcpEchoServer.start(); } }
2. 原理剖析
当 第一个客户端进入内循环 时, 它的任务还没结束
, 第二个客户端就向服务器 发送请求 , 这时由于内部的还 一直循环, 服务器就无法进入内循环
, 只能让第二个客户端一直停留在缓冲区
。这时我们就引入 多线程 的方式, 将每一个服务器都分布同时
执行循环, 这样就能保证不会让多个
服务器一直阻塞到缓冲区
。
三. Tcp 回显服务器的线程池实现
1. 代码展示
package echoTCP; 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.Executor; import java.util.concurrent.Executors; public class MyTcpEchoServer { ServerSocket serverSocket = null; // 进行连接 public MyTcpEchoServer(int port) { try { // 进行连接 serverSocket = new ServerSocket(port); } catch (IOException e) { throw new RuntimeException(e); } } public void start() { // 开始交互 System.out.println("服务器开始运行..."); while(true) { // 使用线程池 Executor executor = Executors.newCachedThreadPool(); executor.execute(() -> { Socket socket = null; try { socket = serverSocket.accept(); } catch (IOException e) { throw new RuntimeException(e); } // 进行接收请求并响应 ProcessServerConnect(socket); }); } } private void ProcessServerConnect(Socket socket) { try(InputStream inputSTream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream() ) { while(true) { System.out.printf("客户端上线 : [客户端ip = %s,客户端端口号 = %d]\n",socket.getInetAddress(),socket.getPort()); // 得到请求 Scanner scanner = new Scanner(inputSTream); if(!scanner.hasNext()) { break; } // 得到请求 String request = scanner.nextLine(); // 计算响应 // 执行业务 String response = process(request); // 返回响应 // 让客户端得到业务的执行结果 PrintWriter printWriter = new PrintWriter(outputStream); // 开始返回 printWriter.println(response); // 进行刷新 printWriter.flush(); // 打印日志 System.out.printf("客户端下线 : [ip = %s , port = %d], 请求 = %s , 响应 = %s\n", socket.getInetAddress(),socket.getPort(),request,response); } } catch (IOException e) { throw new RuntimeException(e); } finally { System.out.printf("任务执行结束: [%s,%d]\n",socket.getInetAddress(),socket.getPort()); try { // 关闭 socket 流 socket.close(); } catch (IOException e) { throw new RuntimeException(e); } } } private String process(String request) { return request; } public static void main(String[] args) { MyTcpEchoServer myTcpEchoServer = new MyTcpEchoServer(9090); myTcpEchoServer.start(); } }
package echoTCP; 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; public class MyTcpEchoClient { Socket clientSocket = null; // 进行连接 public MyTcpEchoClient(String ip , int port) { try { // 进行连接 clientSocket = new Socket(ip, port); } catch (IOException e) { throw new RuntimeException(e); } } public void start() { // 开始交互 System.out.println("客户端开始运行..."); // 发送请求并接受响应 ProcessServerConnect(clientSocket); } private void ProcessServerConnect(Socket socket) { try(InputStream inputSTream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream() ) { while(true) { Scanner in = new Scanner(System.in); System.out.print("请输入你的需求-> "); // 输入请求并发送 String request = in.nextLine(); PrintWriter printWriter = new PrintWriter(outputStream); printWriter.println(request); // 进行刷新 printWriter.flush(); // 接收响应并输出 Scanner scanner = new Scanner(inputSTream); if(!scanner.hasNext()) { break; } String response = scanner.nextLine(); System.out.println("response = "+ response); } } catch (IOException e) { throw new RuntimeException(e); } finally { System.out.printf("任务执行结束: [%s,%d]\n",socket.getInetAddress(),socket.getPort()); try { // 关闭 socket 流 socket.close(); } catch (IOException e) { throw new RuntimeException(e); } } } public static void main(String[] args) { MyTcpEchoClient myTcpEchoClient = new MyTcpEchoClient("127.0.0.1",9090); myTcpEchoClient.start(); } }
代码说明
本文链接:https://blog.runxinyun.com/post/95.html 转载需授权!
留言0