Java:套接字消息丢失
我们正在用Java(1.6)开发一个服务器应用程序,它是一个事务服务器,通过TCP套接字侦听连接。每个新连接创建一个新线程,该线程在连接关闭之前保持活动状态。每个客户机都会将事务发送到要处理的服务器,然后将响应发送回客户机 这个很好用。当我们想通过同一个套接字发送许多异步事务(或消息)时,问题就出现了。我编写了一个小应用程序,它以10毫秒的间隔发送1000个事务。应用程序是异步的,所以发送消息,响应在中间。 这是来自处理输入消息并将其发送到另一个要处理的组件(此组件具有线程池)的部分的代码:Java:套接字消息丢失,java,sockets,Java,Sockets,我们正在用Java(1.6)开发一个服务器应用程序,它是一个事务服务器,通过TCP套接字侦听连接。每个新连接创建一个新线程,该线程在连接关闭之前保持活动状态。每个客户机都会将事务发送到要处理的服务器,然后将响应发送回客户机 这个很好用。当我们想通过同一个套接字发送许多异步事务(或消息)时,问题就出现了。我编写了一个小应用程序,它以10毫秒的间隔发送1000个事务。应用程序是异步的,所以发送消息,响应在中间。 这是来自处理输入消息并将其发送到另一个要处理的组件(此组件具有线程池)的部分的代码: p
public void run(){
...
...
socketBuf=新的BufferedInputStream(输入);
bas=新的ByteArrayOutputStream();
而((字节读取=socketBuf.read(缓冲区))!=-1){
如果(字节数_读取<0){
错误(“尝试从套接字读取,read()返回<0,正在关闭套接字”);
返回;
}
写入(缓冲区,0,字节\读取);
打破
}
如果(字节读取>=0){
paos.flush();
字节数据[]=bas.toByteArray();
如果(data.length>0){
GWTranData tData=posMessage.decode(数据,false);
如果(tData.getMessageType()>0){
//发送至在线前管理器进行处理
PreOnlineJob newJob=新的PreOnlineJob(tData);
newJob.addJobStatusListener(此);
GWServer.getPreOnlineInstance().addJob(newJob);
}
}
}
否则{
clientSocket.close();
打破
}
}虽然(正确);
}
在短时间内发送许多事务时,我们面临的问题是一些消息丢失,无法到达服务器。通过深入分析,我们发现当消息发送速度过快时,缓冲区中有多条消息,因此data[]有两条或多条消息,但只执行一条。发送的消息大小为200字节,因此512的缓冲区就足够了
我实现套接字读取的方式有问题吗?有更好的办法吗
谢谢大家。问题在于如何使用从套接字读取的字节。你的假设是每次阅读都会收到一条“消息”。这种假设是错误的——TCP不知道应用程序消息的边界,但会给您一个字节流,因此您可以一次获得多条消息,或者一条消息的一部分,或者两者兼而有之 您必须缓冲接收到的流中未处理的部分,检查是否收到完整的消息,在收到消息之前再阅读一些内容,处理消息,然后在循环中继续 编辑0: 有几种方法可以在TCP之上设计应用程序级协议:
- 固定长度消息(简单)
- 分隔消息(需要明确的字节序列来指定消息的结束/开始,如FIX中的
,或HTTP中的SOH
)\r\n
- 正如@Thomas在评论中所建议的,以长度为前缀的消息
- “自我描述”的消息——比如s表达式之类的,需要解析
- 也许其他人
public void run() {
...
...
socketBuf = new BufferedInputStream(input);
baos = new ByteArrayOutputStream();
while ((bytes_read = socketBuf.read(buffer)) != -1) {
if (bytes_read < 0) {
log.error("Tried to read from socket, read() returned < 0, Closing socket.");
return;
}
baos.write(buffer, 0, bytes_read);
break;
}
if (bytes_read >= 0) {
baos.flush();
byte data[] = baos.toByteArray();
if (data.length > 0) {
GWTranData tData = posMessage.decode(data, false);
if (tData.getMessageType() > 0) {
// Send to the Pre-Online Manager to be processed
PreOnlineJob newJob = new PreOnlineJob(tData);
newJob.addJobStatusListener(this);
GWServer.getPreOnlineInstance().addJob(newJob);
}
}
}
else {
clientSocket.close();
break;
}
} while(true);
}