读书人

socket 长连接 心跳包,该怎么处理

发布时间: 2012-04-19 14:36:43 作者: rapoo

socket 长连接 心跳包
我们现在需要做一个socket的客户端,与别的公司提供的服务端保持长连接,说明要用心跳包进行长连接,但是用下面的代码好像只能进行第一次正常的连接,之后就会出现被服务端超时强制中断,求高人指点迷津:

Java code
package com.haozhong.recharge.socket;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.PrintWriter;import java.net.InetAddress;import java.net.InetSocketAddress;import java.net.Socket;import java.nio.channels.SocketChannel;import java.util.Random;import com.haozhong.recharge.constants.Constants;// 取参例子 0030    lotterygetcs    101531    7    lt    lot    public class ClientTest {    public static void main(String[] args){        SocketChannel channel = null ;        try {            channel = SocketChannel.open();            InetAddress add = InetAddress.getByName(Constants.LOTTERY_URL) ;            InetSocketAddress isa = new InetSocketAddress(add,Constants.LOTTERY_PORT);            channel.connect(isa) ;            System.out.println("与服务器建立连接") ;            /**             *              */            Socket socket = channel.socket() ;            while(true){                InputStream is = socket.getInputStream();                BufferedReader br = new BufferedReader(new InputStreamReader(is));                OutputStream os = socket.getOutputStream();                PrintWriter pw = new PrintWriter(os,true);                String msg = null ;                char[] cbuf = new char[1024];                byte[] b = null ;                StringBuilder sb = new StringBuilder() ;                String str = "" ;                msg = "0013\ttest\t"+new Random().nextInt(1000000)+"" ;                System.out.println(msg) ;                pw.println(msg);                while(true){                    if (br.read(cbuf) == -1){                        break;                    }                    sb.append(new String(cbuf)) ;                    str = sb.toString() ;                    b = str.getBytes() ;                    if(b.length >= 1024){                        break ;                    }                }                System.out.println(str) ;                Thread.sleep(5*1000) ;                /*if(result != null && result != ""){                    msg ="0032\tlotterygetcs\t33920001\t7\tlj\tlot\t"  ;                    pw.println(msg);                    System.out.println(br.readLine()) ;                    msg = "0100\tlotterygetbulletin\t858939\t60\t33920001\tlot\t2009001\t" ;                    pw.println(msg) ;                    System.out.println(br.readLine()) ;                }*/            }            /*while((msg = r.readLine())!= null){                pw.println(msg);                System.out.println(br.readLine());                if(msg.equals("bye")){                    break ;                }            }*/        } catch (IOException e) {            e.printStackTrace();        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }finally{            if(channel != null){                try {                    channel.close();                } catch (IOException e) {                    // TODO Auto-generated catch block                    e.printStackTrace();                }            }        }    }}


[解决办法]
请对方给你们一个客户端的“参考实现”,既然他们说别人都是没问题的。

主要怀疑的是所传输内容跟服务器端认可的协议机制不同。

另外,心跳检测这个东西,建议全部修改为异步通讯模型,同步通讯模型不太合适。
[解决办法]
这要看服务器的实现了,要是他在返回数据后关闭了outPutStream,你这边的socket自然就断了。

要是不能改服务器的话,就只能重建socket了
------解决方案--------------------


其实错误在这里:
if (br.read(cbuf) == -1){
break;
}
这里不会返回-1,除非服务器输出一个-1的信息;
所以当你第一次读完信息后,下面判断一定会跳出,
if(b.length >= 1024){
break ;
}
后面再进入if (br.read(cbuf) == -1)
如果服务器没有任何的信息返回,这个read方法会无限的等待,直到服务器有信息返回;

[解决办法]
楼主,你的程序连接我用NIO写的EchoServer正常,没法帮到你了。问题估计还是在:传输内容跟服务器端认可的协议机制不一致。

服务端:

