当前位置:编程学习 > C#/ASP.NET >>

Socket通信连续接收数据不流畅,求解惑!!!

做一个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 数据 --------------------编程问答-------------------- tcp/ip  如果2次发送时间间隔很短 会在缓冲满了以后在发送 --------------------编程问答--------------------
引用 1 楼 wuwenbin104 的回复:
tcp/ip  如果2次发送时间间隔很短 会在缓冲满了以后在发送

1. 缓冲大小是用ReceiveBufferSize设置的吗?
2. 一帧数据40Byte,我设置ReceiveBufferSize = 40,速度更慢了。。。
3. 还是不明白为什么加上Send后数据就出现的流畅了...
4. 感谢回答。。。 --------------------编程问答-------------------- 那个不叫流程。原来是一下子连续得到20个消息(在非常不精确的DataTime类型的对象的误差范围之内)。现在每得到一个消息都卡了一下下,这样你的Send语句是让程序变得更慢了,而不是变快了。 --------------------编程问答-------------------- 从你两个图来看,接收速度都很慢,但是基本上是一样速度的。

你可以试试把 AppendToTxt 仅仅记录在内存中,而不是写到窗口UI控件上,看看结果如何。 --------------------编程问答--------------------
引用 3 楼 sp1234 的回复:
那个不叫流程。原来是一下子连续得到20个消息(在非常不精确的DataTime类型的对象的误差范围之内)。现在每得到一个消息都卡了一下下,这样你的Send语句是让程序变得更慢了,而不是变快了。

