【问题标题】:Best way to handle multiple connections at the same time同时处理多个连接的最佳方式
【发布时间】:2014-03-11 22:47:47
【问题描述】:

我有一个应用程序,它监听多个连接并验证用户是否处于活动状态

我使用 WSAASyncSelect 的 1 线程套接字处理方法。

问题是有时很多用户同时连接时,有些用户没有得到回复

我认为这是因为尚未调用“发送”并且程序已收到另一个连接,因此它会再次处理新连接而忽略前一个连接。就像 WSAASyncSelect 已触发,现在它处理新连接而不是完成先前的请求。

那么如何解决这个问题呢?我试图通过在处理连接时使用零参数调用它来临时停止来自 WSAASyncSelect 的事件,直到完成它然后重新启用网络事件,但这也无济于事。

这里是处理事件的代码(接收然后解密然后比较字节然后根据列表框中的内容发送数据,即是否活跃用户)

这要求接收 FD_READ

WSAAsyncSelect s, frmMain.hwnd, 0, 0 'Disabling Notifications event

Do Until bytesRecieved = SOCKET_ERROR
    bytesRecieved = recv(wParam, buffer(Bytes), 500, 0)
    If bytesRecieved > 0 Then
        Bytes = Bytes + bytesRecieved


    ElseIf bytesRecieved = 0 Then
        Exit Sub
    End If
Loop
Call MemCopy(ByVal decryptedArrival, buffer(0), Bytes)

WSAAsyncSelect s, frmMain.hwnd, WINSOCKMSG, FD_CONNECT + FD_READ + FD_CLOSE + FD_ACCEPT + FD_WRITE

If frmMain.chkSaveLog.value = vbChecked Then
    frmMain.txtConnectionsLog.Text = frmMain.txtConnectionsLog.Text & Now & " Receiving a connection (" & wParam & ")" & vbNewLine
    AutoScroll
    If frmMain.chkAutoSave.value = vbChecked Then
        strCurrentLogLine = Now & " Receiving a connection (" & wParam & ")"
        AutoSaveLog (strCurrentLogLine)
        frmMain.cmdClearLogs.Enabled = True
    End If
End If

下面是字节的解密,然后按 ID 比较字节标识符,如 1 = 检查更新 2 - 发送用户信息等

在 Select Case 语句中,然后是发送 Api。

以及接受程序

这在接收 FD_ACCEPT 时调用

Function AcceptConnection(wParam As Long)

lpString = String(32, 0)


        AcSock = accept(wParam, sockaddress, Len(sockaddress))

        strTempIP = getascip(sockaddress.sin_addr)


        frmMain.txtConnectionsLog.Text = frmMain.txtConnectionsLog.Text & Now & " Getting a connection from IP address: " & _
        strTempIP & " (" & AcSock & ")" & vbNewLine
        AutoScroll
        If frmMain.chkAutoSave.value = vbChecked Then
         strCurrentLogLine = Now & " Getting a connection from IP address: " & strTempIP & " (" & AcSock & ")" & vbNewLine
          AutoSaveLog (strCurrentLogLine)
        End If

End Function

对于更好的性能有什么建议吗?

【问题讨论】:

  • 请出示您的实际代码。您可能对事件处理不当。
  • 感谢您的回复。我已经编辑了问题并添加了代码。
  • 那种代码不是使用WSAAsyncSelect()的正确方法。
  • 那么正确的方法是什么?我的错误在哪里?
  • 查看我刚刚发布的答案。

标签: sockets networking vb6 window connection


【解决方案1】:

您展示的内容不是使用WSAAsyncSelect() 的正确方法。尝试更多类似的方法:

创建监听套接字时:

lSock = socket(...)
bind(lSock, ...)
listen(lSock, ...)
WSAAsyncSelect lSock, frmMain.hwnd, WINSOCKMSG, FD_ACCEPT

当一个监听套接字接收到FD_ACCEPT:

