0

我正在用 C# 制作一个 IOCP 服务器,调用 win api。我有 1 个接受线程连接和工作线程,具体取决于 CPU 内核。

我的问题是,我有 2 个线程试图同时处理相同的数据,根据以下代码段,你能告诉我解决方案是什么,还是这是一个设计问题?

public class WorkerThread
    {
        public void Worker(NetSharedData data)
        {
            IntPtr bytestransfer = Marshal.AllocHGlobal(sizeof(UInt32));
            IntPtr clientid = Marshal.AllocHGlobal(sizeof(UInt32));
            IntPtr poverlapped = Marshal.AllocHGlobal(sizeof(UInt32));
            Console.WriteLine("Worker thread running");
            while (true)
            {
                Marshal.WriteInt32(clientid, 0);
                Marshal.WriteInt32(bytestransfer, 0);
                if (SocketInvoke.GetQueuedCompletionStatus(data.completionport, bytestransfer, clientid, poverlapped, SocketInvoke.INFINITE) == true)
                {
                        if (Marshal.ReadInt32(poverlapped) == 0)
                        {
                            //thread shutdown
                            Console.WriteLine("Worker thread shutdown");
                            break;
                        }
                        Client client = data.Clients[Marshal.ReadInt32(clientid)];
                        if (Marshal.ReadInt32(bytestransfer) != 0)
                        {
                            if (client.operationtype == SocketInvoke.FD_WRITE)
                            {
                                if (client.send_data.ispending_operation == true)
                                {
                                    client.SendLeft((uint)Marshal.ReadInt32(bytestransfer));
                                    break;
                                }
                            }
                            if (client.operationtype == SocketInvoke.FD_READ)
                            {
                                if (!client.Recv((uint)Marshal.ReadInt32(bytestransfer)))
                                {
                                    client.Close();
                                    break;
                                }
                                if (data.OnRecv(client) == true)
                                {
                                    //SendLeft test
                                }
                            }
                        }
                }
            }
        }
    }

/*
//THIS IS A CODE SNIPPET THAT BELONGS TO THE ACCEPT THREAD CLASS
*/

public virtual bool Recv(uint expected_data_transfer)
        {
            IntPtr pwsabuf;
            IntPtr wsaoverlapped;
            IntPtr bytes_recv = Marshal.AllocHGlobal(sizeof(int));
            IntPtr flags = Marshal.AllocHGlobal(sizeof(int));
            Marshal.WriteInt32(flags, 0);
            set_total_transfer(SocketInvoke.FD_READ, expected_data_transfer);
            pwsabuf = recv_data.rtn_wsabuffarray(recv_data.buffer, (uint)recv_data.bufflen);
            wsaoverlapped = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SocketInvoke.WSAOVERLAPPED)));
            Marshal.StructureToPtr(overlapped, wsaoverlapped, false);
            SocketInvoke.FillMemory(wsaoverlapped, (uint)Marshal.SizeOf(typeof(SocketInvoke.WSAOVERLAPPED)), 0);
            unsafe
            {
                setoptype(SocketInvoke.FD_READ);
                if (SocketInvoke.WSARecv(Convert.ToUInt32(sock.Handle.ToInt32()), pwsabuf,
                    (uint)1, bytes_recv, flags, wsaoverlapped, (IntPtr)null) == SocketInvoke.SOCKET_ERROR)
                {
                    if (Marshal.GetLastWin32Error() != SocketInvoke.WSA_IO_PENDING)
                    {
                        return false;
                    }
                    return true;
                }
            }
            return true;
        }

也可以使用 WSASend 和 WSARecv 获得部分发送或部分接收数据吗?

4

1 回答 1

4

1)你为什么要这样做?要么使用托管异步套接字方法,要么使用 C 或 C++ 编写整个套接字代码,并通过漂亮干净的接口将其公开给托管代码。

2)我在这里猜测是因为代码不清楚,但是......每次读取或写入操作都需要一个独特的重叠结构。

3) 是的,WSASend 可能会失败并已发送部分数据,通常仅当您的数据缓冲区大于操作系统页面大小并且您达到锁定页面限制时 - 或者如果您用完了非分页池。如果发生这种情况,那么您可能无法从中恢复 - 首先通过限制您可以处理的连接数量来尽量不要陷入这种情况。期望能够处理 100,000 次连接,但要注意有多少重叠操作等待处理。

4) WSARecv 与所有 TCP 读取操作一样,可以返回 1 字节和所提供缓冲区大小之间的任何位置。如果它返回 0,则对等方已关闭连接的发送端。

5)使用 AcceptEx 并摆脱那个 Accept 线程。如果您支持从一个程序中侦听多个端口,则拥有一个专用的 Accept 线程会导致另一个线程上下文切换并且无法扩展。

于 2012-11-24T10:39:43.337 回答