理论上应该是像你说的这样,而且一开始我也是这么认为的(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语句后鼠标反而移动的很流畅。 --------------------编程问答-------------------- 楼主:TCP的数据是以流的形式发送和接收的,所以不应该判断某一次接收的数据大小是否为40,而是应该把收到的数据进行累计,满到40个的时候显示,其实满到4个就可以了。
楼主的这种用法实际上是丢失了很多数据。 --------------------编程问答--------------------
沉了???自己水起…… --------------------编程问答-------------------- 你这个接收数据缓存区不应该设置为40,应该设置1024或更大些。每次接收到数据时,是可以判断总共接收了多少个有效数据位的,然后将有效数据提取存入缓存内,而每次存入数据到缓存后,进行数据有效性检查,有效则取出然后输出到其他地方,比如UI。
具体可参考:C#通讯调试工具v3.0中TCP部分。 --------------------编程问答--------------------
引用 8 楼 yeqi3000 的回复:
你这个接收数据缓存区不应该设置为40,应该设置1024或更大些。每次接收到数据时,是可以判断总共接收了多少个有效数据位的,然后将有效数据提取存入缓存内,而每次存入数据到缓存后,进行数据有效性检查,有效则取出然后输出到其他地方,比如UI。
具体可参考:C#通讯调试工具v3.0中TCP部分。

当我设置成1024的时候,每次接收的有效数据位都是40的倍数(一般是40,80,120,很少有超过120的),为了显示数据传输的实时性,我就直接设置成40了~而且设置成1024并没有解决我上面所述的问题啊…… --------------------编程问答--------------------
引用 9 楼 asan2006 的回复:
当我设置成1024的时候,每次接收的有效数据位都是40的倍数(一般是40,80,120,很少有超过120的),为了显示数据传输的实时性,我就直接设置成40了~而且设置成1024并没有解决我上面所述的问题啊……

你还是不明白缓存的作用,你说能收到的数据位数是40的倍数,那你一次接收即可处理多条数据,而你为什么非得拆成几个40来处理呢?你可以先拿我那个工具调试看看。 --------------------编程问答--------------------
引用 10 楼 yeqi3000 的回复:
Quote: 引用 9 楼 asan2006 的回复:

当我设置成1024的时候,每次接收的有效数据位都是40的倍数(一般是40,80,120,很少有超过120的),为了显示数据传输的实时性,我就直接设置成40了~而且设置成1024并没有解决我上面所述的问题啊……

你还是不明白缓存的作用,你说能收到的数据位数是40的倍数,那你一次接收即可处理多条数据,而你为什么非得拆成几个40来处理呢?你可以先拿我那个工具调试看看。



两个问题:
1. 通讯调试工具v3.0 每次大概接收800个字节(有时候是840或者更多),跟我在1楼说的情况很想,数据是一块一块的跳出来的,虽然这样加快了处理速度,但是实时性并不高(下位机每次发送40个字节),我想要的是实时性处理的,有个几毫秒的延迟没有关系。
2. 不知道是不是我的使用方法有问题,停止监听了以后,仍然还在接收数据。文本框还是不断刷新。
3. 非常感谢你的回答~ --------------------编程问答--------------------
引用 11 楼 asan2006 的回复:
Quote: 引用 10 楼 yeqi3000 的回复:

Quote: 引用 9 楼 asan2006 的回复:

当我设置成1024的时候,每次接收的有效数据位都是40的倍数(一般是40,80,120,很少有超过120的),为了显示数据传输的实时性,我就直接设置成40了~而且设置成1024并没有解决我上面所述的问题啊……

你还是不明白缓存的作用,你说能收到的数据位数是40的倍数,那你一次接收即可处理多条数据,而你为什么非得拆成几个40来处理呢?你可以先拿我那个工具调试看看。



两个问题:
1. 通讯调试工具v3.0 每次大概接收800个字节(有时候是840或者更多),跟我在1楼说的情况很想,数据是一块一块的跳出来的,虽然这样加快了处理速度,但是实时性并不高(下位机每次发送40个字节),我想要的是实时性处理的,有个几毫秒的延迟没有关系。
2. 不知道是不是我的使用方法有问题,停止监听了以后,仍然还在接收数据。文本框还是不断刷新。
3. 非常感谢你的回答~

1,该工具就是实事求是的显示每次接收到的数据。至于你说的实时性并不高,我不太懂你的具体指向。
2,停止监听,跟断开TCP连接是两码事。你若要断开连接,可以右击TCP连接对象列表操作。 --------------------编程问答-------------------- 我仔细看了下你之前帖子内容,似乎你所说的流畅就是数据是一条,一条,有规律的在文本框里显示?这根不是不是真正的所谓实时性。 --------------------编程问答-------------------- Java客户端每次发送数据是否有Refresh? --------------------编程问答--------------------
引用 13 楼 yeqi3000 的回复:
我仔细看了下你之前帖子内容,似乎你所说的流畅就是数据是一条,一条,有规律的在文本框里显示?这根不是不是真正的所谓实时性。

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


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手机传感器的数据。。。 --------------------编程问答--------------------
引用 15 楼 asan2006 的回复:
Quote: 引用 13 楼 yeqi3000 的回复:

我仔细看了下你之前帖子内容,似乎你所说的流畅就是数据是一条,一条,有规律的在文本框里显示?这根不是不是真正的所谓实时性。

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

这个不是数据一卡一卡,实时情况就是接收数据的频率有这么快。如果你非得人为的把数据分割规律显示,那你就先存入缓存,然后再由UI定时10MS取一条来显示好了。 --------------------编程问答-------------------- 有一个问题,你的第二个图的时间差很怪异,windows的C#的DateTime.Now没有这个精确度。 --------------------编程问答--------------------
引用 18 楼 sbwwkmyd 的回复:
有一个问题,你的第二个图的时间差很怪异,windows的C#的DateTime.Now没有这个精确度。

你是说10ms左右吗?
代码也在上面,(timeNew - timeOld).TotalMilliseconds
不是DateTime.Now,应该是DateSpan.TotalMilliseconds的数值。 --------------------编程问答-------------------- 另外windows不是一个实时操作系统,if (numBytes == 40)这个判断可能过滤掉了很多有效数据,TCP粘包是很正常的,必须要考虑的。 --------------------编程问答--------------------
引用 17 楼 yeqi3000 的回复:
Quote: 引用 15 楼 asan2006 的回复:

Quote: 引用 13 楼 yeqi3000 的回复:

我仔细看了下你之前帖子内容,似乎你所说的流畅就是数据是一条,一条,有规律的在文本框里显示?这根不是不是真正的所谓实时性。

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

这个不是数据一卡一卡,实时情况就是接收数据的频率有这么快。如果你非得人为的把数据分割规律显示,那你就先存入缓存,然后再由UI定时10MS取一条来显示好了。


你意思是,加上Send虽然看起来流畅了,其实是传输速度变慢了??? --------------------编程问答--------------------
引用 19 楼 asan2006 的回复:
Quote: 引用 18 楼 sbwwkmyd 的回复:

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

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

windows不是实时系统,我是说两个DateTime.Now的差没有这个精度。一般都是15ms或者10ms左右的一个比较稳点的值。 --------------------编程问答--------------------
引用 20 楼 sbwwkmyd 的回复:
另外windows不是一个实时操作系统,if (numBytes == 40)这个判断可能过滤掉了很多有效数据,TCP粘包是很正常的,必须要考虑的。

恩 有考虑到, 40正好是我这里一个包的大小,不多不少~
最开始我写的是if (numBytes > 0),效果是一样的。。。 --------------------编程问答--------------------
引用 19 楼 asan2006 的回复:
Quote: 引用 18 楼 sbwwkmyd 的回复:

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

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

你的第二个图显示出,时间差的精度达到1ms了,很是奇怪。 --------------------编程问答--------------------
引用 24 楼 sbwwkmyd 的回复:
Quote: 引用 19 楼 asan2006 的回复:

Quote: 引用 18 楼 sbwwkmyd 的回复:

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

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

你的第二个图显示出,时间差的精度达到1ms了,很是奇怪。



应该是10ms左右,没有1ms啊???
而且输出时间差只是为了和第一个图比较~ 呵呵 没啥别的意思 --------------------编程问答--------------------
引用 17 楼 yeqi3000 的回复:
Quote: 引用 15 楼 asan2006 的回复:

Quote: 引用 13 楼 yeqi3000 的回复:

我仔细看了下你之前帖子内容,似乎你所说的流畅就是数据是一条,一条,有规律的在文本框里显示?这根不是不是真正的所谓实时性。

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

这个不是数据一卡一卡,实时情况就是接收数据的频率有这么快。如果你非得人为的把数据分割规律显示,那你就先存入缓存,然后再由UI定时10MS取一条来显示好了。


已验证过,好使!不过定时时间不好掌握啊,毕竟不是均匀的10MS~ --------------------编程问答--------------------
引用 25 楼 asan2006 的回复:
应该是10ms左右,没有1ms啊???
而且输出时间差只是为了和第一个图比较~ 呵呵 没啥别的意思

我是说两个DateTime.Now的差 9,10,11,12 ms都有,很奇怪。要得到这样的差,时间精度必须达到1ms以上。
否则就应该是固定的 10.XXXms 与 0ms 交替出现。 --------------------编程问答--------------------
引用 27 楼 sbwwkmyd 的回复:
Quote: 引用 25 楼 asan2006 的回复:
应该是10ms左右,没有1ms啊???
而且输出时间差只是为了和第一个图比较~ 呵呵 没啥别的意思

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


这个我觉得很正常吧,你在下位机持续发送数据,中间sleep(10),应该也会得到这样子~ --------------------编程问答-------------------- 除 --------------------编程问答-------------------- 没有人讨论了??? --------------------编程问答-------------------- 建议去看下ESFramework框架中对于数据的发送和接受的高效处理方法,也许会给你带来启发。 --------------------编程问答-------------------- 多线程才行。
补充:.NET技术 ,  C#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,