C#多线程客户端重复连接/数据丢失

问题描述 投票:-1回答:1

我在与TCPListener和套接字建立聊天连接时遇到了一个奇怪的问题。出于某种原因,当客户端“连接”到服务器时,它将显示它们已连接两次。同样,一旦所有用户都连接好了,我会向所有用户发送一条消息。他们将回复已确认的回复并声明他们的聊天已开始。

关于我如何设置的注意事项:

  1. 根据日志显示,用户“连接”两次是第二次“一旦连接到white(true)循环,就会“连接”。
  2. 当它们将确认发送回服务器时,并不是所有数据都被发送过来。如果我在客户端上进行线程睡眠,它似乎确实可以开始工作,但是仍然不一致。

这里是代码:

服务器:

private TcpListener tcpListener;
private Thread listen;
private TcpListener tcpUser1, tcpUser2,tcpUser3;
NetworkStream User1Stream,User2Stream,User3Stream;
public event NetworkMessageStringDelegate MessageFromUser;
TcpClient client;

public void start(string ip){

    IpHostEntry host = dns.GetHostEntry(dns.GetHostName());
    IpAddress[] ip = host.AddressList;
    serverStatus = "Server started with IP of: " + ip;
    Thread.Sleep(1);

    tcpUser1 = new TcpListener(IpAddress.Any, 4001);
    listen = new Thread(new ThreadStart(() => ListenUser1(tcpUser1)));
    listen.Start();
    Thread.Sleep(1);

    tcpUser2 = new TcpListener(IpAddress.Any, 4002);
    listen = new Thread(new ThreadStart(() => ListenUser2(tcpUser2)));
    listen.Start();
    Thread.Sleep(1);

    tcpUser3 = new TcpListener(IpAddress.Any, 4003);
    listen = new Thread(new ThreadStart(() => ListenUser3(tcpUser3)));
    listen.Start();
    Thread.Sleep(1);

}

public void ListenUser3(TcpListener tmp){

    tcpListener = (TcpListener)tmp;
    Socket = "Listening for User3";
    tcpUser3.Start();

    Thread.Sleep(2);

    while(true){
        user3 = this.tcpListener.AcceptTcpClient();
        Thread user3Thread = new Thread(new ParmeterizedThreadStart(receiveUser3Data));
        user3Thread.Start(user3);
    }
}

//Mostly from MS documenation
private void receiveUser3Data(object client){
    client = (TcpClient)client;
    User3Stream = client.getStream();
    Socket = "Connected to User: " + client.Client.RemoteEndPoint.toString();
    byte[] message = new byte[4096];
    int total;

    //This is the line it will display the socket message Twice. "Connected to User...."
    while(true){
        total = 0;
        try{
            do{
                total = User3Stream.Read(message,0,4096);
            }
            while(user3.DataAvailable);
        }
        catch()
        {
            Socket = "Error State";
        }
    }
    byte[] infoPacket = new byte[total];
    Array.ConstrainedCopy(message,0,infoPacket,total);
    if(MessageFromUser3 != null){
        MessageFromUser?.Invoke(packet);
    }
}

客户:

