Socket与连接池

问题描述:

 刚学了Socket编程,想拓展一下.与连接池一起用.增加其可用性.

自己写了下,运行没得到想要的结果.不知道错在哪里,所以只能请各位帮忙了.

Server端:
package pool;

import java.net.ServerSocket;
import java.net.Socket;

public class Server implements Runnable{
protected static int maxLeng;
private static int port;
private static ServerSocket server;

public Server() {
}
public Server(int port,int maxLength){
    this.port=port;
    this.maxLeng=maxLength;
}

public void run(){
    try {
        server=new ServerSocket(port,maxLeng);//创建一个最大链接次数为5次的ServerSocket.
        Socket socket=server.accept();
        pool.addSocket(socket);

    } catch (Exception e) {
        e.printStackTrace();
    }
}


public static void main(String[] args) {
    Server server=new Server(9090,5);
    server.run();
}

}

pool and cilent:
package pool;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.LinkedList;
import java.util.List;

public class pool implements Runnable{
private static List poolList=new LinkedList();
private Socket socket;
private String ip;
private int port;
public pool() {
// TODO Auto-generated constructor stub
}
public pool(String ip,int port){
this.ip=ip;
this.port=port;
}

public static void addSocket(Socket socket){
    synchronized (poolList) {//将连接池同步起来
        poolList.add(poolList.size(),socket);
        poolList.notifyAll();
    }
}

public void make(){
    try {
        socket=new Socket(ip,port);
        File file=new File("e:\\我的工号.txt");
        BufferedReader read=new BufferedReader(new InputStreamReader(new FileInputStream(file)));
        String tempStr="";
        String temp=null;
        PrintWriter pw=new PrintWriter(socket.getOutputStream());
        while((temp=read.readLine())!=null){
            tempStr+=(temp+"\r");
        }
        pw.write(tempStr);
        pw.close();
        read.close();
    } catch (Exception e) {
        e.printStackTrace();
    } 
}
public void run() {
    while(true){
        synchronized (poolList) {
            while(poolList.isEmpty()){
                try {
                    poolList.wait();
                } catch (InterruptedException e) {
                    return;
                }
            }
            Socket socket=(Socket)poolList.remove(0);               
        }
        make();
    }
}

public static void main(String[] args) {
    add();
}
public static void add(){
    for(int i=0;i<Server.maxLeng;i++){
        pool p=new pool("10.43.9.51",9090);
        new Thread(p,i+"").start();
    }
}

}
本来是希望通过Socket多线程的读文件.现在运行出现的结果没有报错.也没有任何内容输出.(注:操作步骤没有问题的.)

[quote]还有个地方。 socket=(Socket)list.remove(0);这句话是什么意思。看上去很诡异,不是删除掉list中的第一个值么,为什么还有返回值呢.哦..是不是说进入同步的这个线程要占用这个链接资源,所以要将该资源从list里面去掉啊. [/quote]
对 这个方法就是从列表删除了第一个对象,并且把这个对象返回出去。

删除的意思就是 想从连接池中去掉这个,标识已经被占用了。

欢迎采纳! 不懂的可以站内问我。

[quote]pool.addSocket(socket);[/quote]
服务器在监听了,但是你有客户端往服务端发送连接请求么?

那你的 make() 在什么情况才会调用到呢?是在你 pool 类的run方法的最后面,但你的这个 run() 方法什么时候可以走到调用 make() 的这段代码呢?走不到。

[code="java"]public static void main(String[] args) {
Server server=new Server(9090,5);
server.run();
}
} [/code]这段代码有问题,你应该这样做:

[code="java"]public static void main(String[] args) {
Server server=new Server(9090,5);
Thread threadServer=new Thread(server);
threadServer.start();
}
}[/code]
而不是直接调用run方法。

socket 编程真是一个好东西了。来学习下。

