ReadLine 似乎在轮流连接和发送,导致问题

ReadLine seems to be taking turns around connections and sends, causing problems

我正在处理多个连接的原始模式连接中创建服务器聊天功能,以便在我的大型应用程序中讨论调试消息。它使用 StreamReader 的列表来填充连接的读取流,以允许人们在调试器网络控制台上编写文本消息。

我正在为应用程序的下一个版本实现聊天功能,因此连接的客户端可以在应用程序 运行.

时就打印的调试消息进行聊天和评论

我在 .NET Framework 4.7.2 上使用 Visual Studio 2019。当我完成聊天功能的编写时,似乎当第一个用户连接到调试器时,聊天看起来很好,因为每次他们按 Enter 时,都会出现关于它的调试消息,就像:

9/9/2019 5:57 PM (RemoteDebugger.vb:55): Debug device 192.168.1.105 connected.
Test
9/9/2019 5:57 PM (RemoteDebugger.vb:70): 192.168.1.105> Test
Test 2
9/9/2019 5:57 PM (RemoteDebugger.vb:70): 192.168.1.105> Test 2
Debugger test
9/9/2019 5:57 PM (RemoteDebugger.vb:70): 192.168.1.105> Debugger test

但是,随着时间的流逝,用户将能够很好地连接到调试器,但是聊天功能似乎在第一个用户交谈之前对其他用户不起作用,所以它的行为就像他们正在接受轮流发布他们的消息。

另外,当第二个用户说了一次或太多,而第一个用户重复回车时,第二个用户的消息似乎是在第二次按下回车或第二次发送消息后出现的。

进一步调试,我发现ReadLine好像是阻塞了,本该不打印消息就继续的,下一个连接去另一个StreamReader让消息通过正确。这意味着当我在 ReadLine 断点后恢复程序时,它不会继续,直到检测到下一条消息,如果第二个用户试图说话,断点不会触发,直到第一个用户说话.

为了清楚起见,这里是两个连接的输出:

9/9/2019 5:57 PM (RemoteDebugger.vb:55): Debug device 127.0.0.1 connected.
Hello
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 192.168.1.105> Hello
Test
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 192.168.1.105> Test
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 127.0.0.1> Hello

9/9/2019 5:58 PM (RemoteDebugger.vb:70): 192.168.1.105> 
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 127.0.0.1> Snja

9/9/2019 5:58 PM (RemoteDebugger.vb:70): 192.168.1.105> 
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 127.0.0.1> Sinmue

9/9/2019 5:58 PM (RemoteDebugger.vb:70): 192.168.1.105> 
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 127.0.0.1> Sona

9/9/2019 5:58 PM (RemoteDebugger.vb:70): 192.168.1.105> 

9/9/2019 5:58 PM (RemoteDebugger.vb:70): 127.0.0.1> Mnjad
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 192.168.1.105> 
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 127.0.0.1> kodkw
fds
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 192.168.1.105> fds
9/9/2019 5:57 PM (RemoteDebugger.vb:55): Debug device 127.0.0.1 connected.
Hello
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 192.168.1.105> Hello
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 192.168.1.105> Test
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 127.0.0.1> Hello
Snja
Sinmue
Sona
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 192.168.1.105>
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 127.0.0.1> Snja
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 192.168.1.105>
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 127.0.0.1> Sinmue
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 192.168.1.105>
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 127.0.0.1> Sona
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 192.168.1.105>
Mnjad
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 127.0.0.1> Mnjad
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 192.168.1.105>
kodkw
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 127.0.0.1> kodkw
9/9/2019 5:58 PM (RemoteDebugger.vb:70): 192.168.1.105> fds

在一个大项目中作为聊天功能的一部分的代码如下:

    Public DebugPort As Integer = 3014
    Public RDebugClient As Socket
    Public DebugTCP As TcpListener
    Public DebugDevices As New List(Of Socket)
    Public dbgConns As New List(Of StreamWriter)
    Public dbgChats As New Dictionary(Of StreamReader, String)

    Sub StartRDebugThread()
        If DebugMode Then
            Dim RDebugThread As New Thread(AddressOf StartRDebugger) With {.IsBackground = True}
            RDebugThread.Start()
        End If
    End Sub
    Sub StartRDebugger()
        'Listen to a current IP address
        DebugTCP = New TcpListener(New IPAddress({0, 0, 0, 0}), DebugPort)
        DebugTCP.Start()
        Dim RStream As New Thread(AddressOf ReadAndBroadcastAsync)
        RStream.Start()
        W(DoTranslation("Debug listening on all addresses using port {0}.", currentLang), True, ColTypes.Neutral, DebugPort)

        While Not RebootRequested
            Try
                Dim RDebugStream As NetworkStream
                Dim RDebugClient As Socket
                If DebugTCP.Pending Then
                    RDebugClient = DebugTCP.AcceptSocket
                    RDebugStream = New NetworkStream(RDebugClient)
                    dbgConns.Add(New StreamWriter(RDebugStream) With {.AutoFlush = True})
                    dbgChats.Add(New StreamReader(RDebugStream), RDebugClient.RemoteEndPoint.ToString.Remove(RDebugClient.RemoteEndPoint.ToString.IndexOf(":")))
                    DebugDevices.Add(RDebugClient)
                    Wdbg("Debug device {0} connected.", RDebugClient.RemoteEndPoint.ToString.Remove(RDebugClient.RemoteEndPoint.ToString.IndexOf(":")))
                End If
            Catch ex As Exception
                W(DoTranslation("Error in connection: {0}", currentLang), True, ColTypes.Neutral, ex.Message)
            End Try
        End While

        DebugTCP.Stop()
        dbgConns.Clear()
        Thread.CurrentThread.Abort()
    End Sub

    Sub ReadAndBroadcastAsync()
        While True
            For i As Integer = 0 To dbgChats.Count - 1
                Dim msg As String = dbgChats.Keys(i).ReadLine()
                Wdbg("{0}> {1}", dbgChats.Values(i), msg)
            Next
        End While
    End Sub

我能做什么?我还尝试了 ReadLineAsync 函数来等待它,但它没有任何改变。如果您需要任何进一步的信息,我会提供。

我终于解决了似乎轮流聊天的问题,方法是在阅读前将读取超时设置为 10 毫秒。

streamnet.ReadTimeout = 10 'Seems to have fixed it

当reader超时时,它会产生一个异常,SocketErrorCode是ReadTimeout,所以我用Try..Catch块包围了Read及其周围的所有函数,组成:

Try
    streamnet.Read(buff, 0, 65536)
    Dim msg As String = Text.Encoding.Default.GetString(buff)
    msg = msg.Replace(vbCr, vbNullChar) 'Remove all instances of vbCr (macOS newlines) } Windows hosts are affected, too, because it uses
    msg = msg.Replace(vbLf, vbNullChar) 'Remove all instances of vbLf (Linux newlines) } vbCrLf, which means (vbCr + vbLf)
    If Not msg.StartsWith(vbNullChar) Then Wdbg("{0}> {1}", ip, msg) 'Don't post message if it starts with a null character.
Catch ex As Exception
    Dim SE As SocketException = CType(ex.InnerException, SocketException)
    If Not IsNothing(SE) And Not SE.SocketErrorCode = SocketError.TimedOut Then
        Wdbg("Error from host {0}: {1}", ip, SE.SocketErrorCode.ToString)
    End If
End Try

因此,如果您打算在原始文本模式下制作一个没有客户端 运行 的聊天服务器,请使用它。