socket 长连接 心跳包

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


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();
}
}
}
}
}

...全文
810 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
rorom 2012-01-07
  • 打赏
  • 举报
回复
谢谢楼上的朋友,我先试试
MiceRice 2012-01-06
  • 打赏
  • 举报
回复
服务器端代码如何?另外,你在自己同一台机器上进行心跳测试的话,是否正常?
rorom 2012-01-06
  • 打赏
  • 举报
回复
救命......啊......
rorom 2012-01-06
  • 打赏
  • 举报
回复
服务器是开着的,现在的问题是每次服务器只能返回第一次心跳的结果,然后坐等超时,后面的发送都被无视了,不了解底层,完全不知道什么状态啊
oO临时工Oo 2012-01-06
  • 打赏
  • 举报
回复
检查服务器还开着不啊
rorom 2012-01-06
  • 打赏
  • 举报
回复
高手请进!!!!
MiceRice 2012-01-06
  • 打赏
  • 举报
回复
这是NIO写的测试客户端,供参考:


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);
}
}
MiceRice 2012-01-06
  • 打赏
  • 举报
回复
楼主,你的程序连接我用NIO写的EchoServer正常,没法帮到你了。问题估计还是在:传输内容跟服务器端认可的协议机制不一致。

服务端:

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);
}
}
lxbccsu 2012-01-06
  • 打赏
  • 举报
回复
其实错误在这里:
if (br.read(cbuf) == -1){
break;
}
这里不会返回-1,除非服务器输出一个-1的信息;
所以当你第一次读完信息后,下面判断一定会跳出,
if(b.length >= 1024){
break ;
}
后面再进入if (br.read(cbuf) == -1)
如果服务器没有任何的信息返回,这个read方法会无限的等待,直到服务器有信息返回;
Steve 2012-01-06
  • 打赏
  • 举报
回复
是不是你们公司的firewall给自动切断了。
gukuitian 2012-01-06
  • 打赏
  • 举报
回复
这要看服务器的实现了,要是他在返回数据后关闭了outPutStream,你这边的socket自然就断了。

要是不能改服务器的话,就只能重建socket了
MiceRice 2012-01-06
  • 打赏
  • 举报
回复
请对方给你们一个客户端的“参考实现”,既然他们说别人都是没问题的。

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

另外,心跳检测这个东西,建议全部修改为异步通讯模型,同步通讯模型不太合适。
rorom 2012-01-06
  • 打赏
  • 举报
回复
就是不知道服务端的代码是怎么写的,别人不给,只告诉我们用心跳包长连接,因为短连接还算是正常,所以就没有在本地架设服务端,就算开了,也不能保证跟服务端是一样的,所以就直接拿的服务端测试了,服务端的人说,他们接给别人是没问题的.

67,513

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