Sockets 同时处理多个连接的最佳方法
我有一个应用程序,它侦听多个连接并验证用户是否处于活动状态 我使用WSAASyncSelect的单线程套接字处理方法 问题是,有时当许多用户同时连接时,一些用户没有得到回复 我认为这是因为“发送”还没有被调用,程序已经收到另一个连接,所以它会再次处理新的连接,而忽略上一个连接。像WSAASyncSelect一样,它已经触发,现在正在处理一个新的连接,而不是完成以前的请求 那么如何解决这个问题呢?我试图通过在处理连接时使用零参数调用WSAASyncSelect temporary来停止WSAASyncSelect的事件,直到完成连接,然后重新启用网络事件,但这也没有帮助 以下是处理事件的代码(接收然后解密,然后比较字节,然后根据列表框中的内容(即是否为活动用户)发送数据) 这需要接收FD\U READSockets 同时处理多个连接的最佳方法,sockets,networking,vb6,window,connection,Sockets,Networking,Vb6,Window,Connection,我有一个应用程序,它侦听多个连接并验证用户是否处于活动状态 我使用WSAASyncSelect的单线程套接字处理方法 问题是,有时当许多用户同时连接时,一些用户没有得到回复 我认为这是因为“发送”还没有被调用,程序已经收到另一个连接,所以它会再次处理新的连接,而忽略上一个连接。像WSAASyncSelect一样,它已经触发,现在正在处理一个新的连接,而不是完成以前的请求 那么如何解决这个问题呢?我试图通过在处理连接时使用零参数调用WSAASyncSelect temporary来停止WSAASy
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()
的正确方法。那么正确的方法是什么?我的错误在哪里?请看我刚刚发布的答案。