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

来源:互联网 发布:提高python 运行效率 编辑:程序博客网 时间:2024/05/07 17:29

一. 实验要求

1.Server支持多客户访问

2.CS之间使用TCP连接

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

4.CC之间直接通信既可以使用TCP,也可以使用UDP

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

二. 设计思路

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

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

服务器端:

a)定义一个User类来存放客户的信息,包括名称与端口。

b)定义一个存放User类型的List集合,用于存放所有在线客户,获取本机的IP地址,并制定一个端口号,然    后创建一个TcpListener对象,并通过调用将对象的Start方法在指定的端口进行监听。

c)在单独的线程中,循环调用AcceptTcpClient方法接受客户端的连接请求,并根据该方法的返回结果得到    与该客户端对应的TcpClient对象,然后把该对象存放到List集合中。

d)每得到一个新的TcpClient对象,就创建一个与该客户对应的线程,在线程中与对应的客户进行通信;当    收到的是“Login”信息时,就把该客户的名称加到在线列表中,并把接收到的包括用户名及随机产生的    端口在内的信息转发给所有在线客户;若收到的是“Logout”信息,则把该用户从在线列表中移除;若    收到的是“Talk”信息,则彼此进行通信。

客户端:

a)定义一个User类来存放客户的信息,包括名称与端口。

b)利用TcpClient的构造函数创建一个TcpClient对象,与服务器端建立 连接。

c)利用TcpClient对象的GetStream方法得到网络流,然后利用该网络流 与服务器进行数据传输。

d)创建一个线程监听指定的端口,循环接收并处理服务器发送过来的消 息。

e)完成工作后,向服务器发送关闭消息,并关闭与服务器的连接。

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

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

b)在每个用户随机产生的端口进行UDP监听,当用户接收到服务器转发的其他用户端口时,创建一个线程    循环接收并处理客户端发送过来的消息。

c)客户端与客户端发送消息时,指定想发送的那个客户端的IP地址和端口,就可以进行通信。

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

  服务器端:
   /// <summary>保存连接的所有用户</summary>
   private List<User> userList = new List<User>();
   /// <summary>使用的本机IP地址</summary>
   IPAddress localAddress;
   /// <summary>监听端口</summary>
   private const int port = 51888;
   private TcpListener myListener;
   /// <summary>是否正常退出所有连接线程 </summary>
   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;
  
   }
   /// <summary>接收客户端连接</summary>
   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;
   }
   }
   /// <summary>
   /// 发送信息给所有客户
   /// </summary>
   /// <param name="user">指定发给哪个用户</param>
   /// <param name="message">信息内容</param>

   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<User> userUdpList = new List<User>();
   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();
   }

四.程序运行效果图


0 0