winsock 连接功能仅适用于高速连接

Posted

技术标签:

【中文标题】winsock 连接功能仅适用于高速连接【英文标题】:winsock connect function is working only on high speed connection 【发布时间】:2016-07-12 07:22:52 【问题描述】:

我在我的 VB6 应用程序中使用winsock,这是我的代码:

Private Sub Form_Load()  

  With Winsock1  
    .Close  
    .RemoteHost = Hostip
    .RemotePort = port number
    .Connect
  End With
End Sub

我的问题是,当我使用高速连接 (4G) 时,连接工作正常,但使用其他连接,如 (3G,wise...),它返回以下消息:

连接尝试超时

我该如何补救?

【问题讨论】:

Check this 【参考方案1】:

听起来您需要设置更长的连接超时时间。无法直接使用控件执行此操作。以下是已停用的知识库文章中的修改示例。原始代码不包含 SO_SNDTIMEO 或 SO_RCVTIMEO 选项。

Option Explicit

' Error returned by Winsock API.
Const SOCKET_ERROR = -1

' Level number for (get/set)sockopt() to apply to socket itself.
Const SOL_SOCKET = 65535      ' Options for socket level.
Const IPPROTO_TCP = 6         ' Protocol constant for TCP.

' option flags per socket
Const SO_DEBUG = &H1&         ' Turn on debugging info recording
Const SO_ACCEPTCONN = &H2&    ' Socket has had listen() - READ-ONLY.
Const SO_REUSEADDR = &H4&     ' Allow local address reuse.
Const SO_KEEPALIVE = &H8&     ' Keep connections alive.
Const SO_DONTROUTE = &H10&    ' Just use interface addresses.
Const SO_BROADCAST = &H20&    ' Permit sending of broadcast msgs.
Const SO_USELOOPBACK = &H40&  ' Bypass hardware when possible.
Const SO_LINGER = &H80&       ' Linger on close if data present.
Const SO_OOBINLINE = &H100&   ' Leave received OOB data in line.

Const SO_DONTLINGER = Not SO_LINGER
Const SO_EXCLUSIVEADDRUSE = Not SO_REUSEADDR ' Disallow local address reuse.

' Additional options.
Const SO_SNDBUF = &H1001&     ' Send buffer size.
Const SO_RCVBUF = &H1002&     ' Receive buffer size.
Const SO_ERROR = &H1007&      ' Get error status and clear.
Const SO_TYPE = &H1008&       ' Get socket type - READ-ONLY.

' TCP Options
Const TCP_NODELAY = &H1&      ' Turn off Nagel Algorithm.

' linger structure
Private Type LINGER_STRUCT
  l_onoff As Integer          ' Is linger on or off?
  l_linger As Integer         ' Linger timeout in seconds.
End Type

'timeout structure
Private Type TIMEOUT_STRUCT
    tv_sec As Long  'seconds
    tv_usec As Long 'milliseconds
End Type

'Timeout options
Const SO_SNDTIMEO = &H1005& 'send timeout
Const SO_RCVTIMEO = &H1006& 'receive timeout

' Winsock API declares
Private Declare Function setsockopt Lib "wsock32.dll" (ByVal s As Long, ByVal level As Long, ByVal optname As Long, optval As Any, ByVal optlen As Long) As Long
Private Declare Function getsockopt Lib "wsock32.dll" (ByVal s As Long, ByVal level As Long, ByVal optname As Long, optval As Any, optlen As Long) As Long