public void ConfigureUser3(){

    try{
        socket = new Network.TCPIPClient();
        socket.ReceiveMessage() = new Newowrk.TCPIPClient.NetworkMessageStringDelgate(MessageFromserver);
        socket.SendMessage() = new Newowrk.TCPIPClient.NetworkMessageStringDelgate(sendMessage);
        userThread = new Thread(() => socket.Start("0.0.0.0),4054));
        userThread.Start();
    }
    catch(Exception ex){

    }
}

//This is where if I sleep it will send but it is still inconsident
private void SendMEssageToSever(object tmpVal){
    object[] sendMessage = tmpVal as object[];
    string tmpSendValue  = tmpVal[0].ToString();
    byte sendValue = Coonvert.ToByte(tmpVal[1]);
    packetData[0] = 0;
    packetData[1] = sendValue;
    packetData[2] = Convert.ToByte(tmpSendValue);

    socket.sendMessage = packetData;
}

private voide sendMessage(byte[] userMessage){
    try{
        if(socket){
            outputMessage.Enqueue(userMessage);
            while(outputMessage.Count > 0){
                Byte[] sendMessage = outputMessage.Dequeue();
                string message = ascII.GetString(sendMessage);
                if(socket.Connected){
                    lock(socket){
                        socket.Send(sendMessage,sendMessage.length,0);
                    }
                }
            }
        }
    }
    catch(Exception ex)
}

本质上,此代码对连接到服务器的所有用户重复。

c# tcp server tcpclient
1个回答
1
投票

TcpListener具有异步方法,例如BeginAcceptTcpClient

TcpClient.GetStream()(它是一个NetworkStream)也具有异步方法,例如BeginRead

我建议您更改服务器代码以使用这些代码,并将用户状态存储在一个类中,并将该类在Begin*End*方法之间来回传递。

您可以同时支持N个用户,而不必为每个用户重复代码。您也不必为3个连接使用3个不同的侦听器。只有一名听众,并接受这一听众的客户。其余的通过TcpClient.GetStream()

进行双向通讯

这里是一个最小的服务器示例,它侦听端口9988(仅适用于LoopBack,这意味着本地计算机)。您当然可以更改此设置。

这里没有客户示例。只有服务器。只需将代码复制/粘贴到控制台应用程序中的program.cs文件中即可。

我希望注释足以解释该代码。

我也希望这会有所帮助。

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;

class Program
{
    /// <summary>
    /// Contains the state for a client connection
    /// </summary>
    public class ClientState
    {
        public const int ReceiveBufferSize = 8192;
        // The buffer to receive in
        internal byte[] receiveBuffer = new byte[ReceiveBufferSize];

        // The TcpClient instance representing the remote end (connected client)
        public TcpClient TcpClient { get; set; }

        public byte[] GetReceiveBuffer()
        {
            return receiveBuffer;
        }
    }

    // This method is invoked when data is received from a client
    static void OnReceive(IAsyncResult asyncResult)
    {
        // The state parameter passed to the BeginRead method
        // is provided here in the asyncResult.AsyncState property
        ClientState clientState = asyncResult.AsyncState as ClientState;

        int numberOfBytesReceived = clientState.TcpClient.GetStream().EndRead(asyncResult);

        if (numberOfBytesReceived == 0)
        {
            // This means that the transmission is over
            Console.WriteLine("Client disconnect: {0}", clientState.TcpClient.Client.RemoteEndPoint);
            return;
        }

        // Now the receiveBuffer is filled with <numberOfBytesReceived> bytes received from the client.
        // Do whatever is needed here.
        Console.WriteLine("Received {0} bytes from {1}", numberOfBytesReceived, clientState.TcpClient.Client.RemoteEndPoint);
        // We are also sending some information back:
        StreamWriter streamWriter = new StreamWriter(clientState.TcpClient.GetStream());
        streamWriter.WriteLine("The server has received {0} bytes from you! Keep up the good job!", numberOfBytesReceived);
        streamWriter.Flush();

        // Begin read again
        clientState.TcpClient.GetStream().BeginRead(clientState.GetReceiveBuffer(), 0, ClientState.ReceiveBufferSize, OnReceive, clientState);
    }

    // This method is invoked when a new client connects
    static void OnConnect(IAsyncResult asyncResult)
    {
        // The state parameter passed to the BeginAcceptTcpClient method
        // is provided here in the asyncResult.AsyncState property
        TcpListener tcpListener = asyncResult.AsyncState as TcpListener;

        // Accept the TcpClient:
        TcpClient newClient = tcpListener.EndAcceptTcpClient(asyncResult);

        // Immediately begin accept a new tcp client.
        // We do not want to cause any latency for new connection requests
        tcpListener.BeginAcceptTcpClient(OnConnect, tcpListener);

        // Create the client state to store information aboutn the client connection
        ClientState clientState = new ClientState()
        {
            TcpClient = newClient
        };

        Console.WriteLine("A new client has connected. IP Address: {0}", newClient.Client.RemoteEndPoint);

        // Start receiving data from the client
        // Please note that we are passing the buffer (byte[]) of the client state
        // We are also passing the clientState instance as the state parameter
        // this state parameter is retrieved using asyncResult.AsyncState in the asynchronous callback (OnReceive)
        newClient.GetStream().BeginRead(clientState.GetReceiveBuffer(), 0, ClientState.ReceiveBufferSize, OnReceive, clientState);

        // Nothing else to do.
        // The rest of the communication process will be handled by OnReceive()
    }

    static void Main()
    {
        // Start a tcp listener
        TcpListener tcpListener = new TcpListener(IPAddress.Loopback, 9988);
        tcpListener.Start();

        // Begin accept a new tcp client, pass the listener as the state
        // The state is retrieved using asyncResult.AsyncState in the asynchronous callback (OnConnect)
        tcpListener.BeginAcceptTcpClient(OnConnect, tcpListener);

        // That's it. We don't need anything else here, except wait and see.
        Console.WriteLine("Server is listening on port 9988. Press enter to stop.");
        Console.ReadLine();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.