做一个Socket通信的功能,客户端是Android手机,用Java写的代码,服务端暂时是用C#开发的Winform程序(局域网内WIFI进行数据传输)。但是接收数据的时候,数据在文本框中是一块一块出来的,很不流畅。上个图:前三列数据是Android手机发送的数据(重力加速度计的数据,这个不重要),第四列输出的是两帧数据之间的时间差(单位ms),相关代码如下:
while (socketStatus)
            {
                SocketPacket sPacket = new SocketPacket();           
                sPacket.workSocket = listener.Accept();
                Socket handler = sPacket.workSocket;
                timeOld = DateTime.Now;
                while (true)
                {
                    try
                    {
                        int numBytes = sPacket.workSocket.Receive(sPacket.buffer);
                        if (numBytes == 40)
                        {
                            //accelerometer in units (m/s^2)
                            float ax = BitConverter.ToSingle(sPacket.buffer, 0);
                            float ay = BitConverter.ToSingle(sPacket.buffer, 4);
                            float az = BitConverter.ToSingle(sPacket.buffer, 8);
                            float gx = BitConverter.ToSingle(sPacket.buffer, 12);
                            float gy = BitConverter.ToSingle(sPacket.buffer, 16);
                            float gz = BitConverter.ToSingle(sPacket.buffer, 20);
                            float mx = BitConverter.ToSingle(sPacket.buffer, 24);
                            float my = BitConverter.ToSingle(sPacket.buffer, 28);
                            float mz = BitConverter.ToSingle(sPacket.buffer, 32);                            timeNew = DateTime.Now;
                            AppendToTxt(ax.ToString("0.00") + "\t" + ay.ToString("0.00") + "\t" + az.ToString("0.00") + "\t" + (timeNew - timeOld).TotalMilliseconds + "\r\n");
                            timeOld = timeNew;
                            //Send(handler, "OK");
                        }
                        else if (numBytes == 0)
                        {
                            break;
                        }
                    }
                    catch (System.Exception ex)
                    {
                        MessageBox.Show(ex.Message);
                        break;
                    }                                    }
            }
