Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/324.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在C语言中,通过套接字发送数据是一个非常奇怪的问题#_C#_Arrays_Sockets_Byte - Fatal编程技术网

C# 在C语言中,通过套接字发送数据是一个非常奇怪的问题#

C# 在C语言中,通过套接字发送数据是一个非常奇怪的问题#,c#,arrays,sockets,byte,C#,Arrays,Sockets,Byte,我对这篇冗长的文章表示歉意。我已经尽可能地将其缩小,同时仍在传达问题 好吧,这让我发疯了。我有一个客户端和一个服务器程序,都是C语言。服务器通过Socket.Send()向客户端发送数据。客户端通过Socket.BeginReceive和Socket.Receive接收数据。我的伪协议如下:服务器发送一个两字节(short)值,指示实际数据的长度,后跟实际数据。客户机异步读取前两个“是”,将字节转换为短字节,并立即同步从套接字读取这么多字节 现在,每隔几秒钟,这个循环就可以正常工作,但是当我提高

我对这篇冗长的文章表示歉意。我已经尽可能地将其缩小,同时仍在传达问题

好吧,这让我发疯了。我有一个客户端和一个服务器程序,都是C语言。服务器通过Socket.Send()向客户端发送数据。客户端通过Socket.BeginReceive和Socket.Receive接收数据。我的伪协议如下:服务器发送一个两字节(short)值,指示实际数据的长度,后跟实际数据。客户机异步读取前两个“是”,将字节转换为短字节,并立即同步从套接字读取这么多字节

现在,每隔几秒钟,这个循环就可以正常工作,但是当我提高速度时,事情会变得很奇怪。当客户端试图读取两字节长度时,它似乎会随机读取实际数据。然后,它尝试将这两个任意字节转换为一个短字节,这将导致一个完全不正确的值,从而导致崩溃。下面的代码来自我的程序,但经过了裁剪,只显示了重要的行

用于发送数据的服务器端方法:

private static object myLock = new object();
private static bool sendData(Socket sock, String prefix, byte[] data)
{
    lock(myLock){
        try
        {
            // prefix is always a 4-bytes string
            // encoder is an ASCIIEncoding object    
            byte[] prefixBytes = encoder.GetBytes(prefix);
            short length = (short)(prefixBytes.Length + data.Length);

            sock.Send(BitConverter.GetBytes(length));
            sock.Send(prefixBytes);
            sock.Send(data);

            return true;
        } 
        catch(Exception e){/*blah blah blah*/}
    }
}
用于接收数据的客户端方法:

private static object myLock = new object();
private void receiveData(IAsyncResult result)
{
    lock(myLock){
        byte[] buffer = new byte[1024];
        Socket sock = result.AsyncState as Socket;
        try
        {
            sock.EndReceive(result);
            short n = BitConverter.ToInt16(smallBuffer, 0);
            // smallBuffer is a 2-byte array

            // Receive n bytes
            sock.Receive(buffer, n, SocketFlags.None);

            // Determine the prefix.  encoder is an ASCIIEncoding object
            String prefix = encoder.GetString(buffer, 0, 4);

            // Code to process the data goes here

            sock.BeginReceive(smallBuffer, 0, 2, SocketFlags.None, receiveData, sock);
        }
        catch(Exception e){/*blah blah blah*/}
    }
}
可靠地重新创建问题的服务器端代码:

byte[] b = new byte[1020];  // arbitrary length

for (int i = 0; i < b.Length; i++)
    b[i] = 7;  // arbitrary value of 7

while (true)
{
    sendData(socket, "PRFX", b);
    // socket is a Socket connected to a client running the same code as above
    // "PRFX" is an arbitrary 4-character string that will be sent
}
byte[]b=新字节[1020];//任意长度
for(int i=0;i
查看上面的代码,可以确定服务器将永远发送数字1024,即总数据的长度,包括前缀,作为一个短字符(0x400),后跟ASCII二进制中的“PRFX”,后跟一组7(0x07)。客户端将永远读取前两个字节(0x400),将其解释为1024,将该值存储为n,然后从流中读取1024个“是”

这确实是它在前40次迭代中所做的,但是,客户机会自发地读取前两个“是”,并将其解释为1799次,而不是1024次!十六进制中的1799是0x0707,这是两个连续的7!!!这是数据,不是长度!那两个字节怎么了?无论我在字节数组中输入什么值,都会发生这种情况,我只选择了7,因为很容易看到与1799的相关性

如果你还在阅读这一点,我为你的奉献精神鼓掌

一些重要的意见:

  • 减少b的长度将增加问题发生前的迭代次数,但不会阻止问题的发生
  • 在循环的每次迭代之间增加一个显著的延迟可以防止问题的发生
  • 在同一主机上同时使用客户端和服务器并通过环回地址连接时,不会发生这种情况
如前所述,这让我发疯!我总是能够解决我的编程问题,但这个问题完全难住了我。因此,我在这里恳求任何关于这个问题的建议或知识


谢谢。

我猜
smallBuffer
是在receive方法之外声明的,正在从多个线程重复使用。换句话说,这不是线程安全的


编写好的套接字代码很难。由于您同时编写客户端和服务器,您可能希望了解一下(WCF),它可以让您以更少的麻烦发送和接收整个对象。

我怀疑您是在假设整个消息在对
receiveData
的一次调用中传递。一般来说,情况并非如此。碎片、延迟等最终可能意味着数据以运球的形式到达,因此您可能会在只有900字节准备就绪时调用
receiveData
函数。您的呼叫
sock.Receive(buffer,n,SocketFlags.None)
表示“将数据放入缓冲区,最多为
n
字节”-您可能会收到较少的数据,而实际读取的字节数由
receive
返回

这就解释了为什么减少
b
、增加更多延迟或使用同一主机似乎可以“修复”问题——使用这些方法,整个消息一次性到达的可能性大大增加(越小的
b
,数据越小;添加延迟意味着管道中的总体数据越少;本地地址没有网络)


若要查看这是否确实是问题所在,请记录
Receive
的返回值。如果我的诊断正确,返回值有时会小于1024。若要解决此问题,您需要等待直到本地网络堆栈的缓冲区包含您的所有数据(不理想),或者只在数据到达时接收数据,将其存储在本地,直到整个消息准备就绪,然后再进行处理。

使用套接字时,必须预料套接字传输的字节数可能比预期的少。必须循环.receive方法以获取剩余的字节数

通过套接字发送字节时也是如此。必须检查发送了多少字节,并在发送过程中循环,直到发送完所有字节

这种行为是由于网络层将消息拆分为多个数据包造成的。如果您的消息很短,则您不太可能遇到这种情况。但您应该始终为此编写代码


如果每个缓冲区有更多字节,您很可能会看到发送者的消息被分成多个数据包。每个读取操作都会收到一个数据包,这只是消息的一部分。但是小缓冲区也可以被拆分。

我觉得这里的主要问题是没有检查EndReceive的结果,这是一个数字读取的字节数。如果套接字打开,则可以是任何内容
>0

您没有检查函数的返回值。套接字将返回最多“n”个字节,但只返回可用的字节。机会
sock.Receive(buffer, n, SocketFlags.None);