Function AcceptConnection(wParam As Long)

  AcSock = accept(wParam, sockaddress, Len(sockaddress))
  If AcSock = INVALID_SOCKET Then
    Exit Sub
  End If

  WSAAsyncSelect AcSock, frmMain.hwnd, WINSOCKMSG, FD_READ + FD_CLOSE + FD_WRITE

  ...

End Function

当一个接受的客户端套接字接收到FD_READ:

Function ReadConnection(wParam As Long)

  Do
    bytesRecieved = recv(wParam, ReadBuffer(ReadBytes), 500, 0)
    If bytesRecieved = SOCKET_ERROR Then
      If WSAGetLastError() <> WSAEWOULDBLOCK Then
        Exit Sub
      End If
    ElseIf bytesRecieved = 0 Then
      Exit Sub
    Else
      ReadBytes = ReadBytes + bytesRecieved
    End If
  Loop Until bytesRecieved = SOCKET_ERROR

  ' process ReadBuffer up to ReadBytes number of bytes as needed...
  ' remove processed bytes from front of ReadBuffer and decrement ReadBytes accordingly

  ...

End Function

当一个接受的客户端套接字接收到FD_WRITE:

函数 WriteConnection(wParam As Long)

当 SendBytes > 0 bytesSent = send(wParam, SendBuffer(0), SendBytes, 0) 如果 bytesSent = SOCKET_ERROR 那么 退出子 万一 ' 从 SendBuffer 前面删除 bytesSent 字节数 ... SendBytes = SendBytes - bytesSent; 结束时

结束函数

诀窍是您需要为每个接受的客户端分配单独ReadBufferSendBuffer缓冲区。确保每次收到FD_READ 时,您仅将字节附加到触发FD_READ 的套接字的ReadBuffer,并且每次收到FD_WRITE 时,您仅从SendBufferSendBuffer 中删除字节触发FD_WRITE的套接字。

recv() 没有更多字节要读取时,根据需要处理该套接字的ReadBuffer,只删除前面的完整消息,留下不完整的消息供以后处理。

send()WSAEWOULDBLOCK 失败时,将任何未发送的字节附加到导致send() 失败的套接字的SendBuffer。当您收到一个套接字的FD_WRITE 事件时,检查该套接字的SenBuffer 并重新发送其中的所有字节,当buffer 耗尽或发生WSAEWOULDBLOCK 错误时停止。

【讨论】:

    【解决方案2】:

    非常简单且非常有效的方法是为每个传入连接分叉。这很可能需要您重新构建应用程序,但基本流程应如下所示: 1. 新的连接打开到服务器 2. 服务器接受连接并分叉 3. fork 关闭原始套接字进行侦听,因此只有父级将接受新连接 4. 然后你的魔法发生了,与原始线程分开。

    这样你就不用担心并发问题,只要你的机器可以处理所有的流量和负载,因为每个连接都是独立的。

    【讨论】:

    • 感谢您的回答,但您所说的 fork out 是什么意思?对不起,我不是英语母语人士。
    • @amaninlove Fork 是一个系统调用,可生成现有进程的副本:en.wikipedia.org/wiki/Fork_(system_call)
    • 不过,Windows 不支持分叉。这是针对的 Windows 系统。最接近的等价物是为每个接受的连接生成一个单独的线程/进程,让原始套接字只接受连接而不做任何其他事情。让每个线程/进程独立于其他线程/进程处理其客户端。
    • @RemyLebeau 如果确实 windows 是这里的目标(我没有在帖子中的任何地方注意到它),那么确实线程将是选择的解决方案。线程方法的问题在于它通常比分叉更昂贵(在我们为每个接受的连接创建一个新线程的模型中),你会更快地达到上限。
    • @Puciek:WSAASyncSelect() 是一个特定于 Windows 的函数。如果重叠 I/O 或 I/O 完成端口用于套接字 I/O,线程可以非常有效地完成。
    猜你喜欢
    • 1970-01-01
    • 2018-07-25
    • 1970-01-01
    • 2020-04-17
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-06
    相关资源
    最近更新 更多