我在linux系统下写了一个socket程序。服务端用C语言实现的,客户端用java实现。
基本流程是客户端给服务端发送一个数据过去,然后服务端返回给客户端返回一个数据回来。
但是我测试的时候发现当服务端的数据长度超过1500个字节的时候,我的客户端只能收到1448个字节,其他的就收不到了。后来发现当网络延时较小的时候,都能收到,但是网络延时较大的时候,后面的字节就收不到了。
请问这是什么原因呢?我只是知道网络传输的MTU一般是1500,是不是分包后,后面的数据丢失了呢?
请了解的朋友给分析一下!谢谢!!

解决方案 »

  1.   

    1)TCP通信还是UDP通信?
    2)贴一下代码。还要想那么多,对你这个程序,MTU不会影响你的,若你的问题属实,几乎肯定你的代码有BUG。
      

  2.   

    先谢谢楼上的朋友了!我把服务端和客户端的代码都贴上来,大家帮忙给看看吧!呵呵!
    我在两个网络环境下测试过,第一种是当我用ping命令测试网络延时的时候,time=0.1ms左右,数据就不会丢失,第二种网络环境,time=0.3ms左右的时候数据就会丢包,而且很频繁,我只能收到第一个1448的数据包,后面的496数据包就不知道哪去了。后来用tcpdump抓包看了一下,发现数据包在客户端的linux上也确实是接收到了,但是不知道为什么就是收不到。
    我在windows系统下测试过,同样的网络环境,但是没出现过一次丢包现象。但是windows和linux的网络数据包头好像是不同的。我已经测试了有好几天了,实在是找不到原因了!我对java了解的不多,大部分都是看手册了解的,所以如果有用的不对的地方还请大家给我指出来!万分感谢啊!服务端(C语言):int main()
    {
    unsigned char Recv[100],PassWord[20], TmpData[50], pbuf[3], RecvData[100], SendData[1944];
    int reval, i;

    memset(RecvData, 0, sizeof(RecvData));
    memset(Recv, 0, sizeof(Recv));
    memset(SendData, '1', sizeof(SendData));
    memset(PassWord, 0, sizeof(PassWord));
    memset(TmpData, 0, sizeof(TmpData));
    memset(pbuf, 0, sizeof(pbuf));
    memcpy(pbuf, "ok", sizeof(pbuf));

    reval = Sock_ServerInit(&listenfd,6006); //Sock_ServerInit是我自己写的函数
    if(reval != 0)
    {
    printf("Server Init Error!\n");
    return -1;
    }
    printf("Socket Init OK,Begin get data....\n"); i = 0;
    while(1)
    {
    i++;
    reval = Sock_Accept(listenfd,&connectfd);//Sock_Accept是我自己写的函数
    if(reval == 0)
    {
    reval = recv(connectfd,Recv,4,0);
    printf("recv = %d\n", reval);
    if(4 != reval)
    {
    printf("recv error!code:%04x\n",reval);
    return -2;
    }

    reval = send(connectfd,SendData,sizeof(SendData),10);
    printf("send = %d\n", reval);
    if(reval < 0)
    {
    printf("send error!code:%04x\n",reval);
    return -2;
    } printf("The %d Successed!\n", i);
         CloseSocket(connectfd);
        
    }

    }
    return 0;
    }客户端(JAVA语言):public class JavaSocket {    public static void main(String[] args) {
            
            try{
                while(true){
                    int loop = getInput("loop:", 3);
                    if(loop <= 0)
                        break;
            for(int i = 1; i <= loop; i++){
                Socket socket=new Socket("192.168.2.123",6006); 
                socket.setSoTimeout(160*1000);
                 InputStream in = null;
                 OutputStream out = null;
                 in = socket.getInputStream();
            out = socket.getOutputStream();
            byte[] send = { 0x01, 0x02, 0x03, 0x04 };
                System.out.println("send = " + send.length);
            out.write(send);
            out.flush();
            
            byte[] recv = new byte[2000];
            int recvnum = in.read(recv);
            System.out.println("recvnum = " + recvnum);
            byte[] outdata = new byte[recvnum];
            System.arraycopy(recv,0,outdata,0,recvnum);
            System.out.println("outdatalen = " + outdata.length);
            if(outdata.length == 1944 || outdata.length == 1024)
                System.out.println("OK!\nThe " + i + " Successed!");
            else
                System.out.println("ERROR!");
            
            in.close();
            out.close();
            socket.close();
            }
                }
           }catch(Exception e){
                e.printStackTrace();
            }    }
        
        public static int getInput(String promptString, int type) {
            int out = 0, i = 0;
            try {
                byte[] inchar = new byte[10];
                System.out.print(promptString);
                System.in.read(inchar, 0, 10);
                if ((inchar[0] == 10) || (inchar[0] == 0x0d)) {
                    if (type == 1)
                        return 1;
                    else if (type == 2)
                        return 0;
                    else
                        return -1;
                }
                out = Integer.parseInt((new String(inchar)).trim());
            } catch (Exception ee) {
                return -1;
            }
            return out;
        }
    }
      

  3.   

    答:与系统没有关系.是程序设计上有些不妥.无论是c还是JAVA,发送数据几乎没有阻塞.但接受数据不一定.即:JAVA方: byte[] recv = new byte[2000]; 
        int recvnum = in.read(recv); 
    它不保证:就一次read()就能将C方send()的所有数据就全读进来.尤其是数据量较大的时候.
    同样C方也有这个问题.也不能保证一次read()就全部全部读入JAVA方发送出的数据.尤其是数据量较大的时候
      

  4.   

    请问如何应该如何来修改程序使得可以一次接收较大的数据呢?
    因为服务端一次必须要发送1944个字节,所以我在java客户端就必须一次接收1944个字节,请问应该如何进行处理呢?
      

  5.   

    答:以下代码供你参考:
    (基本想法:无论是C,还是JAVA,在接受数据时,都不能保证一次就全接受1944个数据.故要多次读.)
             byte[] recv = new byte[2000]; 
    byte[] outdata = new byte[2000]; 
    int total=0;
    int recvnum = 0; 
    while(total<1944)
    {
    recvnum=in.read(recv);
    if(recvnum>0)
    {
     System.arraycopy(recv,0,outdata,total,recvnum); 
     total+=recvnum;
    }
    }
      

  6.   

    非常感谢“云上飞翔”的帮助,上面代码的意思是不是就是只要total小于1944就反复的去读,直到recvnum里面读不出数据才算是都读完了?这样的话会不会多读出其他的数据来呢?
      

  7.   

    给你的参考代码是基于你的假设:长度是1944字节.这样不会多读字节.但若你的数据长度有一些是不到1944个,则要加上:if(recvnum==-1){break;}
    这个代码含义是说:若不到1944个字节就没有数据了,则跳出循环.
    以上想法仅供你参考
      

  8.   

     自己做封包,每个包有多大在包头定义.
     用缓冲收数据,从缓冲中把包拆出来.
     
     伪代码如:
     while(缓冲有空间)
     {
         recv(buf);
         cmd = unpack(buf);
         if(cmd 合法)
           处理cmd;
     }
      

  9.   


    今天尝试了一下,果然是多循环了一次就能都收全了。
    但是奇怪的是,有时候一次就能收全了,有时候要两次,而且如果没有数据了的话,recvnum = in.read(recv); 执行到这句就没有返回了,然后等到超时时间后就报出Read time out的Exception来。不知道是怎么回事。为什么没有了不返回-1呢?是不是后来我把服务端的关闭套接字屏蔽了,然后改成一个长连接了,这样这个socket下次可以继续使用,不知道是不是和这个有关系呢?
    呵呵!总之很感谢“云上飞翔”的帮助,目前问题是解决了!o(∩_∩)o...
      

  10.   

    也谢谢sniperhuangwei 提供的方法,我在以前就是这样用的,结果就是接收到的数据长度就不对,unpack自然就无法正常解包了!呵呵!
      

  11.   

    在chinaunix发了一个同样的帖子,一百多个人看了,没有一个人回帖,呵呵!以后遇到困难还是到这里来寻求帮助!o(∩_∩)o...
      

  12.   

    看来楼主不理解unpack的用处,unpack是在已经接收到的数据中拆出一个合法的包来,如果包不合法就丢弃.
    如果长度不够就等待再下一次循环时recv().
      

  13.   

    问:为什么没有了不返回-1呢?是不是后来我把服务端的关闭套接字屏蔽了,然后改成一个长连接了,这样这个socket下次可以继续使用,不知道是不是和这个有关系呢?
    答:有关系。“长连接”时JAVA就读不到-1了。
      

  14.   

    我想问一下,我把服务端里面的CloseSocket(connectfd); 屏蔽掉是不是就是长连接了呢?但是这样还能读出-1来。
    不过我连接同事写的C的服务端的时候就读不出-1来,他的具体实现我不清楚,因为我没见过他的代码。所以我写的服务端是模仿的他的,还是我对长连接这样理解错了啊?
      

  15.   

    对,也可以在包开始处和结束处加上特殊的字符,判断一个包的开始和结束位置.
    close是关闭一个连接.长短连接的定义如下:短连接就是一次操作完后断开连接长连接就是一次操作完后不断开连接连接一时保留着
    如http协议使用的就是短连接,象游戏服务这类则多数使用长连接.
      

  16.   

    没有数据的时候recv不返回是因为你使用的是阻塞I/O.