【问题标题】:is there a better way of sending a list of int over a socket?有没有更好的方法通过套接字发送 int 列表?
【发布时间】:2015-05-19 23:32:05
【问题描述】:

我想通过 tcp 发送一个 int 列表(可以是 10 到 1000 个 int)

在解析数据之前,我会向客户端发送它应该接收多少字节。

我也使用 BeginSend / BeginReceive 模式(每个线程一个线程)。

我想出了这个简单的代码来做我想做的事

const int sizeOfInt = sizeof(int);
int index = 0;

var OriginalList = new List<int>();
OriginalList.Add(1);
OriginalList.Add(42);
//.....
OriginalList.Add(9001);


//prepare and send the ArrayOfByte over the wire
var ArrayOfByte = new byte[OriginalList.Count * sizeOfInt];

foreach (var item in OriginalList)
{
    Array.Copy(BitConverter.GetBytes(item), 0, ArrayOfByte, index, sizeOfInt);

    index += sizeOfInt;
}
//socket.BeginSend(ArrayOfByte....


//socket.BeginReceive(ArrayOfByte....
//On receive move it into a List<int>
int length = ArrayOfByte.Length;

var CopyOfList = new List<int>(length / sizeOfInt);

for (index = 0; index < length; index += sizeOfInt)
{
    CopyOfList.Add(BitConverter.ToInt32(ArrayOfByte, index));
}

有没有更好/更快的方法来做到这一点?

【问题讨论】:

  • 您的代码似乎假定socket.receive(ArrayOfByte) 在一次调用中接收到数组。这并非总是如此!假设您正在写入硬盘驱动器上的文件,关闭程序然后重新打开它,第二次运行如何知道在您的第一次.Write( 调用中写入了多少字节?网络读取也有同样的问题。
  • @ScottChamberlain 我删除了代码的套接字部分,但我确实管理了在另一端解析之前应该到达多少字节

标签: c# sockets serialization asynchronous deserialization


【解决方案1】:

更快:可能不会(您可以优化发送以匹配网络数据包大小,但这不会给您带来太多好处)。异步执行所有操作会使用更少的系统资源(不是阻塞线程),但也不会更快。

更好:首先告诉另一端您要发送多少数据(一个单独的进程,即使在同一台机器上也无法从发送方访问ArrayOfByte 以调整接收大小);第二个把事情分解成单独的功能,每个功能都有一个目的。

【讨论】:

  • 我在我的问题中添加了更多信息,我没有想到添加我是如何管理套接字的。
【解决方案2】:

想法:

创建很多不必要的小byte[] 实例来收集;这是BitConverter API 带来的一点痛苦,但可以避免;我可能只使用位操作(移位/和)来写入字节,因为这样可以避免 CPU 字节序问题,但也可以使用 unsafe 非常有效地完成; BinaryWriter 也可以使用

CPU 字节序

您实际上并不需要 byte[] 来获取所有内容;您可以按顺序编写,但请注意,如果您禁用了套接字缓冲(NoDelay 等),您可能需要考虑数据包的合理大小

如果您了解有关可能整数的更多信息,则可能会使用更有效的表示形式 - 例如,“varint”编码(基本上,类似于 UTF-8 将低位字符编码为单字节,并使用多字节表示高位字符)

为了让消费者有效地处理数据,您还应该发送元素的数量或元素的有效负载大小,以便接收者知道何时收到了逻辑帧 - 除非套接字上只有一条消息


一些基于位操作的套接字代码示例:

int index = 0;
foreach(var val in list) {
    buffer[index++] = (byte)val;
    buffer[index++] = (byte)(val >> 8);
    buffer[index++] = (byte)(val >> 16);
    buffer[index++] = (byte)(val >> 24);

    if(index == buffer.Length) { // flush buffer
        socket.Write(buffer, 0, index);
        index = 0;
    }
}
if(index != 0) { // final flush
    socket.Write(buffer, 0, index);
}

读取代码需要确保在开始之前有一定数量的值,那么:

while(index < availableBytes) {
    int val = buffer[index++] | (buffer[index++] << 8)
            | (buffer[index++] << 16) | (buffer[index++] << 24);
    list.Add(val);
}

【讨论】:

  • 我刚刚用 ILSpy 查看了 bitconverter,我喜欢移除 bitconverter 的想法,我将查看套接字缓冲
  • 对于使用 bit-ops 的解决方案,你是写 4 行代码还是简单地写一个 for 循环(我认为循环会矫枉过正,但我​​更喜欢问)
  • bit-ops 比 bitconverter 快大约 3 倍,并且缓冲套接字似乎很容易实现,所以我会尝试看看它在我当前的代码库中是如何运行的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 2014-09-02
  • 1970-01-01
  • 2023-03-19
相关资源
最近更新 更多