none
[VB2005] 使用 Socket 連接不同的 Server 端, 當連接至第二個 Server 端時, 第一個連接的 Socket Connect 就會被關閉 RRS feed

  • 問題

  • 我使用 Socket 來包成一個物件, 做為 Server/Client 間傳輸資料使用, 目的是要 無論任一方開啟都能建立 Connection 以便資料傳輸
    基本上在 Socket Listen 的使用上大概沒什麼問題 , 但是當使用元件本身 Connect 至遠端時 , 單一連接都正常 , 但是若再次連接到另一個遠端
    電腦時 , 原本 Keep 第一個連線的 Socket 在一段時間後 Connection Status 就會自動變成 Close 狀態 , 本來以為是 Local Port 使用同一組的關係
     , 但是嘗試過使用不同 Port , 結果還是一樣 , 求助高手解答 , 3Q 不盡 ! 附上程式碼

    Public Function Start_to_Connect(ByVal LocalFunctionName As String) As Long

            If LocalFunctionName <> m_LocalFunctionName And m_LocalFunctionName <> "" Then
                Throw New Exception("              LocalFunctionName 已被設定為:" & m_LocalFunctionName & vbCrLf & _
                                    "與目前設定 FunctionName:" & LocalFunctionName & "不相同,FunctionName 必須一致!")
            Else
                m_LocalFunctionName = LocalFunctionName
            End If

            ' Check Remote IP
            If m_RemoteIP = "" Then
                Throw New Exception("請輸入 Connect IP!")
            End If

            ' Check Remote Port
            If m_RemotePort = "" Then
                Throw New Exception("請輸入 Connect Port!")
            End If

            Try
                ' Create the socket instance
                m_clientSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)

                ' Cet the remote IP address
                Dim ip As IPAddress = IPAddress.Parse(m_RemoteIP)
                Dim iPortNo As Integer = System.Convert.ToInt16(m_RemotePort)
                ' Create the end point
                Dim ipEnd As New IPEndPoint(ip, iPortNo)
                ' Connect to the remote host
                m_clientSocket.Connect(ipEnd)
                If m_clientSocket.Connected Then

                    'Wait for data asynchronously
                    WaitForServerData(m_LocalFunctionName)
                End If
            Catch se As SocketException
                Throw New Exception("Start_to_Connect: Connection failed, make sure the server is exist and all setting was correct! " & se.Message)
                Return Fail

            Catch ex As Exception
                Throw New Exception("Start_to_Connect:" & ex.Message)
                Return Fail
            End Try
        End Function

        Public Sub WaitForServerData(ByVal LocalFunctionName As String)
            Try
                Dim theSocPkt As New SocketPacket()

                If m_pfnCallBack Is Nothing Then
                    m_pfnCallBack = New AsyncCallback(AddressOf DataReceivedFromServer)
                End If

                theSocPkt.m_Socket = m_clientSocket
                theSocPkt.m_CurrentFunctionName = LocalFunctionName

                ' Start listening to the data asynchronously
                m_result = m_clientSocket.BeginReceive(theSocPkt.DataBuffer, 0, theSocPkt.DataBuffer.Length, SocketFlags.None, m_pfnCallBack, theSocPkt)

            Catch se As SocketException
                If se.ErrorCode = 10054 Then
                    ' Error code for Connection reset by peer

                    ' Remove the reference to the worker socket of the closed client
                    ' so that this object will get garbage collected
                    Dim socketData As SocketPacket
                    Dim i As Integer = 1

                    For Each socketData In m_workerSocketList
                        Dim tmpsocket As Socket = socketData.m_Socket
                        tmpsocket = socketData.m_Socket
                        If tmpsocket.Connected = False Then
                            m_workerSocketList.Remove(i)
                        End If
                        i = i + 1
                    Next

                    RaiseEvent Update_Connected_Count(m_workerSocketList.Count)
                Else
                    Throw New Exception("WaitForServerData: " & se.Message)
                End If
            Catch ex As Exception
                Throw New Exception("WaitForServerData: " & ex.Message)
            End Try
        End Sub

        Public Sub DataReceivedFromServer(ByVal asyn As IAsyncResult)
            Try
                Dim socketData As SocketPacket = DirectCast(asyn.AsyncState, SocketPacket)
                Dim iRx As Integer = socketData.m_Socket.EndReceive(asyn)
                Dim chars As Char() = New Char(iRx) {}
                Dim d As System.Text.Decoder = System.Text.Encoding.Default.GetDecoder
                Dim charLen As Integer = d.GetChars(socketData.DataBuffer, 0, iRx, chars, 0)
                Dim szData As String = ChartoString(chars, iRx)
                Dim m_nowServerClientID As Integer = 0
                Dim tmp() As String
                Dim info() As String

                szData = szData.Replace("<-<", "")
                szData = szData.Replace(">->", "")

                If szData Like "HandShake;Server*" Then

                    Dim myIPEndPoint As IPEndPoint = socketData.m_Socket.LocalEndPoint

                    tmp = Split(szData, ":")
                    info = Split(tmp(1), "/")
                    socketData.m_RemoteFunctionName = info(2).Trim
                    socketData.m_ClientNumber = info(3).Trim

                    If m_workerSocketList.Contains(socketData.m_RemoteFunctionName) Then
                        m_workerSocketList.Remove(socketData.m_RemoteFunctionName)
                    End If

                    m_workerSocketList.Add(socketData, socketData.m_RemoteFunctionName)

                    Dim msg As String = "<-<HandShake;ClientOK:" & m_LocalIP & "/" & m_LocalPort & "/" & m_LocalFunctionName & ">->"

                    'Use the following code to send bytes
                    Dim byData() As Byte = System.Text.Encoding.Default.GetBytes(msg.ToCharArray)
                    If socketData.m_Socket IsNot Nothing Then
                        socketData.m_Socket.Send(byData)
                    End If

                ElseIf szData Like "EstComSend:*" Then
                    Dim msg As String = ""
                    Dim tmpsocketData As SocketPacket
                    Dim myIPEndPoint As IPEndPoint

                    tmp = Split(szData, ":")
                    info = Split(tmp(1), "/")
                    tmpsocketData = m_workerSocketList(info(2).Trim)
                    If m_workerSocketList.Contains(info(2).Trim) Then
                        myIPEndPoint = tmpsocketData.m_Socket.LocalEndPoint
                        ' Send a welcome message to client
                        msg = "<-<EstComSend;OK:" & myIPEndPoint.Address.ToString & "/" & myIPEndPoint.Port & "/" & socketData.m_CurrentFunctionName & "/" & m_ConnectedIndex & ">->"

                        ' Convert the reply to byte array
                        Dim byData As Byte() = System.Text.Encoding.Default.GetBytes(msg.ToCharArray)

                        tmpsocketData.m_Socket.Send(byData)
                        RaiseEvent Received_Message(socketData.m_RemoteFunctionName, szData, socketData)
                    End If

                ElseIf szData Like "ErrorMsgSend*" Then
                    tmp = Split(szData, ":")
                    info = Split(tmp(1), "/")

                    RaiseEvent Received_Message(socketData.m_RemoteFunctionName, szData, socketData)
                    m_workerSocketList.Remove(info(0).Trim)
                    RaiseEvent Update_Connected_Count(m_workerSocketList.Count)
                    Exit Sub
                Else
                    RaiseEvent Received_Message(socketData.m_RemoteFunctionName, szData, socketData)

                End If

                RaiseEvent Update_Connected_Count(m_workerSocketList.Count)

                WaitForServerData(socketData.m_CurrentFunctionName)

            Catch generatedExceptionName As ObjectDisposedException
                System.Diagnostics.Debugger.Log(0, "1", vbLf & "DataReceivedFromServer: Remote Socket has been closed")
            Catch se As SocketException
                If se.ErrorCode = 10054 Then
                    ' Error code for Connection reset by peer

                    ' Remove the reference to the worker socket of the closed client
                    ' so that this object will get garbage collected
                    Dim socketData As SocketPacket
                    Dim i As Integer = 1

                    For Each socketData In m_workerSocketList
                        Dim tmpsocket As Socket = socketData.m_Socket
                        tmpsocket = socketData.m_Socket
                        If tmpsocket.Connected = False Then
                            m_workerSocketList.Remove(i)
                        End If
                        i = i + 1
                    Next

                    RaiseEvent Update_Connected_Count(m_workerSocketList.Count)
                Else
                    Throw New Exception("DataReceivedFromServer: " & se.Message)
                End If

            Catch ex As Exception
                Throw New Exception("DataReceivedFromServer: " & ex.Message)

            End Try
        End Sub

    Public Class SocketPacket
            Public m_Socket As System.Net.Sockets.Socket
            Public m_CurrentFunctionName As String
            Public m_RemoteFunctionName As String
            Public m_ClientNumber As Integer
            ' Buffer to store the data sent by the client
            Public DataBuffer As Byte() = New Byte(2047) {}

            ' Constructor which takes a Socket and a client number
            Public Sub New(ByVal Socket As System.Net.Sockets.Socket, ByVal ClientNumber As Integer, ByVal currentFunctionName As String, ByVal RemoteFunctionName As String)
                m_Socket = Socket
                m_ClientNumber = ClientNumber
                m_CurrentFunctionName = currentFunctionName
                m_RemoteFunctionName = RemoteFunctionName
            End Sub

            ' Constructor which takes a Socket and a client number
            Public Sub New(ByVal Socket As System.Net.Sockets.Socket, ByVal currentFunctionName As String, ByVal RemoteFunctionName As String)
                m_Socket = Socket
                m_CurrentFunctionName = currentFunctionName
                m_RemoteFunctionName = RemoteFunctionName
            End Sub

            Public Sub New()

            End Sub
        End Class

    2009年10月24日 上午 03:43

