php做网站需要后台吗,网站制作文章标签,珠江新城网站建设,佛山网站优化搜索目录 主要实现需求TCP 服务端收发并行重构启动main方法重构重构分离收发消息的操作重构接收消息的操作重构发送消息TCPServer调用发送消息的逻辑监听客户端链接逻辑重构Socket、流的退出与关闭 TCP 客户端收发并行重构客户端 main函数重构客户端接收消息重构客户端发送消息重构… 目录 主要实现需求TCP 服务端收发并行重构启动main方法重构重构分离收发消息的操作重构接收消息的操作重构发送消息TCPServer调用发送消息的逻辑监听客户端链接逻辑重构Socket、流的退出与关闭 TCP 客户端收发并行重构客户端 main函数重构客户端接收消息重构客户端发送消息重构客户端 linkWith 主方法重构 TCP 收发并行重构测试服务端重构后执行日志客户端重构后执行日志 源码下载 主要实现需求
多线程收发并行 TCP多线程收发协作 TCP 服务端收发并行重构
TCP 服务端收发并行重构
启动main方法重构
原有的main逻辑如下
重构后如下
public class Server {public static void main(String[] args) throws IOException {TCPServer tcpServer new TCPServer(TCPConstants.PORT_SERVER);boolean isSucceed tcpServer.start();if(!isSucceed){System.out.println(Start TCP server failed.);}UDPProvider.start(TCPConstants.PORT_SERVER);// 键盘输入BufferedReader bufferedReader new BufferedReader(new InputStreamReader(System.in));String str;do {str bufferedReader.readLine();tcpServer.broadcast(str);} while (!00bye00.equalsIgnoreCase(str));UDPProvider.stop();tcpServer.stop();}
}重构后从while循环不断读取键盘输入信息当输入“00bye00” 时退出读取。此处只读取键盘输入数据客户端发送的数据在会重新拆分出来新的线程单独处理。
重构分离收发消息的操作
创建 ClientHandler.java 重构收发消息操作
public class ClientHandler {private final Socket socket;private final ClientReadHandler readHandler;private final ClientWriteHandler writeHandler;private final CloseNotiry closeNotiry;public ClientHandler(Socket socket, CloseNotiry closeNotiry ) throws IOException {this.socket socket;this.readHandler new ClientReadHandler(socket.getInputStream());this.writeHandler new ClientWriteHandler(socket.getOutputStream());this.closeNotiry closeNotiry;System.out.println(新客户链接 socket.getInetAddress() \tP: socket.getPort());}
}重构接收消息的操作 /*** 接收数据*/class ClientReadHandler extends Thread {private boolean done false;private final InputStream inputStream;ClientReadHandler(InputStream inputStream){this.inputStream inputStream;}Overridepublic void run(){super.run();try {// 得到输入流用于接收数据BufferedReader socketInput new BufferedReader(new InputStreamReader(inputStream));do {// 客户端拿到一条数据String str socketInput.readLine();if(str null){System.out.println(客户端已无法读取数据);// 退出当前客户端ClientHandler.this.exitBySelf();break;}// 打印到屏幕System.out.println(str);}while (!done);socketInput.close();}catch (IOException e){if(!done){System.out.println(连接异常断开);ClientHandler.this.exitBySelf();}}finally {// 连接关闭CloseUtils.close(inputStream);}}void exit(){done true;CloseUtils.close(inputStream);}}创建一个单独的线程进行接收消息该线程不需要关闭。
重构发送消息 /*** 发送数据*/class ClientWriteHandler {private boolean done false;private final PrintStream printStream;private final ExecutorService executorService;ClientWriteHandler(OutputStream outputStream) {this.printStream new PrintStream(outputStream);// 发送消息使用线程池来实现this.executorService Executors.newSingleThreadExecutor();}void exit(){done true;CloseUtils.close(printStream);executorService.shutdown();}void send(String str) {executorService.execute(new WriteRunnable(str));}class WriteRunnable implements Runnable{private final String msg;WriteRunnable(String msg){this.msg msg;}Overridepublic void run(){if(ClientWriteHandler.this.done){return;}try {ClientWriteHandler.this.printStream.println(msg);}catch (Exception e){e.printStackTrace();}}}}TCPServer调用发送消息的逻辑 public void broadcast(String str) {for (ClientHandler client : clientHandlerList){// 发送消息client.send(str);}}监听客户端链接逻辑重构 private ListClientHandler clientHandlerList new ArrayList();/*** 监听客户端链接*/private class ClientListener extends Thread {private ServerSocket server;private boolean done false;private ClientListener(int port) throws IOException {server new ServerSocket(port);System.out.println(服务器信息 server.getInetAddress() \tP: server.getLocalPort());}Overridepublic void run(){super.run();System.out.println(服务器准备就绪~);// 等待客户端连接do{// 得到客户端Socket client;try {client server.accept();}catch (Exception e){continue;}try {// 客户端构建异步线程ClientHandler clientHandler new ClientHandler(client, handler - clientHandlerList.remove(handler));// 启动线程clientHandler.readToPrint();clientHandlerList.add(clientHandler);} catch (IOException e) {e.printStackTrace();System.out.println(客户端连接异常 e.getMessage());}}while (!done);System.out.println(服务器已关闭);}void exit(){done true;try {server.close();}catch (IOException e){e.printStackTrace();}}}clientHandlerList作为已经建立了连接的客户端的集合用于管理当前用户的信息。接收与发送都使用该集合。
Socket、流的退出与关闭 /*** 退出、关闭流*/public void exit(){readHandler.exit();writeHandler.exit();CloseUtils.close(socket);System.out.println(客户端已退出 socket.getInetAddress() \tP socket.getPort());}/*** 发送消息* param str*/public void send(String str){writeHandler.send(str);}/*** 接收消息*/public void readToPrint() {readHandler.exit();}/*** 接收、发送消息异常自动关闭*/private void exitBySelf() {exit();closeNotiry.onSelfClosed(this);}/*** 关闭流*/public interface CloseNotiry{void onSelfClosed(ClientHandler handler);}TCP 客户端收发并行重构
客户端 main函数重构 public static void main(String[] args) {// 定义10秒的搜索时间如果超过10秒未搜索到就认为服务器端没有开机ServerInfo info UDPSearcher.searchServer(10000);System.out.println(Server: info);if( info ! null){try {TCPClient.linkWith(info);}catch (IOException e){e.printStackTrace();}}}客户端接收消息重构 static class ReadHandler extends Thread{private boolean done false;private final InputStream inputStream;ReadHandler(InputStream inputStream){this.inputStream inputStream;}Overridepublic void run(){try {// 得到输入流用于接收数据BufferedReader socketInput new BufferedReader(new InputStreamReader(inputStream));do {// 客户端拿到一条数据String str null;try {str socketInput.readLine();}catch (SocketTimeoutException e){}if(str null){System.out.println(连接已关闭无法读取数据);break;}// 打印到屏幕System.out.println(str);}while (!done);socketInput.close();}catch (IOException e){if(!done){System.out.println(连接异常断开 e.getMessage());}}finally {// 连接关闭CloseUtils.close(inputStream);}}void exit(){done true;CloseUtils.close(inputStream);}}创建ReadHandler用单独的线程去接收服务端的消息。连接关闭则exit() 关闭客户端。
客户端发送消息重构 private static void write(Socket client) throws IOException {// 构建键盘输入流InputStream in System.in;BufferedReader input new BufferedReader(new InputStreamReader(in));// 得到Socket输出流并转换为打印流OutputStream outputStream client.getOutputStream();PrintStream socketPrintStream new PrintStream(outputStream);boolean flag true;do {// 键盘读取一行String str input.readLine();// 发送到服务器socketPrintStream.println(str);// 从服务器读取一行if(00bye00.equalsIgnoreCase(str)){break;}}while(flag);// 资源释放socketPrintStream.close();}在linkWith() 中调用write() 发送方法由 do-while 循环读取本地键盘输入信息进行发送操作。当满足 “00bye00” 时关闭循环关闭socket连接结束该线程。
客户端 linkWith 主方法重构 public static void linkWith(ServerInfo info) throws IOException {Socket socket new Socket();// 超时时间socket.setSoTimeout(3000);// 端口2000;超时时间300mssocket.connect(new InetSocketAddress(Inet4Address.getByName(info.getAddress()),info.getPort()));//System.out.println(已发起服务器连接并进入后续流程~);System.out.println(客户端信息 socket.getLocalAddress() \tP: socket.getLocalPort());System.out.println(服务器信息 socket.getInetAddress() \tP: socket.getPort());try {ReadHandler readHandler new ReadHandler(socket.getInputStream());readHandler.start();// 发送接收数据write(socket);}catch (Exception e){System.out.println(异常关闭);}// 释放资源socket.close();System.out.println(客户端已退出~);}原有的逻辑里是调用 todo() 方法在todo() 方法里同时进行收发操作。现在是进行读写分离。
TCP 收发并行重构测试
服务端重构后执行日志 客户端重构后执行日志 源码下载
下载地址https://gitee.com/qkongtao/socket_study/tree/master/src/main/java/cn/kt/socket/SocketDemo_L5_TCP_Channel