socket 通讯 并发 问题

时间:2022-02-25 15:34:50
客户端通过socket与主机进行通讯,如果多个用户同时向主机发送数据,则返回结果是错误的,网上查了一些资料,说是数据并发的问题,现在我改怎样改进,才能不致数据错误。
服务端是UNIX C,客户端是vb.net,现把客户端代码贴出来,大家看我看下,该如何改进,才能满足要求。
客户端代码:


Public Class StateObject
    'SOCKET ID
    Public workSocket As Socket = Nothing
    '可接收数据大小
    Public Const BufferSize As Integer = 1024 * 512
    'Public Const BufferSize As Integer = 1024
    '接收数据缓冲区
    Public buffer(BufferSize) As Byte
    '接收数据
    Public sb As New StringBuilder
End Class

Public Class ESA_TCP_CLIENT

    '处理结果
    Public Enum ExSocketState
        ExSocketSucceed = 0     '处理成功
        ExSocketNoConnect = 1   '主机无连接
        ExSocketUnKnown = 2     '未知状态(主机未响应)
        ExSocketAllUsed = 3     '无可用端口(没有空闲状态的端口)
    End Enum

    Private connectDone As New ManualResetEvent(False)
    Private sendDone As New ManualResetEvent(False)
    Private receiveDone As New ManualResetEvent(False)
    Private response As String = String.Empty

    '超时时间(毫秒)
    Private iTimeOutM As Integer
    Private isConn As Boolean = False

    Public Function SendTo(ByVal strHostIP As String, _
                           ByVal iPort As Integer, _
                           ByVal iTimeout As Integer, _
                           ByVal strSend As String, _
                           ByRef strRecv As String, _
                           ByVal Merch_ID As String, _
                           ByVal ULNm As String) As ExSocketState   '增加Merch_ID,ULNm zfy 2011-1-6 用于生成Merch_ID.txt文件
        On Error GoTo go_Err
        Dim ipHostInfo As IPHostEntry = Dns.GetHostEntry(strHostIP)
        Dim ipAddress As IPAddress = ipHostInfo.AddressList(0)
        Dim remoteEP As New IPEndPoint(ipAddress, iPort)
        Dim client As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
        strRecv = ""
        If iTimeout > 0 And iTimeout < 200000 Then   '1000000
            iTimeOutM = iTimeout
        Else
            iTimeOutM = 5000    '5000
        End If
        SendTo = ExSocketState.ExSocketUnKnown
        client.BeginConnect(remoteEP, New AsyncCallback(AddressOf ConnectCallback), client)
        Dim s2 = Now()
        If connectDone.WaitOne(iTimeOutM, False) = True Then
            If isConn = True Then
                Send(client, strSend, Merch_ID, ULNm)  '增加Merch_ID zfy 2011-1-6 用于生成Merch_ID.txt文件
                If sendDone.WaitOne(iTimeOutM, False) = True Then
                    If isConn = True Then
                        '由于测试中出现异步通讯时接收主机报文有报文不完整情况
                        '所以在此处修改为在连接、发送时采用异步方式,而接收数据时采用同步方式
                        '此处还需要考虑同步接收的超时机制,暂略
                        Dim state As New StateObject
                        state.workSocket = client
                        If Receive(state, Merch_ID, ULNm) Then    '增加Merch_ID,ULNm zfy 2011-1-6 用于生成Merch_ID.txt文件
                            'If Receive() Then
                            strRecv = state.sb.ToString()
                            If strRecv.Length > 10 Then
                                strRecv = strRecv.Substring(10, strRecv.Length - 10)
                            Else
                                strRecv = ""
                            End If
                            SendTo = ExSocketState.ExSocketSucceed
                        Else
                            SendTo = ExSocketState.ExSocketUnKnown
                        End If
                    Else
                        SendTo = ExSocketState.ExSocketUnKnown
                    End If
                    Else
                        SendTo = ExSocketState.ExSocketUnKnown
                    End If
            Else
                SendTo = ExSocketState.ExSocketNoConnect
            End If
        Else
            SendTo = ExSocketState.ExSocketNoConnect
        End If
go_Exit:
        If client.Connected = True Then
            client.Shutdown(SocketShutdown.Both)
        End If
        client.Close()
        Exit Function
go_Err:
        GoTo go_Exit
    End Function

    Private Sub ConnectCallback(ByVal ar As IAsyncResult)
        On Error GoTo go_Err
        Dim client As Socket = TryCast(ar.AsyncState, Socket)
        If client IsNot Nothing Then
            client.EndConnect(ar)
            isConn = client.Connected
        Else
            isConn = False
        End If
        connectDone.Set()