Private Sub Command1_Click()
  ' Read all the options and present in a message box.
  Dim socket As Long

  socket = Winsock1.SocketHandle

  If socket = 0 Then
    MsgBox "No Socket"
  Else
    MsgBox "Socket Options:" & vbCrLf & _
           "    SO_DEBUG: " & CStr(GetSocketOption(socket, SOL_SOCKET, SO_DEBUG)) & vbCrLf & _
           "    SO_ACCEPTCONN: " & CStr(GetSocketOption(socket, SOL_SOCKET, SO_ACCEPTCONN)) & vbCrLf & _
           "    SO_REUSEADDR: " & CStr(GetSocketOption(socket, SOL_SOCKET, SO_REUSEADDR)) & vbCrLf & _
           "    SO_KEEPALIVE: " & CStr(GetSocketOption(socket, SOL_SOCKET, SO_KEEPALIVE)) & vbCrLf & _
           "    SO_DONTROUTE: " & CStr(GetSocketOption(socket, SOL_SOCKET, SO_DONTROUTE)) & vbCrLf & _
           "    SO_BROADCAST: " & CStr(GetSocketOption(socket, SOL_SOCKET, SO_BROADCAST)) & vbCrLf & _
           "    SO_USELOOPBACK: " & CStr(GetSocketOption(socket, SOL_SOCKET, SO_USELOOPBACK)) & vbCrLf & _
           "    SO_LINGER: " & CStr(GetSocketOption(socket, SOL_SOCKET, SO_LINGER)) & vbCrLf & _
           "    SO_OOBINLINE: " & CStr(GetSocketOption(socket, SOL_SOCKET, SO_OOBINLINE)) & vbCrLf & _
           "    SO_DONTLINGER: " & CStr(GetSocketOption(socket, SOL_SOCKET, SO_DONTLINGER)) & vbCrLf & _
           "    SO_EXCLUSIVEADDRUSE: " & CStr(GetSocketOption(socket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE)) & vbCrLf & _
           "    SO_SNDBUF: " & CStr(GetSocketOption(socket, SOL_SOCKET, SO_SNDBUF)) & vbCrLf & _
           "    SO_RCVBUF: " & CStr(GetSocketOption(socket, SOL_SOCKET, SO_RCVBUF)) & vbCrLf & _
           "    SO_ERROR: " & CStr(GetSocketOption(socket, SOL_SOCKET, SO_ERROR)) & vbCrLf & _
           "    SO_TYPE: " & CStr(GetSocketOption(socket, SOL_SOCKET, SO_TYPE)) & vbCrLf & vbCrLf & _
           "TCP Options:" & vbCrLf & _
           "    TCP_NODELAY: " & CStr(GetSocketOption(socket, IPPROTO_TCP, TCP_NODELAY))
    End If
End Sub

Private Sub Command2_Click()
  Dim lResult As Long     ' Results of 1st option.
  Dim tout As TIMEOUT_STRUCT

  If (Winsock1.Protocol = sckTCPProtocol) Then
    tout.tv_sec = 10
    tout.tv_usec = 0

    lResult = setsockopt(Winsock1.SocketHandle, SOL_SOCKET, SO_SNDTIMEO, tout, LenB(tout))

    If (lResult = SOCKET_ERROR) Then
      MsgBox "Error setting SO_SNDTIMEO option: " & Translate_DLL_Error(Err.LastDllError)
    Else
      MsgBox "SO_SNDTIMEO option set."
    End If

  End If

End Sub

Public Function GetSocketOption(lSocket As Long, lLevel As Long, lOption As Long) As Long
  Dim lResult As Long       ' Result of API call.
  Dim lBuffer As Long       ' Buffer to get value into.
  Dim lBufferLen As Long    ' len of buffer.
  Dim linger As LINGER_STRUCT

  ' Linger requires a structure so we will get that option differently.
  If (lOption <> SO_LINGER) And (lOption <> SO_DONTLINGER) Then
    lBufferLen = LenB(lBuffer)
    lResult = getsockopt(lSocket, lLevel, lOption, lBuffer, lBufferLen)
  Else
    lBufferLen = LenB(linger)
    lResult = getsockopt(lSocket, lLevel, lOption, linger, lBufferLen)
    lBuffer = linger.l_onoff
  End If

  If (lResult = SOCKET_ERROR) Then
    GetSocketOption = Err.LastDllError
  Else
    GetSocketOption = lBuffer
  End If
End Function

Private Sub Form_Load()
  Winsock1.Bind 8377      ' Set up socket enough to get nonzero socket. handle
End Sub

您可以在https://support.microsoft.com/en-us/kb/237688找到原文

【讨论】:

您好,感谢您的回复,我没有看到任何关于端口号和远程主机和连接的信息 @Nicole,正确的。我以为你已经知道该怎么做了。上面的示例显示了如何设置和读取 winsock 选项。您特别希望设置 SO_SNDTIMEO 选项,如Command2_Click 事件中所示。设置超时后,您可以继续执行其余代码。请注意,winsock 控件必须处于绑定状态(SocketHandle > 0)才能设置选项。在示例中,这在 Form_Load 事件中关闭。有关如何使用 Winsock 控件的更多详细信息,请访问 msdn.microsoft.com/en-us/library/aa733709(v=vs.60).aspx 你好,我明白代码但仍然请求超时,问题是我应该使用 8377 绑定吗?我应该在 setoption 之后还是之前连接? 这个给定的代码应该用在客户端还是服务器端? @Nicole,8377 是要绑定的本地端口。将其替换为您的端口。发送超时用于出现超时错误的 Winsock 控件。

以上是关于winsock 连接功能仅适用于高速连接的主要内容,如果未能解决你的问题,请参考以下文章

winsock2:服务器端代码调用`accept()`后如何获取已连接客户端的ipv4/ipv6地址

丢弃用于 TCP 连接的 winsock 内部缓冲区中的排队数据

密码屏蔽仅适用于 JDBC 连接器

Android Geocoder 是不是仅适用于互联网连接?

AsyncSocket 连接仅适用于家庭网络

仅适用于某些域的代理