解答

  • 因為你的m_mainsocket都是指向同一個參考位址, 如心冷大所言.
    所以你必須在呼叫BeginAccept之前產生一個新的Server端Socket的Instance, 並且將這Socket當做參數傳遞給OnClientConnect這個非同步的委派方法.
    學而不思則罔, 思而不學則殆.
    如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    如果你自私地不肯回饋與分享,那別人為何要花時間回答你的問題?
    2009年10月25日 上午 02:13
    版主

所有回覆

  • 你的m_clientSocket是全域的嗎? 可否告知m_clientSocket是如何宣告? 在哪裡宣告 ?
    另一個是當你主程式連接兩個不同Server時, 你的自訂元件是建立兩個不同的Instance還是同一個 ?


    學而不思則罔, 思而不學則殆.
    如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    如果你自私地不肯回饋與分享,那別人為何要花時間回答你的問題?
    2009年10月24日 上午 04:05
    版主
  • 沒看到你 Listen / Accept 的程式碼片段。
    通常沒有另開一個 Socket 來做 Accept ,導致原先 Socket 被中斷來做 Accept 。

    請你適當精簡你的程式碼並以分隔線區分 Server/Client 兩段落,問題既然發生在連線,請以連線部分程式碼為主,輔助性質的檢查片斷省略,這麼長的程式碼片斷我連看都不想看,直接搜尋 Listen/Accept 來查找問題,沒看到目標就直接跳過內容了。


    論壇是網友平等互助 保證解答請至 微軟技術支援服務
    提問時,錯誤情境描述與錯誤訊息很重要,情境描述包含你做了什麼,預期的結果與實際發生的結果。一個最爛的問法範例:「我的電腦電腦怎麼不能開機?」誰知道你家是不是沒電還是你根本找不到電源鈕。
    2009年10月24日 上午 09:10
  • 不好意思,這是縮短之後的 code,打擾大家的眼睛了!!
     
     Public Class My_Socket
        Private m_ConnectedIndex As Integer = 0
        Private m_mainSocket As Socket
        Public m_clientSocket As Socket
        Private m_workerSocketList As New Collection
        Public pfnWorkerCallBack As AsyncCallback
        Public m_pfnCallBack As AsyncCallback
        Private m_result As IAsyncResult
        Public Event Received_Message(ByVal FunctionName As String, ByVal msg As String, ByVal SocketInfo As
                                                      SocketPacket)

        '-------------------------------------------------   Server Side Function   -------------------------------------------------

        Public Function Start_to_Listen(ByVal LocalFunctionName As String) As Long
            Dim port As Integer = System.Convert.ToInt32(m_LocalPort)
            Dim ipLocal As New IPEndPoint(IPAddress.Parse(m_LocalIP), port)
           
            m_mainSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            m_mainSocket.Bind(ipLocal)
            m_mainSocket.Listen(4)

            Dim theSocPkt As New SocketPacket(m_mainSocket, m_LocalFunctionName, "")

            m_mainSocket.BeginAccept(New AsyncCallback(AddressOf OnClientConnect), theSocPkt)

            Return OK
        End Function

        Public Sub OnClientConnect(ByVal asyn As IAsyncResult)
            Dim workerSocket As Socket = m_mainSocket.EndAccept(asyn)
            Dim socketData As New SocketPacket

            Interlocked.Increment(m_ConnectedIndex)
            socketData.m_ClientNumber = m_ConnectedIndex
            socketData.m_Socket = workerSocket

            Dim myIPEndPoint As IPEndPoint = workerSocket.LocalEndPoint
            Dim byData As Byte() = System.Text.Encoding.Default.GetBytes(msg.ToCharArray)

            m_workerSocketList.Add(socketData, m_ConnectedIndex.ToString)

            WaitForClientData(workerSocket, m_ConnectedIndex, "")
            m_mainSocket.BeginAccept(New AsyncCallback(AddressOf OnClientConnect), Nothing)
        End Sub

        Public Sub WaitForClientData(ByVal soc As System.Net.Sockets.Socket, ByVal clientNumber As Integer, ByVal      
                                                  RemoteFunctionName As String)
            Dim theSocPkt As New SocketPacket(soc, clientNumber, m_LocalFunctionName, RemoteFunctionName)
            If pfnWorkerCallBack Is Nothing Then
                pfnWorkerCallBack = New AsyncCallback(AddressOf DataReceivedFromClient)
            End If

            soc.BeginReceive(theSocPkt.DataBuffer, 0, theSocPkt.DataBuffer.Length, SocketFlags.None, pfnWorkerCallBack,                                   theSocPkt)
        End Sub

        Public Sub DataReceivedFromClient(ByVal asyn As IAsyncResult)
            Dim socketData As SocketPacket = DirectCast(asyn.AsyncState, SocketPacket)
            Dim tmpIPEndPoint As IPEndPoint
            Dim iRx As Integer = socketData.m_Socket.EndReceive(asyn)
            Dim chars As Char() = New Char(iRx) {}
            Dim d As System.Text.Decoder = System.Text.Encoding.Default.GetDecoder()
            Dim charLen As Integer = d.GetChars(socketData.DataBuffer, 0, iRx, chars, 0)
            Dim szData As String = ChartoString(chars, iRx)
           
            tmpIPEndPoint = socketData.m_Socket.RemoteEndPoint 
            RaiseEvent Received_Message(socketData.m_RemoteFunctionName, szData, socketData)
            WaitForClientData(socketData.m_Socket, socketData.m_ClientNumber, socketData.m_RemoteFunctionName)
        End Sub

        Public Function Send_Message(ByVal FunctionName As String, ByVal msg As String) As Long
            If m_workerSocketList.Contains(FunctionName) Then
                Dim socketData As SocketPacket = m_workerSocketList(FunctionName)
                Dim workerSocket As Socket = socketData.m_Socket
                Dim myIPEndPoint As IPEndPoint = workerSocket.LocalEndPoint
                ' Convert the reply to byte array
                Dim byData As Byte() = System.Text.Encoding.Default.GetBytes(msg.ToCharArray)
                workerSocket.Send(byData)
            End If
            Return OK
        End Function

        '-------------------------------------------------   Client Side Function   -------------------------------------------------

        Public Function Start_to_Connect(ByVal LocalFunctionName As String) As Long
            m_clientSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)

            Dim ip As IPAddress = IPAddress.Parse(m_RemoteIP)
            Dim iPortNo As Integer = System.Convert.ToInt16(m_RemotePort)

            Dim ipEnd As New IPEndPoint(ip, iPortNo)
            ' Connect to the remote host
            m_clientSocket.Connect(ipEnd)
            If m_clientSocket.Connected Then

                WaitForServerData(m_LocalFunctionName)
            End If
            Return OK
        End Function

        Public Sub WaitForServerData(ByVal LocalFunctionName As String)
            Dim theSocPkt As New SocketPacket()

            If m_pfnCallBack Is Nothing Then
                m_pfnCallBack = New AsyncCallback(AddressOf DataReceivedFromServer)
            End If

            theSocPkt.m_Socket = m_clientSocket
            theSocPkt.m_CurrentFunctionName = LocalFunctionName
            m_result = m_clientSocket.BeginReceive(theSocPkt.DataBuffer, 0, theSocPkt.DataBuffer.Length,        
                                                                        SocketFlags.None, m_pfnCallBack, theSocPkt)
        End Sub

        Public Sub DataReceivedFromServer(ByVal asyn As IAsyncResult)
            Try
                Dim socketData As SocketPacket = DirectCast(asyn.AsyncState, SocketPacket)
                Dim iRx As Integer = socketData.m_Socket.EndReceive(asyn)
                Dim chars As Char() = New Char(iRx) {}
                Dim d As System.Text.Decoder = System.Text.Encoding.Default.GetDecoder
                Dim charLen As Integer = d.GetChars(socketData.DataBuffer, 0, iRx, chars, 0)
                Dim szData As String = ChartoString(chars, iRx)

                RaiseEvent Received_Message(socketData.m_RemoteFunctionName, szData, socketData)

                WaitForServerData(socketData.m_CurrentFunctionName)
        End Sub

        '-------------------------------------------------   Class SocketPacket   -------------------------------------------------

        Public Class SocketPacket
            Public m_Socket As System.Net.Sockets.Socket
            Public m_CurrentFunctionName As String
            Public m_RemoteFunctionName As String
            Public m_ClientNumber As Integer
            ' Buffer to store the data sent by the client
            Public DataBuffer As Byte() = New Byte(2047) {}

            ' Constructor which takes a Socket and a client number
            Public Sub New(ByVal Socket As System.Net.Sockets.Socket, ByVal ClientNumber As Integer, ByVal
                                    currentFunctionName As String, ByVal RemoteFunctionName As String)
                m_Socket = Socket
                m_ClientNumber = ClientNumber
                m_CurrentFunctionName = currentFunctionName
                m_RemoteFunctionName = RemoteFunctionName
            End Sub

            ' Constructor which takes a Socket and a client number
            Public Sub New(ByVal Socket As System.Net.Sockets.Socket, ByVal currentFunctionName As String, ByVal   
                                    RemoteFunctionName As String)
                m_Socket = Socket
                m_CurrentFunctionName = currentFunctionName
                m_RemoteFunctionName = RemoteFunctionName
            End Sub

            Public Sub New()

            End Sub
        End Class
    End Class

    2009年10月24日 下午 04:09
  • 因為你的m_mainsocket都是指向同一個參考位址, 如心冷大所言.
    所以你必須在呼叫BeginAccept之前產生一個新的Server端Socket的Instance, 並且將這Socket當做參數傳遞給OnClientConnect這個非同步的委派方法.
    學而不思則罔, 思而不學則殆.
    如果你一直都看不懂、不想學習看懂、抗拒看懂MSDN Library的話,那你最好放棄想要寫好程式這件事
    如果你自私地不肯回饋與分享,那別人為何要花時間回答你的問題?
    2009年10月25日 上午 02:13
    版主
  • 你可以先看一下既有討論:
    http://social.msdn.microsoft.com/Search/zh-TW/?Refinement=112&query=EndAccept%20BeginAccept


    論壇是網友平等互助 保證解答請至 微軟技術支援服務
    提問時,錯誤情境描述與錯誤訊息很重要,情境描述包含你做了什麼,預期的結果與實際發生的結果。一個最爛的問法範例:「我的電腦電腦怎麼不能開機?」誰知道你家是不是沒電還是你根本找不到電源鈕。
    2009年10月25日 上午 03:44