go_Exit:
        Exit Sub
go_Err:
        isConn = False
        connectDone.Set()
        GoTo go_Exit
    End Sub

    Private Function Receive(ByRef state As StateObject, ByVal Merch_ID As String, ByVal ULNm As String) As Boolean    '增加Merch_ID,ULNm zfy 2011-1-6 用于生成Merch_ID.txt文件
        On Error GoTo go_Err
        Dim readStream As New NetworkStream(state.workSocket)
        Dim blnReturn As Boolean = False
        Dim intRcv As Integer
        Dim strTmp As String = ""
        state.buffer.Clear(state.buffer, 0, StateObject.BufferSize)
        readStream.ReadTimeout = iTimeOutM
        intRcv = readStream.Read(state.buffer, 0, StateObject.BufferSize)
        Do While intRcv > 0
            strTmp = Encoding.Default.GetString(state.buffer, 0, intRcv)
            state.sb.Append(strTmp)

            If ConfigurationManager.AppSettings("g_IsDebug") = "1" Then
                Dim FileToWrite As System.IO.FileStream = System.IO.File.Create("D:\程序\ESA_OPR_New\debug\Receive" + Merch_ID + "_" + ULNm + ".txt")
                Dim rByte() As Byte = Encoding.Default.GetBytes(state.sb.ToString.ToCharArray)
                FileToWrite.Write(rByte, 0, rByte.Length)
                FileToWrite.Close()
                FileToWrite = Nothing
            End If

            '不按照报文头字符长度来取报文体字段,因为在测试中发现WINDOWS与UNIX在中文和特殊字符的长度判断上不相同
            '所以采用查找结束符号的方式来判断报文尾
            If InStr(strTmp, "</PACKAGE>") > 0 Then
                Exit Do
            Else
                state.buffer.Clear(state.buffer, 0, StateObject.BufferSize)
                intRcv = readStream.Read(state.buffer, 0, StateObject.BufferSize)
            End If
        Loop
        blnReturn = True
        readStream = Nothing
go_Exit:
        Return blnReturn
        Exit Function
go_Err:
        blnReturn = False
        GoTo go_Exit
    End Function


    Private Sub Send(ByVal client As Socket, ByVal data As String, ByVal Merch_ID As String, ByVal ULNm As String)   '增加Merch_ID,ULNm 2010-1-6 zfy
        On Error GoTo go_Err

        '2011-7-14 解决中文字符编码数量问题
        Dim strLen As Integer
        For n = 1 To data.Length
            If AscW(Mid(data, n, 1)) > 256 Then strLen = strLen + 2 Else strLen = strLen + 1
        Next
        data = Format(strLen, "0000000000") & data
        If ConfigurationManager.AppSettings("g_IsDebug") = "1" Then
            Dim FileToWrite As System.IO.FileStream = System.IO.File.Create("D:\程序\ESA_OPR_New\debug\Send" + Merch_ID + "_" + ULNm + ".txt")
            'Dim rByte() As Byte = Encoding.Default.GetBytes(data.ToCharArray)
            Dim rByte() As Byte = Encoding.Default.GetBytes(data.ToCharArray)
            FileToWrite.Write(rByte, 0, rByte.Length)
            FileToWrite.Close()
            FileToWrite = Nothing
        End If

        Dim byteData As Byte() = Encoding.Default.GetBytes(data)         '2011-7-15 zfy 解决报文发送乱码问题
        client.BeginSend(byteData, 0, byteData.Length, 0, New AsyncCallback(AddressOf SendCallback), client)
go_Exit:
        Exit Sub
go_Err:
        GoTo go_Exit
    End Sub

    Private Sub SendCallback(ByVal ar As IAsyncResult)
        On Error GoTo go_Err
        Dim client As Socket = TryCast(ar.AsyncState, Socket)
        If client IsNot Nothing Then
            isConn = True
            Dim bytesSent As Integer = client.EndSend(ar)
        Else
            isConn = False
        End If
        sendDone.Set()
go_Exit:
        Exit Sub
go_Err:
        isConn = False
        sendDone.Set()
        GoTo go_Exit
    End Sub

End Class


17 个解决方案

#1


没人知道吗

#2


这里的人气这么低

#3


引用 2 楼 zhangfengyi 的回复:
这里的人气这么低

因为这里是 VB6.0,不是 vb.net

#4


