Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.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
Sockets 同时处理多个连接的最佳方法_Sockets_Networking_Vb6_Window_Connection - Fatal编程技术网

Sockets 同时处理多个连接的最佳方法

Sockets 同时处理多个连接的最佳方法,sockets,networking,vb6,window,connection,Sockets,Networking,Vb6,Window,Connection,我有一个应用程序,它侦听多个连接并验证用户是否处于活动状态 我使用WSAASyncSelect的单线程套接字处理方法 问题是,有时当许多用户同时连接时,一些用户没有得到回复 我认为这是因为“发送”还没有被调用,程序已经收到另一个连接,所以它会再次处理新的连接,而忽略上一个连接。像WSAASyncSelect一样,它已经触发,现在正在处理一个新的连接,而不是完成以前的请求 那么如何解决这个问题呢?我试图通过在处理连接时使用零参数调用WSAASyncSelect temporary来停止WSAASy

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

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

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

我认为这是因为“发送”还没有被调用,程序已经收到另一个连接,所以它会再次处理新的连接,而忽略上一个连接。像WSAASyncSelect一样,它已经触发,现在正在处理一个新的连接,而不是完成以前的请求

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

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

这需要接收FD\U 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语句中,后跟一个send 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

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

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


这样,您就不必担心并发问题,只要您的计算机能够处理所有流量和负载,因为每个连接都是独立的。

您所展示的不是使用
WSAAsyncSelect()
的正确方法。请尝试类似以下内容:

创建侦听套接字时:

lSock = socket(...)
bind(lSock, ...)
listen(lSock, ...)
WSAAsyncSelect lSock, frmMain.hwnd, WINSOCKMSG, FD_ACCEPT
当侦听套接字接收到
FD\u 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\u 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
函数ReadConnection(wParam尽可能长)
做
ByteReceived=recv(wParam,ReadBuffer(ReadBytes),500,0)
如果BytesReceived=SOCKET\u错误,则
如果WSAGetLastError()WSAEWOULDBLOCK,则
出口接头
如果结束
ElseIf BytesReceived=0,则
出口接头
其他的
ReadBytes=ReadBytes+接收的字节数
如果结束
循环直到字节接收=套接字错误
'处理ReadBuffer,最大为ReadBytes需要的字节数。。。
'从ReadBuffer前面删除已处理的字节,并相应地减少ReadBytes
...
端函数
当接受的客户端套接字接收到
FD\u WRITE

函数WriteConnection(wParam尽可能长)

而SendBytes>0 bytesSent=send(wParam、SendBuffer(0)、SendBytes、0) 如果bytesSent=SOCKET\u错误,则 出口接头 如果结束 '从SendBuffer前面删除字节数。。。 SendBytes=SendBytes-bytesSent; 结束时

端函数

诀窍是,您需要为每个接受的客户机分配单独的
ReadBuffer
SendBuffer
缓冲区。确保每次收到
FD\u READ
时,您仅向触发
FD\u READ
的套接字的
ReadBuffer
追加字节,每次收到
FD\u WRITE
时,您仅从触发
FD\u WRITE
的套接字的
SendBuffer
中删除字节

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


send()
WSAEWOULDBLOCK
一起失败时,将所有未发送的字节追加到导致
send()
失败的套接字的
SendBuffer
。当您收到套接字的
FD\u WRITE
事件时,请检查该套接字的
SenBuffer
,并重新发送其中的所有字节,当
缓冲区
用完或发生
WSAEWOULDBLOCK
错误时停止。谢谢您的回答,但分叉是什么意思?抱歉,我不是英语母语。@amaninlove Fork是一个系统调用,它生成现有进程的副本:不过Windows不支持forking。这是一个Windows系统的目标。最接近的等效方法是为每个接受的连接生成一个单独的线程/进程,将原始套接字保留为仅接受连接,而不执行其他操作。让每个线程/进程独立于其他线程/进程处理其客户机。@RemyLebeau如果windows确实是这里的目标(我在文章的任何地方都没有注意到),那么线程将是选择的解决方案。线程方法的问题是,它通常比分叉(在我们为每个接受的连接创建一个新线程的模型中)更昂贵,而且达到上限的速度要快得多。@Puciek:是Windows特有的函数。如果为套接字I/O使用重叠I/O或I/O完成端口,线程可以非常有效地完成。请显示实际代码。您可能处理不当。谢谢您的回复。我已经编辑了问题并添加了代码。这种代码不是使用
WSAAsyncSelect()
的正确方法。那么正确的方法是什么?我的错误在哪里?请看我刚刚发布的答案。