[code="java"]synchronized (poolList) {
while(poolList.isEmpty()){
try {
poolList.wait();
} catch (InterruptedException e) {
return;
} [/code]
这段话,当线程进入之后,假如遇到empty的时候这个线程wait,等于是这个线程等待在这里,然后让别的线程进入,但是假如别的线程也等着了,那么没有唤醒程序了,所以互相锁住就死锁啦。

[quote]为什么会走不到make()方法呢.哪里有问题么 [/quote]
因为你在调用 make() 之前,有这么一段
[code="java"]
synchronized (poolList) {
while(poolList.isEmpty()){
try {
poolList.wait();
} catch (InterruptedException e) {
return;
}
}
Socket socket=(Socket)poolList.remove(0);
}
[/code]
首先,其中的 [code="java"]poolList.wait();[/code]
它会被执行,然后就一直阻塞在这个地方,而后,你的服务端一直在等待一个客户端来与它进行连接(因为 accept() 是阻塞的),这样的话,服务端无法与任何一个客户端进行连接,也就无法执行[code="java"]pool.addSocket(socket); [/code],这样就相当陷入一个死循环:服务在等客户端进行连接,而且客户端在等服务端产生的 socket。

while换成if
然后后面加一个else{
this.notifyAll();
}
试试。

建议分成三个部份来写:
① 服务端
② 客户端
③ 连接池

你这个整体得改啊 从哪里说哦

[quote]各位稍等。容我在把代码规范一下。分开写。。 [/quote]
好 写好了 我在我本机改改。

[quote]while换成if
然后后面加一个else{
this.notifyAll();
} [/quote]没用的,根本不会走到这个else中去

还有,你的连接池的逻辑感觉有问题啊,一般是初始化一些连接进去,然后在empty的时候,你不光的等待,你得填充你的连接池啊,不然就一直等待了

[quote]貌似会一直等待。。。 [/quote]就是我上面说的那个死循环

[quote]这里有点不明白.启动服务端的时候调用pool的线程.是为了初始化一个连接池么,pool类中run方法,list刚开始也是没有值的,为什么不会产生死锁一直等待呢. [/quote]
因为虽然是空的,但是这个是多线程,另外的线程已经加了之后,会让把wait的线程锁改成notifyAll()的状态。

[quote]//////这里有点不明白.启动服务端的时候调用pool的线程.是为了初始化一个连接池么,pool类中run方法,list刚开始也是没有值的,为什么不会产生死锁一直等待呢. [/quote]
因为你把发送请求至服务端以连接Socket的代码
[code="java"]socket=new Socket(ip,port); [/code]
放到了 Test 类中了啊,它与你的 pool 类是没有关系的,当然就可以启动了

还有你的对象锁最好与业务有关,不要用list来当对象锁。
假如这个对象在另外地方被其他线程引用,同样会造成死锁。

等你的 Test 类中客户端与 服务端的 Socket 连通的时候,服务端就把这个 socket 通过 pool 的 addPoolObject() 添加到了 list 中,然后 list.notifyAll() 就唤醒了 pool 所在的线程,使得 list.wait(); 完成了使命,继而执行它下面的代码

[quote]没看懂。还是不明白。如果客户端有100个请求,但服务端值允许5个,该怎么样写才能使服务端请求不够用的情况下,自动创建新的链接呢。 [/quote]
你的理解其实是错的。
特别是 “自动创建新的链接呢”这句话。
这个和数据库连接不一样。你服务器接收连接 虽然可以限制个数,但是你在服务端不能创建多个连接,放在这里等你客户端用。因为像这样的socket,是需要客户端连接后才能产生的,是标识有具体ip的套接字。而数据库连接不一样,它不用标识你是哪里来的,只要给出用户名密码,所有创建的连接都是一样的,所以才可以缓存到list里面供给你使用。

[quote]上面那个是在启动服务端的时候,创建的连接池,这个时候客户端还没有链接服务端的。 [/quote]如果你只是启动了Server,当然还是没有用的啊。你必须启动:① server ② pool ③ test ,且顺序不能改变

[quote]这句话也没看懂。对象锁与业务相关是什么意思呀。怎么样才是与业务相关呢。举个例子说明一下吧。[/quote]
你现在synchronized(list)你的list是缓存连接的,是有特殊用处的,就相当于与业务有关了我是说单独Object obj=new Object();然后synchronized(obj)。

[quote]那就应该是在连接池里面创建新的链接咯。怎么创建呢。好像还得有状态的标明的[/quote]

[quote]你的意思是这里同步的应该是一个Socket对象么 [/quote]

1,也就是说你创建socket连接池这个想法从根本上就是错误的。
2,我的意思就是 Object obj=new Object();
然后synchronized(obj){
}

[quote]那也就是说服务端启动的时候,在连接池中放如了5个正在等待的链接,对么。[/quote]不是这样理解的。而应该这么理解,你的ServerSocket 不是指定了上限了么,那么你的
[code="java"]
while(true) {
socket.accept();
}
[/code]
应该最多只允许五个客户端连接它,超过五个的话,可能是让客户端等待,直到有客户端断开已知连接

[quote]哦。。好像明白了。ServerSocket的链接个数,跟连接池是不一样的。 [/quote]
肯定是不一样的,serverSocket就相当于一个服务器,你只需要一个在不停的accept就行了。

就比如tomcat,它底层其实就是启动一个serverSocket在不断的等待。

但是客户端socket可以有无数个,而且都是有ip地址的,所以你不可能最开始都生成一大堆客户端socket,然后给客户用,不能这样。

好啦 欢迎lz采纳我的意见,谢谢。

[quote]哦。。好像明白了。ServerSocket的链接个数,跟连接池是不一样的。 [/quote]
嗯,服务端不会自动给你创建空闲连接等你来用的,而是连上一个客户端就开启一个连接。
如果还有疑问,我们继续交流,希望对你有用,同时欢迎兄弟采纳啊。

[quote]socket=(Socket)list.remove(0)[/quote]
你可以去看一下 list 的 remove() 方法的说明,它的确是把一个元素从这个 list 移除了,但它同时还把这个移除的元素给返回了

[quote]是不是说进入同步的这个线程要占用这个链接资源,所以要将该资源从list里面去掉啊[/quote]并不是啊,如果说你下面不调用 readFie()的话,你就没有必要从 list 里面移除socket了

[quote]我把这句注释了。好像也不影响。这句话到底是什么作用呢。
我查了下。remove在这里是每删除一个元素.其他的元素索引都会向前移动.
在这里为什么要这样写呢. [/quote]

其实不这么写也可以,直接删除,只是说有的被删除元素假如里面的属性对你还有用的话,就可以继续引用啊。

兄弟,我有点凌乱了,你的 pool 类中的 addPoolObject() 在哪被调用的呢?