我有一个应用程序,它可以侦听多个连接并验证用户是否处于活动状态
我使用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 - 发送用户信息等
在发送Api之后的Select Case语句中。
接受程序
这要求收到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
对于更好的表现有什么建议吗?
答案 0 :(得分:2)
您展示的内容不是使用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&gt; 0 bytesSent = send(wParam,SendBuffer(0),SendBytes,0) 如果bytesSent = SOCKET_ERROR那么 退出子 万一 &#39;从SendBuffer前面删除bytesSent字节数... SendBytes = SendBytes - bytesSent; 结束时
结束功能
诀窍是您需要为每个接受的客户端分配单独的 ReadBuffer
和SendBuffer
缓冲区。确保每次收到FD_READ
时,您只将字节附加到触发ReadBuffer
的套接字的FD_READ
,并且每次收到FD_WRITE
时,您要删除仅来自触发SendBuffer
的套接字的FD_WRITE
的字节。
当recv()
没有更多字节需要阅读时,根据需要处理该套接字ReadBuffer
,只删除前面的完整邮件,留下不完整的邮件供以后处理。
当send()
与WSAEWOULDBLOCK
失败时,将任何未发送的字节附加到导致SendBuffer
失败的套接字的send()
。当您收到套接字的FD_WRITE
事件时,请检查该套接字SenBuffer
并重新发送其中的所有字节,在buffer
耗尽或{{1}时停止发生错误。
答案 1 :(得分:0)
非常简单且非常有效的方法是为每个传入连接分叉。这很可能需要您重新构建应用程序,但基本流程应如下所示: 1.向服务器打开新连接 2.服务器接受连接并分叉 3. fork关闭原始套接字以进行侦听,因此只有父级接受新连接 然后你的魔法发生了,与原始线程分开。
这样您就不必担心并发问题,只要您的计算机可以处理所有流量和负载,因为每个连接都是独立的。