网络编程,socket应该是一样的

#5


你的问题还需要看下服务器端的接受数据的策略,单从客户端无法找到原因的

#6


高手有没有什么好的解决方案

#7


"如果多个用户同时向主机发送数据,则返回结果是错误"

这意味着单个用户访问是没问题的,这意味着单个用户发送是没问题的,看你的代码需要连接,说明是TCP/IP协议,因为每一个TCP/IP协议都是通过一个套接字连接,一个套接字获取或发送数据,如果单个的成功了,也就意味着客户端应该是没问题的,所以从通讯技术上,我认为问题应该出在服务器那边,而不是客户端。

但如果问题出在握手协议或具体数据上,除非你给出握手协议标准和相关算法,否则别人也很难帮你猜问题在哪。还有,你的是一个类,至于出问题的是在过程调用?数据算法?还是握手协议?都很难估计。但单纯以问题出现的情况来看,服务器端的嫌疑最大,你这问题看来还要自己琢磨一下,因为具体的问题出在哪很难帮你去猜。

#8


哪里有socket通讯教程,看一看,对socket通讯实在不懂,网上搜了一下,没有合适的

#9


向高手们学习了!

#10


该回复于2011-10-31 14:40:40被版主删除

#11


该回复于2011-11-01 13:20:29被版主删除

#12


提供一种思路,能不能让它们排队先来后到,通过数据排序

#13


7楼学习

#14


并发是否有个队列问题?

并发数量多了,这个队列之外的请求就被丢弃了?就像DDOS一样.

#15


我想你的服务端处理数据需要时间而客户端发送间隔太短,socket被挂起无法响应新的请求
可能与你的服务端程序有关。

网络并发是一个很有意思的课题。

#16


如果第一个查询有100条数据,需要用时20秒,第二个查询有20条数据,需要用时10秒,则肯定会出错,第一个查询显示没有查到(实际是有数据的100条),第二个查询返回的是第一个查询的结果,也就是两个结果正好相反.如果先查20条数据用时10秒,后查100条数据用时20秒的则两个查询都能返回正确的结果,这是什么原因

#17


该回复于2012-01-19 09:12:36被版主删除

#1


没人知道吗

#2


这里的人气这么低

#3


引用 2 楼 zhangfengyi 的回复:
这里的人气这么低

因为这里是 VB6.0,不是 vb.net

#4


网络编程,socket应该是一样的

#5


你的问题还需要看下服务器端的接受数据的策略,单从客户端无法找到原因的

#6


高手有没有什么好的解决方案

#7


"如果多个用户同时向主机发送数据,则返回结果是错误"

这意味着单个用户访问是没问题的,这意味着单个用户发送是没问题的,看你的代码需要连接,说明是TCP/IP协议,因为每一个TCP/IP协议都是通过一个套接字连接,一个套接字获取或发送数据,如果单个的成功了,也就意味着客户端应该是没问题的,所以从通讯技术上,我认为问题应该出在服务器那边,而不是客户端。

但如果问题出在握手协议或具体数据上,除非你给出握手协议标准和相关算法,否则别人也很难帮你猜问题在哪。还有,你的是一个类,至于出问题的是在过程调用?数据算法?还是握手协议?都很难估计。但单纯以问题出现的情况来看,服务器端的嫌疑最大,你这问题看来还要自己琢磨一下,因为具体的问题出在哪很难帮你去猜。

#8


哪里有socket通讯教程,看一看,对socket通讯实在不懂,网上搜了一下,没有合适的

#9


向高手们学习了!

#10


该回复于2011-10-31 14:40:40被版主删除

#11


该回复于2011-11-01 13:20:29被版主删除

#12


提供一种思路,能不能让它们排队先来后到,通过数据排序

#13


7楼学习

#14


并发是否有个队列问题?

并发数量多了,这个队列之外的请求就被丢弃了?就像DDOS一样.

#15


我想你的服务端处理数据需要时间而客户端发送间隔太短,socket被挂起无法响应新的请求
可能与你的服务端程序有关。

网络并发是一个很有意思的课题。

#16


如果第一个查询有100条数据,需要用时20秒,第二个查询有20条数据,需要用时10秒,则肯定会出错,第一个查询显示没有查到(实际是有数据的100条),第二个查询返回的是第一个查询的结果,也就是两个结果正好相反.如果先查20条数据用时10秒,后查100条数据用时20秒的则两个查询都能返回正确的结果,这是什么原因

#17


该回复于2012-01-19 09:12:36被版主删除