各位师兄好:
   兄弟做好了一个网关。原理如下:网关程序需要去连接一堆的远程机子,并且是保持长连接,对每一个SOCKET进行相应的处理。远程机子的HOST和PORT都保存在一个ArrayList类型的集合中,我从中依次读取出每一个HOST和PORT,读出一个后,我先判断目前SOCKET连接组中是否有该SOCKET,如果没有则新建该SOCKET,同时启动一个线程来处理相应的事务。在这个线程中,当通讯出错(可能是SOCKET断掉了)出现异常时,将连接组中的该SOCKET删除。程序如下:
public class AmClient {
    private ThreadGroup threadGroup; ///线程组
    private Map socketGroup = new HashMap(); //SOCKET连接组
ArrayList ipArray;////HOST与PORT的集合
    String host;
    int port;
    public AmClient() {
        threadGroup = new ThreadGroup(AmClient.class.getName());
    }
    public static ArrayList readXml() {
........
       //////这里给ipArray这个集合附值,写入N个HOST和PORT
    }
    public static void main(String[] args) {
        AmClient amClient = new AmClient();
        ipList = readXml();
        while (true) {
            try {
                amClient.socketRequest(ipList);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    public void socketRequest(ArrayList ipArray) {
        BufferedReader in;
        PrintWriter out;
        Socket socket;
        for (Iterator it = ipArray.iterator(); it.hasNext(); ) {
            Map ipMap = (Map) it.next(); ///一行是一个Map
            ////取出参数
            host = (String) ipMap.get("host");
            port = Integer.parseInt((String) ipMap.get("port"));
            String key = host + Integer.toString(port);
            try {
                if (socketGroup.get(key) == null) {
                /////当连接没有建立时或连接断开了,新建SOCKET后再启动线程
                System.out.println("controller start...");
socket = new Socket(host, port);
                    AmConnection k = new AmConnection(socket, ipMap);
                    socketGroup.put(key, socket); ///放入连接组中
                }            } catch (Exception e) {
                ////若出现断路情况,将socket从连接组中删除
                if (socketGroup.get(key) != null) {
                    ///若连接组中已有该连接
                    socketGroup.remove(key); ////从连接组中删除
                }
                System.out.println("socket断路了:" + e);
            }
        }
    }
     //处理每个SOCKET的线程
    public class AmConnection extends Thread {
        Socket client; //客户通信套接字
        Map ipMap;
        private String amHost,key;
        private int amPort;
        public AmConnection(Socket client, Map ipMap) {
            super(threadGroup,
                  client.getLocalAddress() + Integer.toString(client.getPort()));
            this.client = client;
            this.ipMap = ipMap;
            amHost = (String) ipMap.get("host");
            amPort = Integer.parseInt((String) ipMap.get("port"));
            key = amHost + Integer.toString(amPort);
            start();
        }
        public void run() {
            try {
........
                ////作相应的处理
            } catch (Exception e) {
                ////若出现断路情况,将socket从连接组中删除
                if (socketGroup.get(key) != null) {
                    ///若连接组中已有该连接
                    socketGroup.remove(key); ////从连接组中删除
                }
                System.out.println("socket断路了:" + e);
                interrupt(); //中断该线程
            }
        }    }
}
现在的问题是:我程序运行后,功能倒是可以实现,可是CPU的占用率达到了100%。后来我在main方法中加了一句
            try {
                amClient.socketRequest(ipList);
                Thread.sleep(5000); //就加了这句
            } 
CPU就正常了。这是为什么呢?望知情者详细解释,谢谢。

解决方案 »

  1.   

    Thread.sleep(5000)让线程不一直占用CPU,这样CPU自然就下来。
      

  2.   

    TO 回复人: lyzkkzong(苯苯) 
      谢谢回复
    TO 回复人: cuij7718(沸腾的音乐 http://sunfruit.blogchina.com) ( )
      那应该如何理解多线程呢?象这样的情况,同时要连接多个远程端,而且又必须保证每个SOCKET的实时通讯。不用线程来处理,怎么做才能保持每个SOCKET的长连接???
    TO 回复人: gemouzhi(gemouzhi)
      那正确的方向是什么,请详细说明,谢谢。
      

  3.   

    soory, 刚才给别人化妆去了, 没及时回你的帖子.1,你的动机必须再和我说一下, 就是主动方与被动方? 还有业务逻辑的处理所需要的时间?2,照顾一群小朋友, 不应该去阁5秒钟,就去问每一个小朋友,谁要上厕所?哪个小朋友要上厕所就举手好了, 所以小朋友是主动方.3,为什么要保持长连接? 这显然是不好的.要不就不用出现JMS的topic了.4,你把你的需求抽象一下, 贴出来, 也许我能帮你, 我也是初学, 水平有限, 希望不会误导你.
      

  4.   

    TO  gemouzhi(gemouzhi) 
      谢谢你的回复,我把我网关的功能需求说明一下:
    1、首先有多个远程机子。
    2、网关这边要先发送一条请求信息去连接远程机子。远程机子收到请求信息后,将一条远程机子上的消息回送给网关。网关收到后,进行相应的处理后再发送请求信息给远程机子。如此循环,即网关发一条请求,远程才会回送一条消息。
    3、若有发现连接断开的情况,将进行重连。
    基本需求就是这样,我也不想保持长连接,可是如果不保持的话,那么如何保证与每个远程机子的实时通讯?难道每次都要new Socket一下,处理一条消息后就close掉。连接建立起来后,双方的通讯频率是很高的,这也是客户要求保持长连接的原因。
    所以我的程序就是考虑把存放远程HOST和PORT的ipArray遍历一下,每取出一条HOST和PORT,就查一下是否有在连接组socketGroup中有该SOCKET了,如果没有,就 new Socket(host, port),同时启用一个线程来处理这个SOCKET的实时通讯的事务。如果连接组中已存在这个SOCKET,那就不管了,因为这个SOCKET的线程已经在启动运行了。
    程序在第一次运行的时候,肯定所有的SOCKET都要新建的。然后,只有在这个SOCKET的通讯出现错误了(基本上就是SOCKET异常断开了),才会新建连接的。
    至于主方法中
            while (true) {
                try {
                    amClient.socketRequest(ipList);
                    Thread.sleep(5000); 
                } catch (Exception e) {
                    e.printStackTrace();
                }}
    amClient.socketRequest(ipList);这句我也觉得放在while (true)怪怪的,正如你说的不应该去阁5秒钟,就去问每一个小朋友。可是我不放在while (true)里,那么程序一运行起来后,CPU就占用到100%,而且放在while里面的还有一个好处就是,当发现SOCKET断开后,每隔5秒会一次重连的尝试,否则就会一直进行重连操作至到连接成功为止,这样比较占资源吧。
    望师兄指教,谢谢。
      

  5.   

    嗯,第一个问题就象你说的,所谓的异常就是
    远程计算机的当机 或 网络异常 或 硬件上的异常 或 人为结束掉进程
    至于第二个问题:我的网关所放的位置很有可能是不固定的。如果是固定的,我曾经考虑过将网关即当客户端又当服务端,即一方面它向远程机子不停地发请求,一方面它开放一个端口来监听远程机子发过来的信息(并做相应处理)。这里需要远程的机子在收到请求后,不是直接将信息应答,而是用一个SOCKET去连接我的网关(我的IP和开放的端口),将信息传给网关。
    这种方式已经被客户拒绝了,他们不允许这么做。所以我想我的网关位置固定与否就没多大关系了啦。
    谢谢指教。另:师兄是否有QQ或MSN可以交流?
      

  6.   

    如果对于消息的安全要求不高,可以尝试UDP连接,这样不用保持长连接还有,至于为什么CPU居高不下的原因 可能使你的线程里面的while里面没有sleep 导致的如果一定要使用socket 那么建议使用NIO的socket连接,可以实现非阻塞的连接,性能很好
      

  7.   

    恩,看见你的短消息了,我加了你的MSN了。你已经说出了我想说的(但竟然“这种方式已经被客户拒绝了,他们不允许这么做”)我也知道你肯定也了解我这么问的意图。因为用那种方式主动转为被动,才能更好的管理和应用技术。可是被客户拒绝了,我感觉很奇怪。(呵呵,说实话,这种方式被拒绝是很伤心的,我想说的一些解决方案也是基于被客户拒绝的这种方式的)这样的话,在MSN上只好讨论一下关于,单线程和多线程解耦了。以前在JMS的回调时候我就想过你这种发散的servers问题,竟然还真有这样的应用。(也许你远程上的程式并不是java写的)anyway,MSN吧。我也是初学,希望能帮的上^_^.
      

  8.   

    如果一定要使用socket 那么建议使用NIO的socket连接,可以实现非阻塞的连接,性能很好???
    怎么用?能不能说的详细些???
      

  9.   

    对于NIO的socket的连接方式,我给你一个地址你看看吧,说的很好
    http://www-128.ibm.com/developerworks/cn/java/j-javaio/
    还有相关的信息,在网上搜搜吧
      

  10.   

    长连接可以
    不要用线程
    连接是资源,应按资源池的机制实现
    管理几个连接池,每个连接池对应不同的远程机子
    每个连接池有ip、port、初始连接数,最大连接数等参数
    ResourcePoolManager负责参数读取、所有资源池初始化、销毁、关闭、重启、取空闲连接、返还连接等等
    ResourcePool负责单个池的维护,ResourcePool中包含多个Resourceable实例
    这样你可以管理监控每个池甚至每个连接的状态
      

  11.   

    TO cuij7718(沸腾的音乐 http://sunfruit.blogchina.com)
      NIO的socket有没有线程处理的例子。
      

  12.   

    NIO的socket有没有线程处理的例子。完整的一个例子,http://sunfruit.blogchina.com中的例子基本不能用啊。