基本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(); } @Override public 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-1 2013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main 信息: Created and started Thread Thread-2 2013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main 信息: Created and started Thread Thread-3 2013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main 信息: Created and started Thread Thread-4 2013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main 信息: Created and started Thread Thread-5 2013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main 信息: Created and started Thread Thread-6 2013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main 信息: Created and started Thread Thread-7 2013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main 信息: Created and started Thread Thread-8 2013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main 信息: Created and started Thread Thread-9 2013-6-27 10:22:17 com.iteye.xuj.socket.TCPEchoServerThread main 信息: Created and started Thread Thread-10 2013-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 string 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据9 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据1 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据5 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据3 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据8 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据0 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据2 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据4 2013-6-27 10:22:17 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据6 2013-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-1 2013-6-27 10:28:09 com.iteye.xuj.socket.TCPEchoServerPool main 信息: Created and started Thread = Thread-2 2013-6-27 10:28:09 com.iteye.xuj.socket.TCPEchoServerPool main 信息: Created and started Thread = Thread-3 2013-6-27 10:28:09 com.iteye.xuj.socket.TCPEchoServerPool main 信息: Created and started Thread = Thread-4 2013-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 string 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Connected to server...sending echo string 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据4 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据2 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据1 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据0 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据3 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据6 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据8 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据7 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据9 2013-6-27 10:28:18 com.iteye.xuj.socket.SpendProtocol spend 信息: Received: 测试数据5
相关推荐
Java中的Socket线程池是一种高效的网络编程模型,它结合了Socket通信和多线程技术,以提高服务端处理客户端请求的并发性能。对于初学者来说,理解并掌握这个概念至关重要,因为这能帮助他们构建更稳定、可扩展的网络...
在Java中,Socket是实现TCP通信的基础类,它提供了客户端与服务器端之间的双向通信通道。下面将详细介绍Java Socket与TCP网络编程的相关知识点。 1. **TCP协议基础** TCP是一种面向连接的、可靠的、基于字节流的...
本压缩包文件"TCP-socket.zip"包含了一个基于Java实现的TCP Socket通信客户端和服务端,且支持多线程并发连接。这个学习资源可以帮助我们深入了解Java中的TCP Socket编程和多线程技术。 首先,让我们来看看TCP协议...
Java TCP/IP Socket编程是网络通信领域中的核心技术,尤其在Java编程中,Socket是实现客户端与服务器之间通信的基础。本资料“Java TCP-IP Socket编程-卡尔弗特.pdf”旨在深入探讨如何利用Java语言进行TCP/IP套接字...
在这个“java+socket 及多线程线程池应用”的教程中,我们可以期待学习到以下核心知识点: 1. **Socket基础**:首先会讲解Socket的基本概念,包括服务器端Socket和客户端Socket的工作原理,以及TCP/IP协议在Socket...
"SocketTest3"可能是一个包含示例代码的文件,它演示了如何将Socket与线程池结合来处理网络连接。这个文件可能包含以下内容: - Server端:创建ServerSocket监听端口,接收到连接请求后,不直接创建新线程,而是将...
- **实现**:为每个连接创建一个新线程或使用线程池管理线程。 6. **文件I/O操作**: - **输入流**:从网络中读取数据。 - **输出流**:向网络发送数据。 - **缓冲流**:提高读写效率。 7. **数据库操作**: ...
"HP-Socket-master" 是一个基于Java开发的网络通信框架,专为高性能、高并发的分布式系统设计。这个项目可能是为了提供一个稳定的、可扩展的、易于使用的套接字通信库,以支持企业级应用的需求。从其名称来看,"HP" ...
Java Socket 实例是Java网络编程中的重要组成部分,用于实现客户端与服务器之间的通信。在这个实例中,我们关注的是服务器端的多线程应用,这在处理多个并发连接时尤其有用。多线程允许服务器同时处理多个客户端请求...
2. 线程池中的线程负责与客户端建立Socket连接,接收和发送数据。 3. 如果所有线程都在忙,新来的请求会被放入队列等待,直到有线程可用。 4. 当线程完成任务后,不会立即销毁,而是返回线程池,等待下一次分配任务...
Java作为一种强大的编程语言,提供了丰富的API来支持TCP编程,使得开发者能够轻松地创建多线程TCP连接。本文将深入探讨如何使用Java实现多线程TCP连接,并涉及相关的关键知识点。 首先,TCP是一种面向连接的、可靠...
总的来说,Java Socket库提供了一个强大且灵活的框架,用于构建基于TCP协议的客户端-服务器应用程序。无论是简单的文件传输,还是复杂的应用级协议,都可以通过Java Socket实现。在实际开发中,了解和熟练掌握Socket...
在本项目"java,socket多线程一对一聊天"中,我们探讨的是如何使用Java的Socket库来实现一个简单的、一对一的聊天应用。由于描述中提到“暂不支持并发”,我们可以理解这个系统可能只处理单个连接,而不是同时处理多...
Java Socket是Java编程语言中实现网络通信的基础组件,它提供了应用程序与网络协议的接口,使得开发者可以构建基于TCP(传输控制协议)或UDP(用户数据报协议)的应用程序。本教程将深入探讨Java Socket编程的核心...
在Java编程中,多线程是一项关键特性,它允许程序同时执行多个任务,极大地提高了效率。在本项目"java多线程实现-tcp端口扫描"中,我们利用多线程技术来加速TCP端口扫描的过程。TCP(Transmission Control Protocol...
每个线程可以独立地与一个客户端进行通信,而不会阻塞其他客户端的处理。在Java或C++等支持多线程的语言中,我们可以使用特定的API(如Java的`Thread`类或C++的`std::thread`)来创建和管理线程。 在“socket-test...
线程池是一种管理线程的机制,而Socket是实现进程间通信(IPC)的一种方式,尤其在网络环境中。下面将详细阐述这两个概念以及它们的结合使用。 1. **线程池**: - **定义**:线程池是一组预先创建的线程,用于执行...
总的来说,`TCP client.java`和`TCP server.java`是TCP Socket编程的基础,它们展示了如何在Java中实现客户端和服务器之间的通信。通过学习和理解这些基础,开发者可以构建更复杂的网络应用,如聊天程序、文件传输...
2. **多线程**:每个客户端连接可能对应一个单独的线程,用于处理与该客户端的交互。 3. **线程池**:使用`ThreadPoolExecutor`来管理和调度线程,提高并发性能。 4. **并发控制**:可能使用`synchronized`关键字、`...
本实例探讨的是如何利用Java的Socket实现TCP(Transmission Control Protocol)协议下的多线程通讯,允许一个服务端同时处理多个客户端的连接请求。 首先,TCP是一种面向连接的、可靠的、基于字节流的传输层通信...