C#基于TCP&UDP实现服务器与多个客户端之间的通信(客户端之间直接通信,不靠服务器端转发消息)

一、实验要求

1、Server支持多客户访问。

2、C与S之间使用TCP连接。

3.C与C之间直接通信(不是通过S传递)。

4、C与C之间直接通信既可以使用TCP,也可以使用UDP。

5、可以使用Socket,也可以使用TcpClient/UdpClient等。

二、设计思路

1、创建服务器端和客户端的主体框架:

        首先在服务器端的Windows窗体中添加5个Label控件,2个TextBox控件,2个RichTextBox控件,1个ListBox控件和1个Button控件;然后在客户端的Windows窗体中添加4个Label控件,1个TextBox控件,2个RichTextBox控件,1个ListBox控件,2个Button控件。建立程序的主要界面后,系统自动生成界面的主要窗口生成代码。在服务器端和客户端的每个按钮的代码段中,分别添加事件触发的处理代码。其中服务器端的2个TextBox用来存放IP地址和端口号,而ListBox用来显示当前连接服务器的客户端,RichTextBox用来显示双方的通信信息。


2、实现C/S的通信,使用的是TCP

      服务器端:

(1)、定义一个User类来存放客户的信息,包括名称与端口
(2)、定义一个存放User类型的List集合,用于存放所有在线客户,获取本机的IP地址,并制定一个端口号,然后创建一个TcpListener对象,并通过调用将对象的Start方法在指定的端口进行监听。
(3)、在单独的线程中,循环调用AcceptTcpClient方法接受客户端的连接请求,并根据该方法的返回结果得到与该客户端对应的TcpClient对象,然后把该对象存放到List集合中
(4)、每得到一个新的TcpClient对象,就创建一个与该客户对应的线程,在线程中与对应的客户进行通信当收到的是“Login”信息时,就把该客户的名称加到在线列表中,并把接收到的包括用户名及随机产生的端口在内的信息转发给所有在线客户;若收到的是“Logout”信息,则把该用户从在线列表中移除;若收到的是“Talk”信息,则彼此进行通信。


      客户端:

(1)、定义一个User类来存放客户的信息,包括名称与端口。
(2)、利用TcpClient的构造函数创建一个TcpClient对象,与服务器端建立 连接。
(3)、利用TcpClient对象的GetStream方法得到网络流,然后利用该网络流与服务器进行数据传输。
(4)、创建一个线程监听指定的端口,循环接收并处理服务器发送过来的消息。
(5)、完成工作后,向服务器发送关闭消息,并关闭与服务器的连接。

    3、实现C/C直接通信,使用的是UDP

(1)、在客户端,用户一旦连接上服务器,就随机产生一个端口,并把用户和此端口发给服务器,服务器再将各用户对应的名称及端口转发给所有在线用户,因此,服务器在C与C通信的过程中只是充当转发端口的角色,一旦用户得知要与之通信的用户的IP地址及端口时,就可直接通信。

(2)、在每个用户随机产生的端口进行UDP监听,当用户接收到服务器转发的其他用户端口时,创建一个线程循环接收并处理客户端发送过来的消息。
(3)、客户端与客户端发送消息时,指定想发送的那个客户端的IP地址和端口,就可以进行通信。

三、TCP+UDP通信核心代码:

服务器端:

 /// 保存连接的所有用户
   private List userList = new List();
   /// 使用的本机IP地址
   IPAddress localAddress;
   /// 监听端口
   private const int port = 51888;
   private TcpListener myListener;
   /// 是否正常退出所有连接线程 
   bool isNormalExit = false;
   IPEndPoint iep;
   IPHostEntry local;
   User user;
   TcpClient newClient;

   public Server()
   {
       InitializeComponent();
       UserNameList.HorizontalScrollbar = true;
       local = Dns.GetHostByName(Dns.GetHostName());
       iep = new IPEndPoint(local.AddressList[0], port);
       this.iPTextBox.Text = local.AddressList[0].ToString();
       this.portTextBox.Text = port.ToString();
   }

   private void start_button_Click(object sender, EventArgs e)
   { 
   }

   private void Server_Load(object sender, EventArgs e)
   {
       myListener = new TcpListener(iep);
       myListener.Start();
       AddItemToRichTextBox(string.Format("服务器已启动\n"));
       //创建一个线程监听客户端连接请求
       Thread myThread = new Thread(ListenClientConnect);
       myThread.Start();
       myThread.IsBackground = true;
       iPTextBox.Enabled = false;
       portTextBox.Enabled = false;   
   }

   /// 接收客户端连接
   private void ListenClientConnect()
   {
       newClient = null;
       while (true)
       {
           try
           {
               newClient = myListener.AcceptTcpClient();
           }
           catch
           {
               break;
           }

           //每接收一个客户端连接,就创建一个对应的线程循环接收该客户端发来的消息
           user = new User(newClient);
           Thread threadReceive = new Thread(ReceiveData);
           threadReceive.Start(user);
           userList.Add(user);
           SendMessage(string.Format("Talk,Hello I am Server!"));
           threadReceive.IsBackground = true;
       }
   }

   /// 
   /// 发送信息给所有客户
   /// 
   /// 指定发给哪个用户
   /// 信息内容
   private void SendToAllClient(User user, string message)
   {
       string command = message.Split(',')[0].ToLower();
       if (command == "login")
       {
           for (int i = 0; i < userList.Count; i++)
           {
               SendToClient(userList[i],message);
               if (userList[i].userName != user.userName)
               {
               SendToClient(user,"login,"+userList[i].userName+","+userList[i].userPort);
               }
           }
       }
       else if (command == "logout")
       {
           for (int i = 0; i < userList.Count; i++)
           {
               if (userList[i].userName != user.userName)
               {
                   SendToClient(userList[i],message);
               }
           }
       }
   }

客户端:

    private bool isExit = false;

   //存放客户端名称及端口
   private List userUdpList = new List();
   private TcpClient client;
   private BinaryReader br;
   private BinaryWriter bw;
   private UdpClient receiveUdpClient;
   private UdpClient sendUdpClient;
   IPAddress IP;
   int random_port;
   User user;

   public Client()
   {
       InitializeComponent();
       Random r = new Random((int)DateTime.Now.Ticks);
       user_textBox.Text = "User" + r.Next(100,999);
       user_listBox.HorizontalScrollbar = true;
   }

   private void Client_Load(object sender, EventArgs e)
   {
   }

   private void login_button_Click(object sender, EventArgs e)
   {
       login_button.Enabled = false;
       user_textBox.Enabled = false;
       try
       {
           client = new TcpClient(Dns.GetHostName(),51888);
           //随机产生一个端口,并在此端口进行UDP监听
           Random random = new Random();
           random_port = random.Next(60000);
           IP=Dns.GetHostByName(Dns.GetHostName()).AddressList[0];
           IPEndPoint local = new IPEndPoint(IP,random_port);
           receiveUdpClient = new UdpClient(local);
       }
       catch
       {
           MessageBox.Show("连接失败!");
           login_button.Enabled = true;
           return;
       }
       NetworkStream networkStream = client.GetStream();
       br = new BinaryReader(networkStream);
       bw = new BinaryWriter(networkStream);
       //把用户名及随机产生的端口发到服务器
       SendMessageToServer("Login," + user_textBox.Text + "," + random_port);
       //接收服务器消息的线程
       Thread threadReceive = new Thread(new ThreadStart(ReceiveFromServer));
       threadReceive.IsBackground = true;
       threadReceive.Start();
   }

四、程序运行效果图

C#基于TCP&UDP实现服务器与多个客户端之间的通信(客户端之间直接通信,不靠服务器端转发消息)_第1张图片
————————————————
版权声明:本文为CSDN博主「scau_11jkx」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/scau_11jkx/article/details/30294363

 

https://blog.csdn.net/scau_11jkx/article/details/30294363?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~first_rank_v2~rank_v25-18-30294363.nonecase

你可能感兴趣的:(服务器和多客户端通信)