Java code
public class EchoServer {    private static int SOCKET_NUM = 55555;    private static DateFormat dateFormatter = new SimpleDateFormat("yyyyMMdd HH:mm:ss");        /**     * @param args     */    public static void main(String[] args) {        new EchoServer().start();    }    public void start() {        try {            Selector selector = bindServer();  // 绑定服务端口,并定义一个事件选择器对象记录套接字通道的事件                        /* 通过此循环来遍例事件 */            while (true) {                log("Waiting events.");                int n = selector.select(); // 查询事件如果一个事件都没有,这里就会阻塞                log("Got events: " + n);                                ByteBuffer echoBuffer = ByteBuffer.allocate(50); // 定义一个byte缓冲区来存储收发的数据                /* 循环遍例所有产生的事件 */                for (SelectionKey key : selector.selectedKeys()) {                    SocketChannel sc;                    selector.selectedKeys().remove(key);  // 将本此事件从迭带器中删除                                        /* 如果产生的事件为接受客户端连接(当有客户端连接服务器的时候产生) */                    if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {                                                ServerSocketChannel subssc = (ServerSocketChannel) key.channel(); // 定义一个服务器socket通道                                                sc = subssc.accept(); // 将临时socket对象实例化为接收到的客户端的socket                                                sc.configureBlocking(false); // 将客户端的socket设置为异步                                                sc.register(selector, SelectionKey.OP_READ); // 将客户端的socket的读取事件注册到事件选择器中                                               System.out.println("Got new client:" + sc);                    }                    /* 如果产生的事件为读取数据(当已连接的客户端向服务器发送数据的时候产生) */                    else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {                                                sc = (SocketChannel) key.channel(); // 临时socket对象实例化为产生本事件的socket                                                ByteArrayOutputStream bos = new ByteArrayOutputStream(); // 定义一个用于存储byte数据的流对象,存储全部信息                                                echoBuffer.clear(); // 先将客户端的数据清空                                                try {                            // 循环读取所有客户端数据到byte缓冲区中,当有数据的时候read函数返回数据长度                            // NIO会自动的将缓冲区一次容纳不下的自动分段                            int readInt = 0; // 为读取到数据的长度                            while ((readInt = sc.read(echoBuffer)) > 0) {                                // 如果获得数据长度比缓冲区大小小的话                                if (readInt < echoBuffer.capacity()) {                                                                        byte[] readByte = new byte[readInt]; // 建立一个临时byte数组,将齐长度设为获取的数据的长度                                    // 循环向此临时数组中添加数据                                    for (int i = 0; i < readInt; i++) {                                        readByte[i] = echoBuffer.get(i);                                    }                                                                        bos.write(readByte); // 将此数据存入byte流中                                }                                // 否则就是获得数据长度等于缓冲区大小                                else {                                                                        bos.write(echoBuffer.array()); // 将读取到的数据写入到byte流对象中                                }                            }                            // 当循环结束时byte流中已经存储了客户端发送的所有byte数据                            log("Recive msg: " + new String(bos.toByteArray()));                        } catch (Exception e) {                                                        e.printStackTrace(); // 当客户端在读取数据操作执行之前断开连接会产生异常信息                                                        key.cancel(); // 将本socket的事件在选择器中删除                            break;                        }                                                writeBack(sc, bos.toByteArray()); // 向客户端写入收到的数据                    }                }            }        } catch (Exception e) {            e.printStackTrace();        }    }    /**     * 绑定服务端口,初始化整个服务     * @throws IOException     */    private Selector bindServer() throws IOException {        log("Start binding server socket:" + SOCKET_NUM);                Selector selector = Selector.open(); // 定义一个事件选择器对象记录套接字通道的事件        ServerSocketChannel ssc = ServerSocketChannel.open(); // 定义一个异步服务器socket对象        ssc.configureBlocking(false);// 将此socket对象设置为异步        ServerSocket ss = ssc.socket(); // 定义服务器socket对象-用来指定异步socket的监听端口等信息        InetSocketAddress address = new InetSocketAddress(SOCKET_NUM); // 定义存放监听端口的对象        ss.bind(address); // 将服务器与这个端口绑定        ssc.register(selector, SelectionKey.OP_ACCEPT); // 将异步的服务器socket对象的接受客户端连接事件注册到selector对象内        log("Binded socket at:" + SOCKET_NUM);                return selector;    }        private boolean writeBack(SocketChannel sc, byte[] b) {        ByteBuffer echoBuffer = ByteBuffer.allocate(b.length); // 建立这个byte对象的ByteBuffer        echoBuffer.put(b); // 将数据存入                 echoBuffer.flip(); // 将缓冲区复位以便于进行其他读写操作        try {            // 向客户端写入数据,数据为接受到数据            sc.write(echoBuffer);        } catch (IOException e) {            e.printStackTrace();            return false;        }        System.out.println("Msg echo back: " + new String(echoBuffer.array()));        return true;    }    private static void log(Object msg) {        System.out.println("SERVER [" + dateFormatter.format(new Date()) + "]: " + msg);    }} 


[解决办法]
这是NIO写的测试客户端,供参考:

Java code
public class EchoTestClient {    private static int SOCKET_NUM = 55555;    private static DateFormat dateFormatter = new SimpleDateFormat("yyyyMMdd HH:mm:ss");    /**     * @param args     */    public static void main(String[] args) {        new EchoTestClient().start();    }    private void start() {        SocketChannel client = null;        try {            Selector selector = Selector.open(); // 定义一个记录套接字通道事件的对象            client = connect(selector);            String a = "ABCD"; // 要发送的数据            while (true) {                ByteBuffer sendbuffer = ByteBuffer.allocate(40); // 定义用来存储发送数据的byte缓冲区                ByteBuffer readBuffer = ByteBuffer.allocate(40); // 定义用于接收服务器返回的数据的缓冲区                sendbuffer.put(a.getBytes()); // 将数据put进缓冲区                sendbuffer.flip(); // 将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位                client.write(sendbuffer); // 向服务器发送数据                log("Send to server: " + new String(sendbuffer.array()));                // 利用循环来读取服务器发回的数据                {                    // 如果客户端连接没有打开就退出循环                    if (!client.isOpen())                        break;                    // 此方法为查询是否有事件发生如果没有就阻塞,有的话返回事件数量                    int shijian = selector.select(1000);                    // 如果没有事件返回循环                    if (shijian == 0) {                        continue;                    }                    // 定义一个临时的客户端socket对象                    SocketChannel sc;                    // 遍例所有的事件                    for (SelectionKey key : selector.selectedKeys()) {                        // 删除本次事件                        selector.selectedKeys().remove(key);                        // 如果本事件的类型为read时,表示服务器向本客户端发送了数据                        if (key.isReadable()) {                            // 将临时客户端对象实例为本事件的socket对象                            sc = (SocketChannel) key.channel();                            // 定义一个用于存储所有服务器发送过来的数据                            ByteArrayOutputStream bos = new ByteArrayOutputStream();                            // 将缓冲区清空以备下次读取                            readBuffer.clear();                            // 此循环从本事件的客户端对象读取服务器发送来的数据到缓冲区中                            while (sc.read(readBuffer) > 0) {                                // 将本次读取的数据存到byte流中                                bos.write(readBuffer.array());                                // 将缓冲区清空以备下次读取                                readBuffer.clear();                            }                            // 如果byte流中存有数据                            if (bos.size() > 0) {                                // 建立一个普通字节数组存取缓冲区的数据                                byte[] b = bos.toByteArray();                                log("Recive from server: " + new String(b));                            }                        }                    }                }                Thread.sleep(2000);            }        } catch (Exception e) {            e.printStackTrace();        } finally {            // 关闭客户端连接,此时服务器在read读取客户端信息的时候会返回-1            if (client != null) {                try {                    client.close();                } catch (IOException e) {                }                log("Connection closed!");            }        }    }    private SocketChannel connect(Selector selector) throws IOException {        SocketAddress address = new InetSocketAddress("localhost", SOCKET_NUM); // 定义一个服务器地址的对象        SocketChannel client = SocketChannel.open(address); // 定义异步客户端        client.configureBlocking(false); // 将客户端设定为异步        client.register(selector, SelectionKey.OP_READ); // 在轮讯对象中注册此客户端的读取事件(就是当服务器向此客户端发送数据的时候)        return client;    }    private static void log(Object msg) {        System.out.println("CLIENT [" + dateFormatter.format(new Date()) + "]: " + msg);    }} 

读书人网 >J2EE开发

热点推荐