Java 从TCP套接字读取数据的最有效方法

Java 从TCP套接字读取数据的最有效方法,java,sockets,java-io,Java,Sockets,Java Io,我有一个到服务器的TCP连接,由套接字和流实现。在会话期间,服务器可以发送任意数量的消息-我必须读取并处理所有消息 我创建了一个线程,它以无限周期检查和读取数据: in=socket.getInputStream(); ByteArrayOutputStream baos=null; 字节[]buf=新字节[4096]; 而(!isInterrupted()){ bas=新的ByteArrayOutputStream(); 对于(int s;(s=in.read(buf))!=-1;){ 写入

我有一个到服务器的TCP连接,由套接字和流实现。在会话期间,服务器可以发送任意数量的消息-我必须读取并处理所有消息

我创建了一个线程,它以无限周期检查和读取数据:

in=socket.getInputStream();
ByteArrayOutputStream baos=null;
字节[]buf=新字节[4096];
而(!isInterrupted()){
bas=新的ByteArrayOutputStream();
对于(int s;(s=in.read(buf))!=-1;){
写入(buf,0,s);

如果(in.available()TCP不是面向消息的,它是面向流的。这意味着如果发送两条消息AA和BB,则很可能在不同的情况下读取值AABB、A B、A ABB、AAB、AA BB(其中空格表示不同的读取尝试)

您需要自己处理消息大小或消息分隔符,因此不再需要In .Audiable()。此外,您的代码至少将3次复制到不同的缓冲区,并考虑在Socket上使用BuffReDePixStand。GETInPuttScript()。< /P> < P>移除可用的()调用。<代码>
不是对流结束的有效检查,它在文档中也这样说。而且它永远不会返回负值。另外,
readChunk()
方法是应该进行读取的方法。TCP中也没有消息,因此使用
available()
或任何其他技术来识别它们是无效的

编辑

在另一种情况下,你说你有一个计数前缀。使用它。用
DataInputStream.readInt()
读取它,分配一个该大小的
byte[]
数组,并用
DataInputStream.readFully()填充它:


根据你所说的信息,这是一种方法:

in = socket.getInputStream();
byte[] buff = new byte[4096];
int packLen=0;
int ret=0;
while(!isInterrupted()) {
    int offset=0;
    int bLeft=4;
    // 99% of the times the read will return 4 bytes, 
    // but just in case, put it in a loop.
    while (bLeft > 0) {
        ret = in.read(buff, offset, bLeft);
        if (ret > 0) {
            bLeft-=ret;
            offset+=ret;
        }
        else if (ret == 0) {
            // socket has been closed
        }
        else {
            // soket has an error
        }

    }
    // convert the 4 bytes to an int, depends on the way it's was sent
    // this method is used frecuently
    packLen = (int)((buff[0] & 0xff) << 24) |
                 (int)((buff[1] & 0xff) << 16) |
                 (int)((buff[2] & 0xff) << 8) |
                 (int)(buff[3] & 0xff);

    // if the 4 bytes of the CRC32 is not included in the length, 
    // increment the length
    packLen+=4;
    offset=4;
                if (packLen > 4092)
                {
                   // packet is too big, ignore it or do something else
                   packLen=4092;
                }
    bLeft=packLen;
    // Noew loop until the whole mesage has been read
    while (bLeft > 0) {
        ret = in.read(buff, offset, bLeft);
        if (ret > 0) {
            bLeft-=ret;
            offset+=ret;
        }
        else if (ret == 0) {
            // socket has been closed
        }
        else {
            // soket has an error
        }
    }
    // the readChunk function must be change
    // Need to pass the length of the message. 
    // Is not the buff.length anymore 
    readChunk(buff, packLen+4 /* +4 for the length of the message*/);
}
in=socket.getInputStream();
字节[]buff=新字节[4096];
int-packLen=0;
int-ret=0;
而(!isInterrupted()){
整数偏移=0;
int-bLeft=4;
//99%的读取次数将返回4个字节,
//但以防万一,把它放在一个循环中。
while(bLeft>0){
ret=英寸读取(增益、偏移、bLeft);
如果(ret>0){
bLeft-=ret;
偏移量+=ret;
}
else if(ret==0){
//插座已关闭
}
否则{
//索凯特有个错误
}
}
//根据发送方式,将4个字节转换为int
//这种方法经常使用
packLen=(int)((buff[0]&0xff)0){
bLeft-=ret;
偏移量+=ret;
}
else if(ret==0){
//插座已关闭
}
否则{
//索凯特有个错误
}
}
//必须更改readChunk函数
//需要传递消息的长度。
//不再是buff.length了
readChunk(buff,packLen+4/*+4表示消息的长度*/);
}
如果您需要Java CRC32类,我可以提供给您,它符合PKZIP和以太网标准

编辑: 注:
如果数据包长度大于4096,此方法将不起作用。

@bvitaliyg若要添加到此答案,如果效率至关重要,请尝试使用java nio。请参阅。总消息长度在前四个字节中传输。在读取消息时是否可以使用它?当然。首先读取消息长度,分配该大小的缓冲区,读取整个mes如果需要并行执行,请使用阻塞I/O和线程。如果您真的希望它具有高吞吐量,请使用现成的消息传递系统,它们很难正确执行。此外-您的目标吞吐量是多少?如果没有
可用()
读取周期(
read()
)永不结束,它返回零。虽然嗅探器显示消息的这一端。我重复。
available()
不是检测消息结尾的有效技术。在不知道您接收的消息类型的情况下,很难给出答案。它是文本?、二进制数据?还是固定长度?您是否也对服务器进行编程?大多数情况下都需要协议。@ja_mesa它是二进制数据。前四个字节用于写入消息长度,后四个字节用于写入消息长度CRC32校验和的字节。
DataInputStream.readInt()
readFully()
已经完成了所有这一切。
int len = din.readInt();
byte[] message = new byte[len];
din.readFully(message);
in = socket.getInputStream();
byte[] buff = new byte[4096];
int packLen=0;
int ret=0;
while(!isInterrupted()) {
    int offset=0;
    int bLeft=4;
    // 99% of the times the read will return 4 bytes, 
    // but just in case, put it in a loop.
    while (bLeft > 0) {
        ret = in.read(buff, offset, bLeft);
        if (ret > 0) {
            bLeft-=ret;
            offset+=ret;
        }
        else if (ret == 0) {
            // socket has been closed
        }
        else {
            // soket has an error
        }

    }
    // convert the 4 bytes to an int, depends on the way it's was sent
    // this method is used frecuently
    packLen = (int)((buff[0] & 0xff) << 24) |
                 (int)((buff[1] & 0xff) << 16) |
                 (int)((buff[2] & 0xff) << 8) |
                 (int)(buff[3] & 0xff);

    // if the 4 bytes of the CRC32 is not included in the length, 
    // increment the length
    packLen+=4;
    offset=4;
                if (packLen > 4092)
                {
                   // packet is too big, ignore it or do something else
                   packLen=4092;
                }
    bLeft=packLen;
    // Noew loop until the whole mesage has been read
    while (bLeft > 0) {
        ret = in.read(buff, offset, bLeft);
        if (ret > 0) {
            bLeft-=ret;
            offset+=ret;
        }
        else if (ret == 0) {
            // socket has been closed
        }
        else {
            // soket has an error
        }
    }
    // the readChunk function must be change
    // Need to pass the length of the message. 
    // Is not the buff.length anymore 
    readChunk(buff, packLen+4 /* +4 for the length of the message*/);
}