在网上无意看到一个多线程的Socket服务器端例子,觉得非常不错。特别是其中的线程池的思想,简单而且高效。虽然JDK1.5开始已经自带了线程池包,但该代码不失为学习Socket和多线程的一个好的入门例子。
下面的代码是对该例子的简单整理,补充了注释。
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.LinkedList;
import java.util.List;
/**
* <pre>
* PooledConnectionHandler实现了Runnable接口,它的用途是处理服务器端传来的Socket连
* 接。该类维护了一个被称为"连接池"的全局链式列表(静态),该列表在类被加载时创建。
*
* 当该类的run()方法被调用时,它首先检查该"连接池"中是否有需要待处理的客户端连接,如果
* 没有(可能是请求尚未到来)则调用wait()方法等待,而另一个静态方法processRequest则负
* 责接收客户端请求并添加到"连接池"的末尾,然后通知所有正在等待的线程,各个等待的线程则
* 以"互斥"的方式竞争资源(请求)当某个线程率先获取到对象锁,拿到一个连接后,将释放锁,然
* 后在自己的线程中处理请求。各个线程之间彼此不会互相影响。
*
* 需要注意的是这个类的一个方法:processRequest这个方法第一个为静态方法,因为对于这个
* 方法来说,它只是一个负责通知的角色,所以没有必要是对象级的。将其修饰符置为static是合
* 理的。
*
* 其次我们看到在对全局资源"连接池"进行操作时,不管是往池中添加请求,还是从池中取出请求,
* 都需要在关键的语句块中增加synchronized{},来保证同一时刻不会有多个线程竞争同一个
* 资源,或者在添加资源未完成前有另一个线程试图读取该资源。
*
* 最后一个要注意的地方是其中的wait()和notifyAll()方法,wait()方法的调用发送在线程创建
* 完成,但请求尚未到来之前,这时线程并不持有对List的锁,而notifyAll()方法唤起所有等待
* 的线程,所有等待线程将一起竞争锁,此时只有一个线程可能检测到池非空而进入池中请求资
* 源。
* </pre>
*/
public class PooledConnectionHandler implements Runnable {
/** The client connection of Socket. */
protected Socket connection;
/** The request pool. */
protected static List pool = new LinkedList();
/**
* Instantiates a new pooled connection handler.
*/
public PooledConnectionHandler() {
}
/*
* (non-Javadoc)
*
* @see java.lang.Runnable#run()
*/
public void run() {
while (true) {
// 因为可能有多个线程同时去Pool中取Socket进行处理。
// 所以这里我们需同步,防止同一个请求被多次处理
synchronized (pool) {
while (pool.isEmpty()) {
try {
pool.wait();// 没有请求到来则等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 从池中取出一个Socket,准备进行处理
connection = (Socket) pool.remove(0);
}
// 取到Socket后则不需要同步了,因为此时是Connection是对象
// 级属性,在线程内部自己处理,不涉及公共资源的访问
handleConnection();
}
}
/**
* Process request, append Socket to pool and notify all waitting thread
*
* @param requestToHandle the request to handle
*/
public static void processRequest(Socket requestToHandle) {
// 因为有可能在向池中塞请求的时候,另外一个线程
// 正在从池中取Socket,所以这里需要同步一下
synchronized (pool) {
// 将来自客户端的请求添加到请求队列末尾
pool.add(pool.size(), requestToHandle);
// 通知其它正在等待的线程有新请求来到,
// 此时所有处于wait状态的线程将被唤醒
pool.notifyAll();
}
}
/**
* Handle connection.
*/
public void handleConnection() {
try {
PrintWriter streamWriter = new PrintWriter(connection
.getOutputStream());
BufferedReader streamReader = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
String fileToRead = streamReader.readLine();
BufferedReader fileReader = new BufferedReader(new FileReader(
fileToRead));
String line = null;
while ((line = fileReader.readLine()) != null)
streamWriter.println(line);
fileReader.close();
streamWriter.close();
streamReader.close();
} catch (FileNotFoundException e) {
System.out.println("");
} catch (IOException e) {
System.out.println("" + e);
}
}
}
import java.io.IOException;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* <pre>
* PooledRemoteFileServer是一个多线程、池化的Socket服务器。它能够在指定的端口
* 监听来自客户端的连接请求,同时它限定了允许同时连接的数目。
*
* 在服务器端,服务器启动时创建指定定数量的后台处理类实例,这些实例实际上是实现了
* Runnable接口的类,它们在创建完成后将立刻唤起,不停地监控来自客户端的连接。
*
* 另一方面,服务器在创建线程之后,将在指定的端口监听。一旦有客户端连接上,立刻将
* 该连接交给后台在等待的线程去处理,接着立刻返回继续在端口监听。这样的话后台线程
* 的处理将不会造成前端服务器监听的阻塞。
* </pre>
*/
public class PooledRemoteFileServer {
/** The max connections. */
protected int maxConnections;
/** The listen port. */
protected int listenPort;
/** The server socket. */
protected ServerSocket serverSocket;
/**
* Instantiates a new pooled remote file server.
*
* @param aListenPort the a listen port
* @param maxConnections the max connections
*/
public PooledRemoteFileServer(int aListenPort, int maxConnections) {
listenPort = aListenPort;// 监听端口
this.maxConnections = maxConnections;// 最大同时连接
}
/**
* 初始化池:每次创建一个Runnable实例,然后创建线程对象
*/
public void setUpHandlers() {
for (int i = 0; i < maxConnections; i++) {
PooledConnectionHandler currentHandler = new PooledConnectionHandler();
// 线程启动后将一直监控Socket队列,以轮询的方式
// 监控是否有新的客户端请求到来,如果有的话则取
// 出处理,无的话则继续等待直至请求到来
new Thread(currentHandler, "Handler" + i).start();
}
}
/**
* 接收客户端连接
*/
public void acceptConnections() {
try {
ServerSocket server = new ServerSocket(listenPort, 5);
Socket incomingConnection = null;
while (true) {
incomingConnection = server.accept();
handleConnection(incomingConnection);
}
} catch (BindException be) {
System.out.println("");
} catch (IOException ioe) {
System.out.println("" + listenPort);
}
}
/**
* 在池中处理Socket请求
*
* @param connectionToHandle the connection to handle
*/
protected void handleConnection(Socket connectionToHandle) {
PooledConnectionHandler.processRequest(connectionToHandle);
}
public static void main(String args[]) {
PooledRemoteFileServer server = new PooledRemoteFileServer(1001, 3);
// 初始化线程池
server.setUpHandlers();
// 开始在指定端口等待到来的请求
server.acceptConnections();
}
}
这个例子的精髓是在PooledConnectionHandler类,它首先创建一个公共的全局“线程池”(LinkList),然后启动线程监控线程池,与此同时服务器端在接收到客户端请求后将请求加到“线程池”中,这两个动作是异步的,在加的时候不允许读,在读得到时候不允许加(通过synchronized关键字控制),而且多个线程之间并不会互相影响,因为其中的connection属性是对象级的。
从这个例子中我们也可以学到在多线程的情况下,哪些变量是必须设置为全局的(static),哪些是必须设置为对象级的:即会被多个线程访问的资源必须设置为全局的,而跟线程处理状态,结果有关的属性一般必须设置为对象级的,以防止互相干扰。
其次就是在多线程情况下,哪些方法是可以设置为static的而不会出现线程安全的问题,哪些方法是不能设置为静态方法的:如果方法是属于控制流程,通知,派发的,那么一般可以设置为静态的。因为这些方法一般不需要多个,一个就够了。就如同控制器只要一个就够了。而业务逻辑实现方法一般不能设置为静态的,因为静态方法不能引用对象变量(非静态变量),但业务逻辑通常是需要针对不同的用户做出不同的处理的,所以几乎可以肯定的说是绝对会出现对象变量的。
转:http://www.blogjava.net/pengpenglin/archive/2008/08/16/222495.html
分享到:
相关推荐
通过上述步骤,你可以构建一个基础的Delphi多线程Socket服务器客户端系统,实现高效、稳定的网络通信。随着需求的增加,可以扩展功能,如添加多线程池管理、数据压缩、消息队列等高级特性,以提升系统的性能和可靠性...
在IT行业中,网络编程是不可...通过以上知识点,我们可以构建出一个简单的多线程C# socket聊天室。在实际开发中,还可以考虑添加更多的功能,如用户认证、加密传输、消息历史记录等,以增强聊天室的安全性和用户体验。
在Linux操作系统中,多线程编程与网络通信的结合是一个重要的技术领域,特别是在服务器开发中。...对于初学者来说,这是一个很好的起点,通过动手实践,逐步掌握Linux多线程Socket编程的核心技巧。
以下是一个简单的多线程Socket服务器的步骤: 1. **创建ServerSocket对象**:首先,服务器需要创建一个`ServerSocket` 对象,并指定一个端口号来监听。例如: ```java ServerSocket serverSocket = new ...
Socket通讯与多线程处理是计算机网络编程中的重要概念,主要应用于分布式系统、服务器开发以及实时数据传输等场景。在本实例中,我们将探讨如何利用Socket进行通信,并结合多线程技术来提升程序的并发处理能力。 ...
在多线程SOCKET实例中,服务器端通常会创建一个主监听线程,负责接受客户端的连接请求。当收到请求时,主监听线程会创建一个新的工作线程来处理这个客户端的通信,而自身则继续监听新的连接。这样,每个工作线程都...
本实例主要探讨的是使用C++实现基于Socket的多线程通信,具体涉及了VC++(Visual C++)环境下的开发,以及C/S(Client/Server)架构的双向通信模式。下面我们将详细讲解相关知识点。 首先,Socket是网络通信的基本...
接着,我们转向多线程Socket通信。在这种模式下,服务器端会为每个客户端连接创建一个新的线程,这样每个客户端的请求都能独立处理,提高了并发性能。然而,多线程也有其挑战,比如线程的创建、销毁和管理会消耗资源...
《C#.NET 4.0实现的多线程Socket聊天室服务器与客户端详解》 在IT领域,网络通信是至关重要的部分,而Socket编程作为网络通信的基础,是开发者必须掌握的关键技能之一。本文将深入探讨如何使用C#.NET 4.0框架,结合...
在这个名为"JAVA写的多线程socket通信程序源码"的项目中,我们可以看到作者通过Java语言实现了一个基于多线程的Socket通信模型,这通常是服务器和客户端之间的交互。 首先,让我们了解一下Java中的Socket。Socket是...
总之,"socket多线程例程非阻塞模式"是一个实用的编程实例,涵盖了网络通信、多线程以及异步处理的关键技术,对于学习和理解这些概念非常有价值。通过对源代码的分析和实践,开发者能够提升自己的网络编程技能,为...
在这个Java多线程Socket实现中,我们将探讨如何创建服务器端(ServerSocket)和客户端(Socket),以及如何使用多线程来处理并发连接。 首先,我们需要理解Java中的`Socket`类和`ServerSocket`类。`ServerSocket`是...
### Delphi多线程Socket编程介绍 随着分布式系统的广泛应用,多任务并发技术变得越来越重要。在当前基于多线程的操作系统环境下,开发并发多任务程序已成为软件开发领域的一个热点话题。Delphi作为一种强大的开发...
在本文中,我们将深入探讨如何使用C#语言编写一个基于...总之,C#的Socket类库为开发多线程Socket服务器提供了强大的支持。通过合理的设计和编程实践,我们可以创建出稳定、高效的网络服务,满足各种应用场景的需求。
以下是一个简单的多线程Socket服务端程序结构: 1. 初始化Socket:调用`socket()`创建Socket,设置套接字选项,如`SO_REUSEADDR`,以允许快速重启服务。 2. 绑定与监听:使用`bind()`将Socket与指定的IP地址和端口...
本文将深入探讨基于Visual C++的TCP多线程客户端-服务器结构,并以"vc socket tcp 多线程客户端--服务器结构的例子"为例进行解析。这个例子包含了一个名为"RawSocketServerExample"的文件,很可能是实现此架构的源...
综上所述,"基于多线程和SOCKET聊天室"项目展示了如何在C#环境下利用多线程和Socket技术实现一个简单的聊天室。它不仅涵盖了网络编程的核心概念,还涉及到UI设计和并发处理,对于学习和理解这些技术具有很高的实践...
多线程Socket阻塞模式下通信的例子 BCB-Socket阻塞模式通讯测试(多线程) 作者:Sncel(地狱情人) QQ:6522203 指导:invalid(空心菜) QQ:309283 http://bcb.vicp.net 测试方法: 在单机上测试: 如果有多个客户...
在Python编程中,多线程和Socket通信是两个重要的概念,它们经常被结合使用以实现高效、并发的网络服务。本篇文章将详细讲解如何在Python中编写一个多线程的Socket程序...这将帮助你掌握Python的多线程Socket编程技术。