`

JAVA套接字(Socket)五 连接池例子

阅读更多
1. 介绍
我们现在已经拥有的 MultithreadedServer 每当有客户机申请一个连接时都在一个新 Thread 中创建一个新 ConnectionHandler。这意味着可能有一捆 Thread “躺”在我们周围。而且创建 Thread 的系统开销并不是微不足道的。如果性能成为了问题(也请不要事到临头才意识到它),更高效地处理我们的服务器是件好事。那么,我们如何更高效地管理服务器端呢?我们可以维护一个进入的连接池,一定数量的 ConnectionHandler 将为它提供服务。这种设计能带来以下好处:
  • 它限定了允许同时连接的数目。
  • 我们只需启动 ConnectionHandler Thread 一次。

幸运的是,跟在我们的多线程示例中一样,往代码中添加“池”不需要来一个大改动。事实上,应用程序的客户机端根本就不受影响。在服务器端,我们在服务器启动时创建一定数量的 ConnectionHandler,我们把进入的连接放入“池”中并让 ConnectionHandler 打理剩下的事情。这种设计中有很多我们不打算讨论的可能存在的技巧。例如,我们可以通过限定允许在“池”中建立的连接的数目来拒绝客户机。
请注意:我们将不会再次讨论 acceptConnections()。这个方法跟前面示例中的完全一样。它无限循环地调用 ServerSocket 上的 accept() 并把连接传递到 handleConnection()。
2. 创建 PooledRemoteFileServer 类
这里是 PooledRemoteFileServer 类的结构:
import java.io.*;
import java.net.*;
import java.util.*;

public class PooledRemoteFileServer {
    protected int maxConnections;
    protected int listenPort;
    protected ServerSocket serverSocket;

    public PooledRemoteFileServer(int aListenPort, int maxConnections) {
        listenPort = aListenPort;
        this.maxConnections = maxConnections;
    }
    public static void main(String[] args) {
    }
    public void setUpHandlers() {
    }
    public void acceptConnections() {
    }
    protected void handleConnection(Socket incomingConnection) {
    }
}


请注意一下您现在应该熟悉了的 import 语句。我们给类以下实例变量以保存:
  • 我们的服务器能同时处理的活动客户机连接的最大数目
  • 进入的连接的侦听端口(我们没有指定缺省值,但如果您想这样做,并不会受到限制)
  • 将接受客户机连接请求的 ServerSocket

类的构造器用的参数是侦听端口和连接的最大数目
我们的类有一个 main() 方法和三个其它方法。稍后我们将探究这些方法的细节。现在只须知道 setUpHandlers() 创建数目为 maxConnections 的大量 PooledConnectionHandler,而其它两个方法则与我们前面已经看到的相似:acceptConnections() 在 ServerSocket 上侦听传入的客户机连接,而 handleConnection 则在客户机连接一旦被建立后就实际处理它。

3. 实现 main()
这里我们实现需作改动的 main() 方法,该方法将创建能够处理给定数目的客户机连接的 PooledRemoteFileServer,并告诉它接受连接:
public static void main(String[] args) {
    PooledRemoteFileServer server = new PooledRemoteFileServer(3000, 3);
    server.setUpHandlers();
    server.acceptConnections();
}


我们的 main() 方法很简单。我们实例化一个新的 PooledRemoteFileServer,它将通过调用 setUpHandlers() 来建立三个 PooledConnectionHandler。一旦服务器就绪,我们就告诉它 acceptConnections()。
4. 建立连接处理程序
public void setUpHandlers() {
    for (int i = 0; i < maxConnections; i++) {
        PooledConnectionHandler currentHandler = new PooledConnectionHandler();
        new Thread(currentHandler, "Handler " + i).start();
    }
}


setUpHandlers() 方法创建 maxConnections(例如 3)个 PooledConnectionHandler 并在新 Thread 中激活它们。用实现了 Runnable 的对象来创建 Thread 使我们可以在 Thread 调用 start() 并且可以期望在 Runnable 上调用了 run()。换句话说,我们的 PooledConnectionHandler 将等着处理进入的连接,每个都在它自己的 Thread 中进行。我们在示例中只创建三个 Thread,而且一旦服务器运行,这就不能被改变。

5. 处理连接
这里我们实现需作改动的 handleConnections() 方法,它将委派 PooledConnectionHandler 处理连接:
protected void handleConnection(Socket connectionToHandle) {
PooledConnectionHandler.processRequest(connectionToHandle);
}


我们现在叫 PooledConnectionHandler 处理所有进入的连接(processRequest() 是一个静态方法)。
这里是 PooledConnectionHandler 类的结构:
import java.io.*;
import java.net.*;
import java.util.*;

public class PooledConnectionHandler implements Runnable {
    protected Socket connection;
    protected static List pool = new LinkedList();

    public PooledConnectionHandler() {
    }
    public void handleConnection() {
    }
    public static void processRequest(Socket requestToHandle) {
    }
    public void run() {
    }
}


这个助手类与 ConnectionHandler 非常相似,但它带有处理连接池的手段。该类有两个实例变量:
connection 是当前正在处理的 Socket
名为 pool 的静态 LinkedList 保存需被处理的连接

6. 填充连接池
这里我们实现 PooledConnectionHandler 上的 processRequest() 方法,它将把传入请求添加到池中,并告诉其它正在等待的对象该池已经有一些内容:
public static void processRequest(Socket requestToHandle) {
    synchronized (pool) {
        pool.add(pool.size(), requestToHandle);
        pool.notifyAll();
    }
}


理解这个方法要求有一点关于 Java 的关键字 synchronized 如何工作的背景知识。我们将简要讲述一下线程。
先来看一些定义:
  • 原子方法。在执行过程中不能被中断的方法(或代码块)
  • 互斥锁。客户机欲执行原子方法时必须获得的单个“锁”

因此,当对象 A 想使用对象 B 的 synchronized 方法 doSomething() 时,对象 A 必须首先尝试获取对象 B 的互斥锁。是的,这意味着当对象 A 拥有该互斥锁时,没有其它对象可以调用对象 B 上任何其它 synchronized 方法。
synchronized 块是个稍微有些不同的东西。您可以同步任何对象上的一个块,而不只是在本身的某个方法中含有该块的对象。在我们的示例中,processRequest() 方法包含有一个 pool(请记住它是一个 LinkedList,保存等待处理的连接池)的 synchronized块。我们这样做的原因是确保没有别人能跟我们同时修改连接池。
既然我们已经保证了我们是唯一“涉水”池中的人,我们就可以把传入的 Socket 添加到 LinkedList 的尾端。一旦我们添加了新的连接,我们就用以下代码通知其它正在等待该池的 Thread,池现在已经可用:
pool.notifyAll();

Object 的所有子类都继承这个 notifyAll() 方法。这个方法,连同我们下一屏将要讨论的 wait() 方法一起,就使一个 Thread 能够让另一个 Thread 知道一些条件已经具备。这意味着该第二个 Thread 一定正在等待那些条件的满足。

7. 从池中获取连接
这里我们实现 PooledConnectionHandler 上需作改动的 run()方法,它将在连接池上等待,并且池中一有连接就处理它:
public void run() {
        while (true) {
             synchronized (pool) {
                  while (pool.isEmpty()) {
                       try {
                            pool.wait();
                       } catch (InterruptedException e) {
                            return;
                       }
                   }
                   connection = (Socket) pool.remove(0);
             }
             handleConnection();
        }
}


回想一下在前一屏讲过的:一个 Thread 正在等待有人通知它连接池方面的条件已经满足了。在我们的示例中,请记住我们有三个 PooledConnectionHandler 在等待使用池中的连接。每个 PooledConnectionHandler 都在它自已的 Thread 中运行,并通过调用 pool.wait() 产生阻塞。当我们的 processRequest() 在连接池上调用 notifyAll() 时,所有正在等待的 PooledConnectionHandler 都将得到“池已经可用”的通知。然后各自继续前行调用 pool.wait(),并重新检查 while(pool.isEmpty()) 循环条件。除了一个处理程序,其它池对所有处理程序都将是空的,因此,在调用 pool.wait() 时,除了一个处理程序,其它所有处理程序都将再次产生阻塞。恰巧碰上非空池的处理程序将跳出 while(pool.isEmpty()) 循环并攫取池中的第一个连接:
connection = (Socket) pool.remove(0);

处理程序一旦有一个连接可以使用,就调用 handleConnection() 处理它。
在我们的示例中,池中可能永远不会有多个连接,只是因为事情很快就被处理掉了。如果池中有一个以上连接,那么其它处理程序将不必等待新的连接被添加到池。当它们检查 pool.isEmpty() 条件时,将发现其值为假,然后就从池中攫取一个连接并处理它。
还有另一件事需注意。当 run() 拥有池的互斥锁时,processRequest() 如何能够把连接放到池中呢?答案是对池上的 wait() 的调用释放锁,而 wait() 接着就在自己返回之前再次攫取该锁。这就使得池对象的其它同步代码可以获取该锁。
8. 处理连接:再一次
这里我们实现需做改动的 handleConnection() 方法,该方法将攫取连接的流,使用它们,并在任务完成之后清除它们:
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("Could not find requested file on the server.");
    } catch (IOException e) {
        System.out.println("Error handling a client: " + e);
    }
}


跟在多线程服务器中不同,我们的 PooledConnectionHandler 有一个 handleConnection() 方法。这个方法的代码跟非池式的 ConnectionHandler 上的 run() 方法的代码完全一样。首先,我们把 OutputStream 和 InputStream 分别包装进(用 Socket 上的 getOutputStream() 和 getInputStream())BufferedReader 和 PrintWriter。然后我们逐行读目标文件,就象我们在多线程示例中做的那样。再一次,我们获取一些字节之后就把它们放到本地的 line 变量中,然后写出到客户机。完成读写操作之后,我们关闭 FileReader 和打开的流。

9. 总结一下带有连接池的服务器
我们的带有连接池的服务器研究完了。让我们回顾一下创建和使用“池版”服务器的步骤:
  • 创建一个新种类的连接处理程序(我们称之为 PooledConnectionHandler)来处理池中的连接。
  • 修改服务器以创建和使用一组 PooledConnectionHandler。

   附: PooledRemoteFileServer 的完整代码清单
import java.io.*;
import java.net.*;
import java.util.*;

public class PooledRemoteFileServer {
    protected int maxConnections;
    protected int listenPort;
    protected ServerSocket serverSocket;
    public PooledRemoteFileServer(int aListenPort, int maxConnections) {
        listenPort = aListenPort;
        this.maxConnections = maxConnections;
    }
    public void acceptConnections() {
        try {
            ServerSocket server = new ServerSocket(listenPort, 5);
            Socket incomingConnection = null;
            while (true) {
                incomingConnection = server.accept();
                handleConnection(incomingConnection);
            }
        } catch (BindException e) {
            System.out.println("Unable to bind to port " + listenPort);
        } catch (IOException e) {
            System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort);
        }
    }
    protected void handleConnection(Socket connectionToHandle) {
        PooledConnectionHandler.processRequest(connectionToHandle);
    }
    public static void main(String[] args) {
        PooledRemoteFileServer server = new PooledRemoteFileServer(3000, 3);
        server.setUpHandlers();
        server.acceptConnections();
    }
    public void setUpHandlers() {
        for (int i = 0; i < maxConnections; i++) {
            PooledConnectionHandler currentHandler = new PooledConnectionHandler();
            new Thread(currentHandler, "Handler " + i).start();
        }
    }
}


PooledConnectionHandler 的完整代码清单
import java.io.*;
import java.net.*;
import java.util.*;

public class PooledConnectionHandler implements Runnable {
    protected Socket connection;
    protected static List pool = new LinkedList();
    public PooledConnectionHandler() {
    }
    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("Could not find requested file on the server.");
        } catch (IOException e) {
            System.out.println("Error handling a client: " + e);
        }
    }
    public static void processRequest(Socket requestToHandle) {
        synchronized (pool) {
            pool.add(pool.size(), requestToHandle);
            pool.notifyAll();
        }
    }
    public void run() {
        while (true) {
            synchronized (pool) {
                while (pool.isEmpty()) {
                    try {
                        pool.wait();
                    } catch (InterruptedException e) {
                        return;
                    }
                }
                connection = (Socket) pool.remove(0);
            }
            handleConnection();
        }
    }
}


ref:http://blog.csdn.net/m13666368773/article/details/7633145
分享到:
评论

相关推荐

    java socket连接池 实现

    Java Socket 连接池实现是提高网络应用性能和效率的关键技术之一。在高并发的网络环境中,频繁地创建和销毁Socket连接会导致大量的系统资源浪费,影响整体性能。为了解决这个问题,开发人员通常会使用连接池来管理和...

    JAVA网络编程资料(1)-Socket套接字—Java套接字编程.chm

    JAVA网络编程资料(1)-Socket套接字—Java套接字编程.chm

    Socket套接字—Java套接字编程

    Socket套接字是Java编程语言中用于网络通信的核心组件,它是实现客户端-服务器模型的基础。在Java中,Socket和ServerSocket类提供了套接字编程的基本功能。本文将深入探讨Java套接字编程的相关知识点,包括Socket和...

    java socket连接池

    Java Socket连接池是一种优化网络通信性能的技术,它允许应用程序复用已经建立的Socket连接,从而减少因频繁创建和销毁Socket连接而产生的开销。在高并发的服务器环境中,Socket连接池能够有效地提升系统效率和响应...

    Java实现Socket长连接和短连接

    综上所述,Java实现Socket长连接和短连接涉及网络通信基础、连接管理、异常处理等多个方面,开发者需要根据实际需求权衡选择合适的方式。通过深入理解这些概念和技术,可以有效地优化网络服务,提升应用的性能和用户...

    socket 套接字 Java 套接字 编程

    - Socket(套接字):在计算机网络中,Socket是应用程序通过网络进行通信的接口,它允许两个网络应用程序之间建立连接并进行数据传输。 - TCP/IP协议:Socket通常基于TCP/IP协议族工作,提供可靠的面向连接的通信...

    java基于UNIX域套接字(unix domain socket)连接redis

    在这个例子中,`"unix:///path/to/redis.sock"`是连接URI,指定了Redis服务器的UNIX域套接字路径。`RedisClient.create()`方法会创建一个Redis客户端实例,然后调用`connect()`获取到一个`StatefulRedisConnection`...

    socket 客户端连接池实现

    在Java中,可以使用Apache Commons Pool库来实现Socket连接池,或者自定义一个基于LinkedList或ConcurrentHashMap的数据结构来管理和维护连接。同时,可以结合JMX(Java Management Extensions)进行监控,查看连接...

    基于Java的Socket套接字编程 源代码

    Java的Socket套接字编程是网络通信中的基础技术,它提供了两台计算机间低级别的连接。在Java中,Socket类和ServerSocket类是实现客户端-服务器通信的核心组件。本资源包含的是基于Java的Socket套接字编程的源代码...

    java中socket套接字定义与使用

    总的来说,Java中的Socket套接字是实现网络通信的核心工具,无论是基于TCP的稳定传输还是UDP的高效传输,都能通过Socket灵活地构建网络应用。在实际开发中,开发者应根据应用的需求和场景选择合适的通信协议,以实现...

    Java 套接字编程Java 套接字编程

    Java支持两种主要类型的套接字:流套接字(`Socket`)和自寻址数据套接字(`DatagramSocket`)。 - **流套接字**:流套接字提供了一种可靠的、面向连接的服务,它基于TCP协议工作。流套接字适用于需要保证数据完整...

    Java套接字(socket)101

    我整理生成的CHM格式文件 Java套接字(socket)101 关于JAVA.NET包的SOCKET编程! 因为最近在搞这个所以整理了一些资料和大家分享 并非本人原创,郑重感谢原作者!

    socket线程连接池实例

    通常,我们会在工厂类中设置Socket的连接参数,如超时时间、套接字选项等。 2. **配置GenericObjectPool**:接着,我们需要对`GenericObjectPool`进行配置,包括设置最大池大小、空闲超时时间、是否允许空闲对象...

    Java Socket套接字

    Java Socket套接字是Java网络编程中的核心组件,它提供了应用程序之间进行低级通信的机制。Socket基于TCP/IP协议,允许两台计算机通过网络进行双向通信。本篇文章将深入探讨Java Socket的基础知识,包括其原理、创建...

    java与c++通过socket通信

    1. 使用`socket`函数创建一个套接字,如`socket(AF_INET, SOCK_STREAM, 0)`,定义协议族、套接字类型和协议。 2. 绑定套接字到本地地址,使用`bind`函数,指定IP和端口。 3. 监听来自客户端的连接请求,使用`listen`...

    一个经典的java 套接字传输范例

    Java套接字(Socket)是网络编程中的基本概念,它为两台计算机之间的通信提供了低级别的接口。在Java中,套接字允许客户端应用程序连接到服务器,并通过网络发送和接收数据。下面,我们将深入探讨Java套接字传输的...

    套接字socket编程文档

    在IT领域,套接字(Socket)编程是网络通信的核心技术之一,它为应用程序提供了低级别的、进程间的通信机制。套接字允许不同计算机上的进程相互通信,无论是同在一个局域网还是跨越互联网。本篇文章将深入探讨套接字...

    Socket套接字—Java套接字编程(上1).rar_socket_套接字_套接字 Java

    "Socket套接字—Java套接字编程(上1)"这个资料包可能包含了关于Java套接字的基础概念、创建和使用过程,以及相关实例。下面我们将深入探讨Java中的套接字编程。 首先,Java中的Socket类是Java.io包的一部分,它提供...

    java套接字聊天

    在Java中,我们使用`java.net.Socket`类来创建客户端套接字,`java.net.ServerSocket`类来创建服务器端套接字。 2. **服务器端实现**: - 首先,服务器需要监听特定的端口,这可以通过`ServerSocket(int port)`...

Global site tag (gtag.js) - Google Analytics