首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 企业软件 > 行业软件 >

Java Socket-03 Java TCP 1客户一线程与线程池

2013-11-09 
Java Socket-03 Java TCP 一客户一线程与线程池? ? 基本TCP响应服务器一次只能处理一个客户端的请求。当一

Java Socket-03 Java TCP 一客户一线程与线程池

? ? 基本TCP响应服务器一次只能处理一个客户端的请求。当一个客户端向一个已经被其他客户端占用的服务器发送连接请求时,虽然其在连接建立后即可向服务器端发送数据,服务器端在处理完已有客户端的请求前,却不会对新的客户端作出响应,。这种类型的服务器称为"迭代服务器(iterative server)"。迭代服务器按顺序处理客户端的请求,也就是说在完成了对前一客户端的服务后,才会对下一个客户端进行响应。这种服务器最适用于每个客户端所请求的连接时间都被限制在较小范围内的应用中,而对于允许客户端请求长时间服务的情况,后续客户端将面临无法接受的长时间等待。

?

客户端代码,用于测试:

客户端线程类:

package com.iteye.xuj.socket;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;import java.net.SocketException;import java.net.UnknownHostException;import java.util.logging.Logger;public class SpendProtocol implements Runnable {private Socket socket = null;private byte[] data = null;private Logger logger;public SpendProtocol(Socket socket, byte[] data, Logger logger) {this.socket = socket;this.data = data;this.logger = logger;}public static void spend(Socket socket, byte[] data, Logger logger) throws UnknownHostException, IOException {logger.info("Connected to server...sending echo string");// 获取套接字的输入输出流InputStream in = socket.getInputStream();OutputStream out = socket.getOutputStream();// 发送字符串到回馈服务器out.write(data);// 从回馈服务器接受回馈信息int totalBytesRcvd = 0;int bytesRcvd;while (totalBytesRcvd < data.length) {if ((bytesRcvd = in.read(data, totalBytesRcvd, data.length - totalBytesRcvd)) == -1)throw new SocketException("Connection closed prematurely");totalBytesRcvd += bytesRcvd;}logger.info("Received: " + new String(data));// 关闭套接字socket.close();}@Overridepublic void run() {try {spend(socket, data, logger);} catch (Exception e) {e.printStackTrace();}}}

?客户端类,发送指定条数的信息:

package com.iteye.xuj.socket;import java.net.Socket;import java.util.logging.Logger;import java.io.IOException;public class TestClient {public static void main(String[] args) throws IOException {// 地址、数据、端口String server = "127.0.0.1";int servPort = 7;final Logger logger = Logger.getLogger("practical");// 发送10个请求int sendSize = 10;for (int i = 0; i < sendSize; i++) {byte[] data = ("测试数据" + i).getBytes();Socket socket = new Socket(server, servPort);// 创建一个新的Thread实例来处理新的连接Thread thread = new Thread(new SpendProtocol(socket, data, logger));// 为连接开始执行新的线程thread.start();}}}

?

?

服务器端线程类:

package com.iteye.xuj.socket;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;import java.util.logging.Level;import java.util.logging.Logger;//声明实现Runnable接口public class EchoProtocol implements Runnable {// 类成员变量private static final int BUFSIZE = 32;private Socket clntSock;private Logger logger;// 构造函数public EchoProtocol(Socket clntSock, Logger logger) {this.clntSock = clntSock;this.logger = logger;}// 实现回显协议public static void handleEchoClient(Socket clntSock, Logger logger) {try {// 从套接字中获取输入/输出流InputStream in = clntSock.getInputStream();OutputStream out = clntSock.getOutputStream();int recvMsgSize;int totalBytesEchoed = 0;byte[] echoBuffer = new byte[BUFSIZE];// 接收和回显while ((recvMsgSize = in.read(echoBuffer)) != -1) {out.write(echoBuffer, 0, recvMsgSize);totalBytesEchoed += recvMsgSize;}// 在日志中记录连续的详细信息,同时记录远端的SocketAddress和回显的字节数logger.info("Client " + clntSock.getRemoteSocketAddress() + ", echoed " + totalBytesEchoed + " bytes.");} catch (IOException ex) {// 将异常写入日志logger.log(Level.WARNING, "Exception in echo protocol", ex);} finally {try {clntSock.close();} catch (IOException e) {}}}public void run() {handleEchoClient(clntSock, logger);}}

?

?

一客户一线程服务器:

? ??在一客户一线程( thread-per-client ) 的服务器中, 为每个连接都创建了一个新的线程来处理。 服务器循环执行一些任务, 在指定端口上侦听连接, 反复接收客户端传入的连接请求,并为每个连接创建一个新的线程来对其进行处理。

?

服务器代码:

package com.iteye.xuj.socket;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.util.logging.Logger;public class TCPEchoServerThread {public static void main(String[] args) throws IOException {int echoServPort = 7;ServerSocket servSock = new ServerSocket(echoServPort);Logger logger = Logger.getLogger("practical");// 一直反复循环,处理传入的连接请求while (true) {// 接收传入的连接请求Socket clntSock = servSock.accept();// 创建一个新的Thread实例来处理新的连接Thread thread = new Thread(new EchoProtocol(clntSock, logger));// 为连接开始执行新的线程thread.start();// 记录日志logger.info("Created and started Thread " + thread.getName());}}}

?

启动服务器,再运行客户端:

服务器端输出:

2013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main信息: Created and started Thread Thread-12013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main信息: Created and started Thread Thread-22013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main信息: Created and started Thread Thread-32013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main信息: Created and started Thread Thread-42013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main信息: Created and started Thread Thread-52013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main信息: Created and started Thread Thread-62013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main信息: Created and started Thread Thread-72013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main信息: Created and started Thread Thread-82013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main信息: Created and started Thread Thread-92013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main信息: Created and started Thread Thread-102013-6-27 10:22:17 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29702, echoed 13 bytes.2013-6-27 10:22:17 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29701, echoed 13 bytes.2013-6-27 10:22:17 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29704, echoed 13 bytes.2013-6-27 10:22:17 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29710, echoed 13 bytes.2013-6-27 10:22:17 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29708, echoed 13 bytes.2013-6-27 10:22:17 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29709, echoed 13 bytes.2013-6-27 10:22:17 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29706, echoed 13 bytes.2013-6-27 10:22:17 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29703, echoed 13 bytes.2013-6-27 10:22:17 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29707, echoed 13 bytes.2013-6-27 10:22:17 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29705, echoed 13 bytes.

?客户端输出:

2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据92013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据12013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据52013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据32013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据82013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据02013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据22013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据42013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据62013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据7

?

?

线程池

? ??每个新线程都会消耗系统资源:创建一个线程将占用 CPU 周期,而且每个线程都自己的数据结构(如,栈)也要消耗系统内存。另外,当一个线程阻塞( block )时, JVM 将保存其状态, 选择另外一个线程运行, 并在上下文转换( context switch ) 时恢复阻塞线程的状态。 随着线程数的增加, 线程将消耗越来越多的系统资源。 这将最终导致系统花费更多的时间来处理上下文转换和线程管理, 更少的时间来对连接进行服务。 那种情况下, 加入一个额外的线程实际上可能增加客户端总服务时间。

? ??可以通过限制总线程数并重复使用线程来避免这个问题。与为每个连接创建一个新的线程不同,服务器在启动时创建一个由固定数量线程组成的线程池( thread pool )。当一个新的客户端连接请求传入服务器, 它将交给线程池中的一个线程处理。 当该线程处理完这个客户端后,又返回线程池,并为下一次请求处理做好准备。如果连接请求到达服务器时,线程池中的所有线程都已经被占用,它们则在一个队列中等待,直到有空闲的线程可用。?

? ??

服务器代码:

package com.iteye.xuj.socket;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.util.logging.Level;import java.util.logging.Logger;public class TCPEchoServerPool {public static void main(String[] args) throws IOException {int echoServPort = 7;int threadPoolSize = 5;final ServerSocket servSock = new ServerSocket(echoServPort);final Logger logger = Logger.getLogger("practical");// 创建并启动threadPoolSize个新线程for (int i = 0; i < threadPoolSize; i++) {Thread thread = new Thread() {public void run() {while (true) {try {// 接受连接请求Socket clntSock = servSock.accept();// 将客户端套接字传递给EchoProtocol.handleEchoClient()方法EchoProtocol.handleEchoClient(clntSock, logger);} catch (IOException ex) {logger.log(Level.WARNING, "Client accept failed", ex);}}}};thread.start();logger.info("Created and started Thread = " + thread.getName());}}}

?

启动服务顺,再运行客户端:

服务器启动时输出:

2013-6-27 10:28:09 com.iteye.xuj.socket.TCPEchoServerPool main信息: Created and started Thread = Thread-12013-6-27 10:28:09 com.iteye.xuj.socket.TCPEchoServerPool main信息: Created and started Thread = Thread-22013-6-27 10:28:09 com.iteye.xuj.socket.TCPEchoServerPool main信息: Created and started Thread = Thread-32013-6-27 10:28:09 com.iteye.xuj.socket.TCPEchoServerPool main信息: Created and started Thread = Thread-42013-6-27 10:28:09 com.iteye.xuj.socket.TCPEchoServerPool main信息: Created and started Thread = Thread-5

运行客户端后服务器端输出:

2013-6-27 10:28:18 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29732, echoed 13 bytes.2013-6-27 10:28:18 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29730, echoed 13 bytes.2013-6-27 10:28:18 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29729, echoed 13 bytes.2013-6-27 10:28:18 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29728, echoed 13 bytes.2013-6-27 10:28:18 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29731, echoed 13 bytes.2013-6-27 10:28:18 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29734, echoed 13 bytes.2013-6-27 10:28:18 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29736, echoed 13 bytes.2013-6-27 10:28:18 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29735, echoed 13 bytes.2013-6-27 10:28:18 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29737, echoed 13 bytes.2013-6-27 10:28:18 com.iteye.xuj.socket.EchoProtocol handleEchoClient信息: Client /127.0.0.1:29733, echoed 13 bytes.

?客户端输出:

2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Connected to server...sending echo string2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据42013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据22013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据12013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据02013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据32013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据62013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据82013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据72013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据92013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend信息: Received: 测试数据5

??

热点排行