Java:套接字消息丢失

时间:2012-08-16 14:46:13

标签: java sockets

我们正在开发一个Java(1.6)服务器应用程序,它是一个事务服务器,通过TCP套接字监听连接。每个新连接都会创建一个新线程,该线程将保持活动状态,直到连接关闭每个客户端都会将事务发送到将要处理的服务器,然后将响应发送回客户端。

这很好用。当我们想要通过同一个套接字发送许多异步事务(或消息)时,就会出现问题。我写了一个小应用程序,在每次事务之间发送1000个事务,间隔为10毫秒。该应用程序是异步的,因此发送消息并且响应在中间。

这是来自处理incomming消息并将它们发送到另一个要处理的组件的部分的代码(该组件有一个线程池):

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

我们在短时间内发送许多交易时遇到的问题是某些消息丢失而无法到达服务器。进行深入分析,我们发现当消息发送得太快时,缓冲区中有多条消息,因此data []有两条或更多条消息,但只会执行一条消息。发送的消息大小为200字节,因此512的缓冲区已足够。

我实现套接字读取的方式有问题吗?还有更好的方法吗?

谢谢你们。

2 个答案:

答案 0 :(得分:6)

问题在于您使用从套接字读取的字节数的方式。您的假设是每次阅读都会收到一条“消息”。这个假设是错误的 - TCP不知道您的应用程序消息边界,但是为您提供了一个 stream 字节,因此您可以一次获得多条消息,或者消息的一部分,或两者兼而有之。 / p>

您必须缓冲接收到的流的未处理部分,检查是否有完整的消息,阅读更多信息,处理消息,然后继续循环。

编辑0:

有几种方法可以在TCP上设计应用程序级协议

  • 固定长度消息(简单),
  • 分隔消息(需要显式字节序列来指定消息的结束/开始,如FIX中的SOH或HTTP中的\r\n),
  • @Thomas在评论中建议的长度为前缀的消息,
  • “自描述”消息 - 如s表达式或不需要解析的
  • 也许是其他人。

答案 1 :(得分:1)

您需要调整代码以在消息到达时处理消息。目前,您正在将从连接到EOS的整个流累积到一个巨大的字节数组中,然后将其处理为好像只包含一个消息。从“丢失”消息的角度来看,这不仅是错误的(正是你正在做失败),但它也极其浪费时间和空间。在处理第一条消息之前,您无需等待EOS。您需要弄清楚如何读取流,直到您只有一条消息,处理它,然后重复处理下一条消息,终止于EOS。