c# - 如何在多线程 C# 中使用 TCP 客户端/监听器?

标签 c# tcp network-programming tcpclient tcplistener

我为我的服务器编写了这段代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Net;
    using System.Threading;
    using System.Net.Sockets;
    using System.IO;


namespace ConsoleApplication1
{
    class Program
    {
        private static bool terminate;

        public static bool Terminate
        {
            get { return terminate; }
        }

        private static int clientNumber = 0;
        private static TcpListener tcpListener;

        static void Main(string[] args)
        {
            StartServer();
            Console.Read();
        }

        private static void StartServer()
        {
            try
            {
                Console.WriteLine("Server starting...");
                tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 8000);
                terminate = false;
                tcpListener.Start();
                tcpListener.BeginAcceptTcpClient(ConnectionHandler, null);
                Console.ReadLine();

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally 
            {
                Console.WriteLine("Server stopping...");
                terminate = true;
                if (tcpListener != null)
                {
                    tcpListener.Stop();
                }
            }
        }

        private static void ConnectionHandler(IAsyncResult result)
        {
            TcpClient client = null;

            try
            {
                client = tcpListener.EndAcceptTcpClient(result);
            }
            catch (Exception)
            {
                return;
            }

            tcpListener.BeginAcceptTcpClient(ConnectionHandler, null);

            if (client!=null)
            {
                Interlocked.Increment(ref clientNumber);
                string clientName = clientNumber.ToString();
                new ClientHandler(client, clientName);
            }
        }
    }
}


   class ClientHandler
    {
        private TcpClient client;
        private string ID;

        internal ClientHandler(TcpClient client, string ID)
        {
            this.client = client;
            this.ID = ID;
            Thread thread = new Thread(ProcessConnection);
            thread.IsBackground = true;
            thread.Start();
        }

        private void ProcessConnection()
        {
            using (client)
            {
                 using (BinaryReader reader=new BinaryReader(client.GetStream()))
                 {
                     if (reader.ReadString()==Responses.RequestConnect)
                     {
                          using (BinaryWriter writer=new BinaryWriter(client.GetStream()))
                          {
                              writer.Write(Responses.AcknowledgeOK);
                              Console.WriteLine("Client: "+ID);
                              string message = string.Empty;
                              while (message!=Responses.Disconnect)
                              {
                                  try
                                  {
                                      message = reader.ReadString();
                                  }
                                  catch
                                  {
                                      continue;
                                  }
                                  if (message==Responses.RequestData)
                                  {
                                      writer.Write("Data Command Received");
                                  }
                                  else if (message==Responses.Disconnect)
                                  {
                                      Console.WriteLine("Client disconnected: "+ID);
                                  }
                                  else
                                  {
                                      Console.WriteLine("Unknown Command");
                                  }
                              }
                          }
                     }
                     else
                     {
                         Console.WriteLine("Unable to connect client: "+ID);
                     }
                 }
            }
        }
    }

   class Responses
    {
        public const string AcknowledgeOK = "OK";
        public const string AcknowledgeCancel = "Cancel";
        public const string Disconnect = "Bye";
        public const string RequestConnect = "Hello";
        public const string RequestData = "Data";
    }

此代码以多线程方式监听客户端请求。我无法理解如何区分连接到我的服务器的不同客户端以及哪个客户端正在断开连接并请求不同的命令。

我的客户端代码是:

private static void clietnRequest(string message,ref string response)
{
    using (TcpClient client = new TcpClient())
    {
        if (!client.Connected)
        {
            client.Connect(IPAddress.Parse("127.0.0.1"), 8000);
            using (NetworkStream networkStream = client.GetStream())
            {
                using (BinaryWriter writer = new BinaryWriter(networkStream))
                {
                    writer.Write(Responses.RequestConnect);
                    using (BinaryReader reader = new BinaryReader(networkStream))
                    {
                        if (reader.ReadString() == Responses.AcknowledgeOK)
                        {
                            response = Responses.AcknowledgeOK;

                        }
                    }
                }
            }
        }
    }
}

这段代码将客户端连接到服务器,但我无法再发送消息。如果客户端已连接,我希望在我的应用程序中,他可以向服务器发送命令。而不是每次都这样做,它充当服务器的新客户端。我在这里遗漏了一些东西,请指导我正确的方向。我是 c# 网络编程的新手。请帮助我改进我的代码。 Tcp Listener 和 Tcp Client 对这种情况有效还是我需要使用套接字?

最佳答案

每次发送消息后,客户端都会关闭连接,如果你想这样做没有任何问题,但你需要向服务器发送某种形式的标识,以便它可以告诉你这是不是新连接,而是旧连接第二次连接。这正是 HTTP 的内容协议(protocol)正在执行,“身份”是互联网 cookie。

如果您很少传输数据,那么第一个选项很好,但如果您经常传输数据,则需要保持连接打开。

基本上,您需要在客户端请求函数中执行连接和断开连接操作,并将打开的连接作为参数传入。

private void MainCode()
{
    using (TcpClient client = new TcpClient())
    {
        client.Connect(IPAddress.Parse("127.0.0.1"), 8000);

        while(variableThatRepresentsRunning)
        {
            //(Snip logic that gererates the message)

            clietnRequest(message, ref response, client);

            //(Snip more logic)
        }
    }
}

private static void clietnRequest(string message,ref string response, TcpClient client)
{
    if (client.Connected)
    {
        using (NetworkStream networkStream = client.GetStream())
        {
            using (BinaryWriter writer = new BinaryWriter(networkStream))
            {
                writer.Write(Responses.RequestConnect);
                using (BinaryReader reader = new BinaryReader(networkStream))
                {
                    if (reader.ReadString() == Responses.AcknowledgeOK)
                    {
                        response = Responses.AcknowledgeOK;

                    }
                }
            }
        }
    }
    else
    {
        //Show some error because the client was not connected
    }
}

通过这种方式,client对象服务器端代表客户端,每个连接的客户端都有一个实例,并且将保持与该客户端的关联,直到他断开连接。如果您想跟踪所有已连接的客户端,您需要将它们全部插入到某个集合中,例如 List<TcpClient>。 (使用 Concurrent Collection 或使用锁定,因为您是多线程的)然后您将获得所有客户端的列表(您需要让客户端自行清理,以便它们在断开连接后将自己从列表中删除) .

关于c# - 如何在多线程 C# 中使用 TCP 客户端/监听器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14252182/

相关文章:

java - 当套接字由于网络丢失而关闭时,如何获取套接字的缓冲数据?

python - python 中的多人服务器套接字

java - 如何等到 TCP 端口在 Java 中真正( native )关闭?

c# - 将现有项目添加到解决方案的效果

c# - 不能在表达式树的 lambda 中使用动态委托(delegate)参数

c# - DateTimePicker 的初始值错误

sockets - 针对低延迟和小数据包的建议 TCP 套接字设置

java - RMI 只是一种基础技术或仍然值得使用

c++ - 共享内存不在进程之间共享

c# - 使用 ASMX 并模拟方法