可以看出接收数据的时间间隔很不稳定,具体的视觉效果,看起来在textBox框中数据是一下子整块跳出来的。上面相关的Send代码我注释掉了,如果加上去,数据反而看起来流畅了很多。
Send方法:private static void Send(Socket handler, String data)
        {
            // Convert the string data to byte data using ASCII encoding.
            byte[] byteData = Encoding.ASCII.GetBytes(data);            // Begin sending the data to the remote device.
            handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler);
        }        private static void SendCallback(IAsyncResult ar)
        {
            try
            {
                // Retrieve the socket from the state object.
                Socket handler = (Socket)ar.AsyncState;                // Complete sending the data to the remote device.
                int bytesSent = handler.EndSend(ar);         
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
如果每次接收完数据后,就进行一次Send,数据看起来就是这样的:因为在Android手机上,每次发送完收据后sleep(9),加上程序运行的时间,所以时间间隔是10ms左右,这样子在WINFORM上看起来数据就是很流畅的流下去。
但是我并不需要向客户端发送数据,服务端只需要接收并处理数据就好了,为什么会出现这种情况呢???
一开始我以为是异步接收数据的问题,后来我重写了服务端的C#代码,不管是同步接收还是异步接收数据都是这样的情况。
会不会和TCP的握手协议有关呢???求解惑!!!通信socket数据

解决方案 »

  1.   

    tcp/ip  如果2次发送时间间隔很短 会在缓冲满了以后在发送
      

  2.   

    1. 缓冲大小是用ReceiveBufferSize设置的吗?
    2. 一帧数据40Byte,我设置ReceiveBufferSize = 40,速度更慢了
    3. 还是不明白为什么加上Send后数据就出现的流畅了...
    4. 感谢回答
      

  3.   

    那个不叫流程。原来是一下子连续得到20个消息(在非常不精确的DataTime类型的对象的误差范围之内)。现在每得到一个消息都卡了一下下,这样你的Send语句是让程序变得更慢了,而不是变快了。
      

  4.   

    从你两个图来看,接收速度都很慢,但是基本上是一样速度的。你可以试试把 AppendToTxt 仅仅记录在内存中,而不是写到窗口UI控件上,看看结果如何。
      

  5.   

    理论上应该是像你说的这样,而且一开始我也是这么认为的(Send会让程序变慢)。
    因为客户端每次固定发送40个Byte数据,我把服务端接收buffer大小设置为40。    class SocketPacket
        {
            // Client  socket.
            public Socket workSocket = null;
            // Size of receive buffer.
            public const int BufferSize = 40;
            // Receive buffer.
            public byte[] buffer = new byte[BufferSize];
            
        }
    这样仍然达不到我所说的那种数据流畅的效果。2. 关于你所说的记录在内存中,查看效果,我尝试过下面的做法:
    让数据控制鼠标的移动(不在UI控件上显示),如果不加Send,鼠标指针一卡一卡的,那种卡的感觉就像内存不够用了似的~加上Send语句后鼠标反而移动的很流畅。
      

  6.   

    楼主:TCP的数据是以流的形式发送和接收的,所以不应该判断某一次接收的数据大小是否为40,而是应该把收到的数据进行累计,满到40个的时候显示,其实满到4个就可以了。
    楼主的这种用法实际上是丢失了很多数据。
      

  7.   

    你这个接收数据缓存区不应该设置为40,应该设置1024或更大些。每次接收到数据时,是可以判断总共接收了多少个有效数据位的,然后将有效数据提取存入缓存内,而每次存入数据到缓存后,进行数据有效性检查,有效则取出然后输出到其他地方,比如UI。
    具体可参考:C#通讯调试工具v3.0中TCP部分。
      

  8.   

    你还是不明白缓存的作用,你说能收到的数据位数是40的倍数,那你一次接收即可处理多条数据,而你为什么非得拆成几个40来处理呢?你可以先拿我那个工具调试看看。
    两个问题:
    1. 通讯调试工具v3.0 每次大概接收800个字节(有时候是840或者更多),跟我在1楼说的情况很想,数据是一块一块的跳出来的,虽然这样加快了处理速度,但是实时性并不高(下位机每次发送40个字节),我想要的是实时性处理的,有个几毫秒的延迟没有关系。
    2. 不知道是不是我的使用方法有问题,停止监听了以后,仍然还在接收数据。文本框还是不断刷新。
    3. 非常感谢你的回答~
    1,该工具就是实事求是的显示每次接收到的数据。至于你说的实时性并不高,我不太懂你的具体指向。
    2,停止监听,跟断开TCP连接是两码事。你若要断开连接,可以右击TCP连接对象列表操作。
      

  9.   

    Java客户端每次发送数据是否有Refresh?
      

  10.   

    额 是这样的,我下位机每次(约10ms间隔)发送40个字节,在PC机Server端接收数据后要进行相应的数据处理,我希望接收到40个字节后立即进行数据处理。现在遇到的状态是每次从缓存中读取的数据都是40个N倍,也就是缓存中存储了N帧的数据。所以我才把缓存大小设置成了40.
    额 不过貌似跑题了 我在1楼说的主要问题是:即使设置缓存大小40,如果在每次接收数据后不加Send,数据仍然是一卡一卡的
      

  11.   


    public void run() {
    try {
    PORT = Integer.parseInt(port.getText().toString());
    serverIpAddress = ipAdr.getText().toString();
    InetAddress serverAddr = InetAddress.getByName(serverIpAddress); socket = new Socket(serverAddr, PORT);
    connected = true;
    outputStream = new DataOutputStream(socket.getOutputStream());
    while (connected) { if (connected && outputStream != null) {
    byte[] byte_ax = float2byte(ax);
    byte[] byte_ay = float2byte(ay);
    byte[] byte_az = float2byte(az);
    byte[] byte_gx = float2byte(gx);
    byte[] byte_gy = float2byte(gy);
    byte[] byte_gz = float2byte(gz);
    byte[] byte_mx = float2byte(mx);
    byte[] byte_my = float2byte(my);
    byte[] byte_mz = float2byte(mz);
    byte[] btemp = new byte[40];
    for (int i = 0; i < 4; i++) {
    btemp[i] = byte_ax[i];
    btemp[i + 4] = byte_ay[i];
    btemp[i + 8] = byte_az[i];
    btemp[i + 12] = byte_gx[i];
    btemp[i + 16] = byte_gy[i];
    btemp[i + 20] = byte_gz[i];
    btemp[i + 24] = byte_mx[i];
    btemp[i + +28] = byte_my[i];
    btemp[i + 32] = byte_mz[i];
    } outputStream.write(btemp);
    outputStream.flush();
    Thread.sleep(9);
    } }
    } catch (Exception e) {
    refreshDisplay(e.getMessage()); } finally {
    try {
    info_disp = false;
    connected = false;
    connectPhones.setText("Start Streaming");
    outputStream.close();
    socket.close();
    } catch (Exception a) {
    }
    }
    }
    你说的是上面的outputStream.flush();吗?
    数据refresh的频率很高,发送的是Android手机传感器的数据
      

  12.   

    额 是这样的,我下位机每次(约10ms间隔)发送40个字节,在PC机Server端接收数据后要进行相应的数据处理,我希望接收到40个字节后立即进行数据处理。现在遇到的状态是每次从缓存中读取的数据都是40个N倍,也就是缓存中存储了N帧的数据。所以我才把缓存大小设置成了40.
    额 不过貌似跑题了 我在1楼说的主要问题是:即使设置缓存大小40,如果在每次接收数据后不加Send,数据仍然是一卡一卡的
    这个不是数据一卡一卡,实时情况就是接收数据的频率有这么快。如果你非得人为的把数据分割规律显示,那你就先存入缓存,然后再由UI定时10MS取一条来显示好了。
      

  13.   

    有一个问题,你的第二个图的时间差很怪异,windows的C#的DateTime.Now没有这个精确度。
      

  14.   

    你是说10ms左右吗?
    代码也在上面,(timeNew - timeOld).TotalMilliseconds
    不是DateTime.Now,应该是DateSpan.TotalMilliseconds的数值。
      

  15.   

    另外windows不是一个实时操作系统,if (numBytes == 40)这个判断可能过滤掉了很多有效数据,TCP粘包是很正常的,必须要考虑的。
      

  16.   

    额 是这样的,我下位机每次(约10ms间隔)发送40个字节,在PC机Server端接收数据后要进行相应的数据处理,我希望接收到40个字节后立即进行数据处理。现在遇到的状态是每次从缓存中读取的数据都是40个N倍,也就是缓存中存储了N帧的数据。所以我才把缓存大小设置成了40.
    额 不过貌似跑题了 我在1楼说的主要问题是:即使设置缓存大小40,如果在每次接收数据后不加Send,数据仍然是一卡一卡的
    这个不是数据一卡一卡,实时情况就是接收数据的频率有这么快。如果你非得人为的把数据分割规律显示,那你就先存入缓存,然后再由UI定时10MS取一条来显示好了。你意思是,加上Send虽然看起来流畅了,其实是传输速度变慢了???
      

  17.   

    你是说10ms左右吗?
    代码也在上面,(timeNew - timeOld).TotalMilliseconds
    不是DateTime.Now,应该是DateSpan.TotalMilliseconds的数值。
    windows不是实时系统,我是说两个DateTime.Now的差没有这个精度。一般都是15ms或者10ms左右的一个比较稳点的值。
      

  18.   

    恩 有考虑到, 40正好是我这里一个包的大小,不多不少~
    最开始我写的是if (numBytes > 0),效果是一样的
      

  19.   

    你是说10ms左右吗?
    代码也在上面,(timeNew - timeOld).TotalMilliseconds
    不是DateTime.Now,应该是DateSpan.TotalMilliseconds的数值。
    你的第二个图显示出,时间差的精度达到1ms了,很是奇怪。
      

  20.   

    你是说10ms左右吗?
    代码也在上面,(timeNew - timeOld).TotalMilliseconds
    不是DateTime.Now,应该是DateSpan.TotalMilliseconds的数值。
    你的第二个图显示出,时间差的精度达到1ms了,很是奇怪。
    应该是10ms左右,没有1ms啊???
    而且输出时间差只是为了和第一个图比较~ 呵呵 没啥别的意思
      

  21.   

    额 是这样的,我下位机每次(约10ms间隔)发送40个字节,在PC机Server端接收数据后要进行相应的数据处理,我希望接收到40个字节后立即进行数据处理。现在遇到的状态是每次从缓存中读取的数据都是40个N倍,也就是缓存中存储了N帧的数据。所以我才把缓存大小设置成了40.
    额 不过貌似跑题了 我在1楼说的主要问题是:即使设置缓存大小40,如果在每次接收数据后不加Send,数据仍然是一卡一卡的
    这个不是数据一卡一卡,实时情况就是接收数据的频率有这么快。如果你非得人为的把数据分割规律显示,那你就先存入缓存,然后再由UI定时10MS取一条来显示好了。已验证过,好使!不过定时时间不好掌握啊,毕竟不是均匀的10MS~
      

  22.   

    我是说两个DateTime.Now的差 9,10,11,12 ms都有,很奇怪。要得到这样的差,时间精度必须达到1ms以上。
    否则就应该是固定的 10.XXXms 与 0ms 交替出现。
      

  23.   

    我是说两个DateTime.Now的差 9,10,11,12 ms都有,很奇怪。要得到这样的差,时间精度必须达到1ms以上。
    否则就应该是固定的 10.XXXms 与 0ms 交替出现。这个我觉得很正常吧,你在下位机持续发送数据,中间sleep(10),应该也会得到这样子~
      

  24.   

    建议去看下ESFramework框架中对于数据的发送和接受的高效处理方法,也许会给你带来启发。