c# UDP/TCP协议简单实现(简单聊天工具)

长时间没有摸这两个协议,写个代码温习下

下面是界面

【服务器界面】

【登陆界面】

【好友列表界面(我登陆了2个)】

【聊天界面】

下面大致讲解下用到的内容

1、用户登陆于服务器通信用到的tcp协议,服务器接收到用户登陆信息(包括ip,端口,用户名等)后,返回已经登陆的用户列表信息(包括ip,端口,用户名等)给这个用户,同时服务器使用Udp协议向已经登陆的用户发送最新用户列表(包括ip,端口,用户名等)用于更新用户列表

2、用户登陆成功后展示好友列表,并启动udp协议的监听(叫监听似乎不太合适,暂且这么叫吧 形象),用以接收好友发来的消息和服务器返回的好友信息(1中提到的发送用户列表信息)

3、关于聊天有被动接收到消息和主动发送消息

先说主动发送消息吧:双击列表的某个好友打开聊天窗口,然后发送内容,通过udp协议向好友发送信息

被动接收消息:当2中提到的udp监听器接收到消息,则打开聊天窗口,并显示信息

4、用户退出时想服务器发送数据退出,用到的tcp协议,服务器接到到信息,更新在线用户列表并向其他用户发送用户最新列表进行更新(用到udp协议)

口才不行,写的有点乱

下面上代码解释下

先来服务器代码,服务器我使用了控制台程序

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Text;
  4 using System.Net.Sockets;
  5 using System.Threading;
  6 using System.Net;
  7 using System.IO;
  8 
  9 namespace QQServer
 10 {
 11     class Program
 12     {
 13         public static List<string> userlist = new List<string>();
 14         static TcpListener tl;
 15         static NetworkStream ns;
 16         static void Main(string[] args)
 17         {
 18             //声明监听对象
 19 
 20             //声明网络流
 21 
 22             //IPEndPoint ip=new IPEndPoint(
 23             tl = new TcpListener(12345);
 24             tl.Start();
 25             Console.WriteLine("TcpListener Star");
 26             //开启线程
 27             Thread th = new Thread(new ThreadStart(listen));
 28             th.IsBackground = true;
 29             th.Start();
 30             while (true)
 31             {
 32                 string index = Console.ReadLine();
 33                 if (index == "exit")
 34                     break;
 35              
 36             }
 37 
 38         }
 39         private static void listen()
 40         {
 41             Console.WriteLine("TcpListenering...");
 42             while (true)
 43             {
 44                 //获得响应的Socket
 45                 Socket sock = tl.AcceptSocket();
 46                 //通过该Socket实例化网络流           
 47                 ns = new NetworkStream(sock);
 48                 //ClientTcp是添加的类,下面会做说明
 49                 ClientTcp ct = new ClientTcp(ns);
 50                 //ct_MyEvent方法注册ClientTcp类的MyEvent事件
 51                 ct.MyEvent += new MyDelegate(ct_MyEvent);
 52                 //开启线程
 53                 Thread th = new Thread(new ThreadStart(ct.TcpThread));
 54                 th.IsBackground = true;
 55                 th.Start();
 56             }
 57         }
 58 
 59 
 60         static void ct_MyEvent(string temp)
 61         {
 62             if (temp.StartsWith("login:"))
 63             {
 64                 temp = temp.Replace("login:", "");
 65                 Console.WriteLine("UserLogin:" + temp);
 66                 string[] index = temp.Split(';');
 67                 if (!ContainsList(index[0]))
 68                 {
 69                     userlist.Add(temp);
 70                 }
 71                 SendUsersToUser(index[0]);
 72             }
 73             else if (temp.StartsWith("out:"))
 74             {
 75                 temp = temp.Replace("out:", "");
 76                 Console.WriteLine("UserLoginOut:" + temp);
 77                 if (ContainsList(temp))
 78                 {
 79                     RemoveList(temp);
 80                 }
 81                 SendUsersToUser(temp);
 82             }
 83         }
 84 
 85         static void SendUsersToUser(string outuser)
 86         {
 87             string message = GetUserStr();
 88             UdpClient uc;
 89             foreach (string s in userlist)
 90             {
 91                 string[] _userstrindex=s.Split(';');
 92                 if (_userstrindex[0] == outuser)
 93                     continue;
 94                 string _ipsindex = _userstrindex[1];
 95                 string[] _ipindex = _ipsindex.Split(':');
 96                 byte[] b = System.Text.Encoding.UTF8.GetBytes("users" + message);
 97                 //向本机的8888端口发送数据
 98                 uc = new UdpClient();
 99                 uc.Send(b, b.Length, _ipindex[0], int.Parse(_ipindex[1]));
100             }
101         }
102 
103         static string GetUserStr()
104         {
105             StringBuilder sb = new StringBuilder();
106             foreach (string s in userlist)
107             {
108                 if (sb.Length > 0)
109                     sb.Append("#");
110                 sb.Append(s);
111             }
112             return sb.ToString();
113         }
114 
115         static bool ContainsList(string str)
116         {
117             foreach (string s in userlist)
118             {
119                 if (s.Split(';')[0] == str)
120                 {
121                     return true;
122                 }
123             }
124             return false;
125         }
126 
127         static void RemoveList(string str)
128         {
129             for (int i = userlist.Count - 1; i >= 0; i--)
130             {
131                 string s = userlist[i];
132                 if (s.Split(';')[0] == str)
133                 {
134                     userlist.Remove(s);
135                 }
136             }
137         }
138     }
139 
140     public delegate void MyDelegate(string temp);
141     class ClientTcp
142     {
143         //设置网络流局部对象
144         private NetworkStream ns;
145         //声明类型为MyDelegate的事件MyEvent
146         public event MyDelegate MyEvent;
147         //构造函数中接收参数以初始化
148         public ClientTcp(NetworkStream ns)
149         {
150             this.ns = ns;
151         }
152         //服务器端线程所调用的方法
153         public void TcpThread()
154         {
155             //获得相关的封装流
156             StreamReader sr = new StreamReader(ns);
157             string temp = sr.ReadLine();
158             //接收到客户端消息后触发事件将消息回传
159             if (!temp.StartsWith("getuser"))
160             {
161                 MyEvent(temp);
162             }
163             StringBuilder sb = new StringBuilder();
164             foreach (string s in Program.userlist)
165             {
166                 if (sb.Length > 0)
167                     sb.Append("#");
168                 sb.Append(s);
169             }
170             StreamWriter sw = new StreamWriter(ns);
171             //转换为大写后发送消息给客户端
172             sw.WriteLine(sb.ToString());
173             sw.Flush();
174             sw.Close();
175             sr.Close();
176         }
177     }
178 }

需要注意的地方:

tl = new TcpListener(12345);这个地方使用了固定端口12345,所有客户端跟服务器进行通信必须使用这个端口

Thread th = new Thread(new ThreadStart(ct.TcpThread)); th.IsBackground = true; th.Start();

这个地方为什么使用一个线程呢???

当接收到一个信息后需要进行处理,如果同时有好多信息进来的话容易堵塞,所有用线程,并且接收到一个信息马上将信息放到 ClientTcp ct = new ClientTcp(ns);这里,然后慢慢进行处理吧

服务器接收到的消息有多种,怎么区分呢???

有登陆的信息,有退出的信息,有获取列表的信息,我们可以在发送的消息内用一些字段进行标记,例如在头部加上“getuser”等等的

=======================================================

下面是客户端的

登陆

 1 using System;
 2 using System.Collections.Generic;
 3 using System.ComponentModel;
 4 using System.Data;
 5 using System.Drawing;
 6 using System.Text;
 7 using System.Windows.Forms;
 8 using System.Net.Sockets;
 9 using System.IO;
10 using System.Net;
11 
12 namespace QQClient
13 {
14     public partial class Login : Form
15     {
16         private TcpClient tc;
17         //声明网络流
18         private NetworkStream ns;
19         public Login()
20         {
21             InitializeComponent();
22         }
23 
24         private void button1_Click(object sender, EventArgs e)
25         {
26             string username = textBox1.Text;
27             string ipstr = textBox2.Text;
28             string poitstr = textBox3.Text;
29 
30             IPHostEntry ipe = Dns.GetHostEntry(Dns.GetHostName());
31             IPAddress ipa = null;
32             foreach (IPAddress ip in ipe.AddressList)
33             {
34                 if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
35                     continue;
36                 ipa = ip;
37                 break;
38             }
39 
40             StringBuilder sb = new StringBuilder();
41             sb.Append("login:");
42             sb.Append(username + ";");
43             sb.Append(ipa.ToString() + ":");
44             Random r = new Random();
45             int port = r.Next(2000, 65535);            
46             sb.Append(port.ToString());
47 
48             try
49             {
50                 tc = new TcpClient(ipstr, int.Parse(poitstr));
51             }
52             catch
53             {
54                 MessageBox.Show("无法连接到主机");
55             }
56             //实例化网络流对象
57             ns = tc.GetStream();
58             StreamWriter sw = new StreamWriter(ns);
59             StreamReader sr = new StreamReader(ns);
60             //将TextBox1的值传给服务器端
61             sw.WriteLine(sb.ToString());
62             sw.Flush();
63             //接收服务器端回传的字符串
64             string users = sr.ReadLine();
65            
66             sr.Close();
67             sw.Close();
68 
69             Main main=new Main();
70             main.Username=username;
71             main.Users=users;
72             main.Port = port;
73             main.ThisIP = ipa.ToString();
74             main.ServerIP = textBox2.Text;
75             main.ServerPort = textBox3.Text;
76             this.Hide();
77             main.ShowDialog();           
78         }
79 
80         private void button2_Click(object sender, EventArgs e)
81         {
82             Application.Exit();
83         }
84     }
85 }

列表界面

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.Text;
  7 using System.Windows.Forms;
  8 using System.Net.Sockets;
  9 using System.IO;
 10 using System.Threading;
 11 using System.Net;
 12 
 13 namespace QQClient
 14 {
 15     public partial class Main : Form
 16     {
 17         public string Username { get; set; }
 18         public string Users { get; set; }
 19         public int Port { get; set; }
 20         public string ServerIP;
 21         public string ServerPort;
 22         public string ThisIP { get; set; }
 23         public static List<Talking> TalkList = new List<Talking>();
 24         public List<User> userList = new List<User>();
 25         public Main()
 26         {
 27             InitializeComponent();
 28         }
 29 
 30         private void Main_Load(object sender, EventArgs e)
 31         {
 32             //Control.CheckForIllegalCrossThreadCalls = false;
 33             this.Text = Username;
 34             LoadUser();
 35             StartListen();
 36         }
 37 
 38         private void LoadUser()
 39         {
 40             if (string.IsNullOrEmpty(Users))
 41                 return;
 42             this.listView1.Items.Clear();
 43             userList.Clear();
 44             string[] _userindex = Users.Split('#');
 45             foreach (string s in _userindex)
 46             {
 47                 string[] _index = s.Split(';');
 48                 string _username = _index[0];
 49                 //string[] _ipinex = _index[1].Split(':');
 50                 //string ip = _ipinex[0];
 51                 //string port = _ipinex[1];
 52                 if (_username != Username)
 53                 {
 54                     //TreeNode tn = new TreeNode();
 55                     //tn.Text = _username;
 56                     //tn.Tag = _index[1];
 57                     //this.treeView1.Nodes.Add(tn);
 58 
 59                     ListViewItem lvitem = new ListViewItem();
 60 
 61                     lvitem.ImageIndex = 0;
 62                     lvitem.Text = _username;
 63                     lvitem.Tag = _index[1];
 64                     this.listView1.Items.Add(lvitem);
 65                     userList.Add(new User() { UserName = _username, Ips = _index[1] });
 66                 }
 67             }
 68         }
 69                
 70 
 71         private void button2_Click(object sender, EventArgs e)
 72         {
 73             Application.Exit();
 74         }
 75 
 76         private void button1_Click(object sender, EventArgs e)
 77         {
 78             try
 79             {
 80                 TcpClient tc = new TcpClient(ServerIP, int.Parse(ServerPort));
 81                 //实例化网络流对象
 82                 NetworkStream ns = tc.GetStream();
 83                 StreamWriter sw = new StreamWriter(ns);
 84                 StreamReader sr = new StreamReader(ns);
 85                 //将TextBox1的值传给服务器端
 86                 sw.WriteLine("getuser");
 87                 sw.Flush();
 88                 //接收服务器端回传的字符串
 89                 Users = sr.ReadLine();
 90                 sr.Close();
 91                 sw.Close();
 92                 LoadUser();
 93             }
 94             catch
 95             { }
 96         }
 97 
 98         private void Main_FormClosed(object sender, FormClosedEventArgs e)
 99         {
100             try
101             {
102                 TcpClient tc = new TcpClient(ServerIP, int.Parse(ServerPort));
103                 //实例化网络流对象
104                 NetworkStream ns = tc.GetStream();
105                 StreamWriter sw = new StreamWriter(ns);
106                 //将TextBox1的值传给服务器端
107                 sw.WriteLine("out:" + Username);
108                 sw.Flush();
109                 sw.Close();
110                 iswork = false;
111             }
112             catch
113             { }
114             Application.Exit();
115         }
116 
117         private void listView1_MouseDoubleClick(object sender, MouseEventArgs e)
118         {
119             if (this.listView1.SelectedItems.Count > 0)
120             {
121                 ListViewItem lvitem = this.listView1.SelectedItems[0];
122                 string toname = lvitem.Text;
123                 string toips = lvitem.Tag.ToString();
124                 Talking t = isHaveTalk(toname);
125                 if (t != null)
126                 {
127                     t.Focus();
128                 }
129                 else
130                 {
131                     Talking talk = new Talking();
132                     talk.UserName = Username;
133                     talk.ToName = toname;
134                     talk.ToIP = toips;
135                     TalkList.Add(talk);
136                     talk.Show();
137                 }
138             }
139         }
140 
141         private Talking isHaveTalk(string toname)
142         {
143             foreach (Talking tk in TalkList)
144             {
145                 if (tk.ToName == toname)
146                     return tk;
147             }
148             return null;
149         }
150 
151         public static void RemoveTalking(Talking _talk)
152         {
153             foreach (Talking tk in TalkList)
154             {
155                 if (tk.ToName == _talk.ToName)
156                 {
157                     TalkList.Remove(_talk);
158                     return;
159                 }
160             }
161         }
162 
163         bool iswork = false;
164         UdpClient uc = null;
165         private void StartListen()
166         {
167 
168             iswork = true;
169             Thread th = new Thread(new ThreadStart(listen));
170             //设置为后台
171             th.IsBackground = true;
172             th.Start();
173         }
174         private void listen()
175         {
176             uc = new UdpClient(Port);
177             IPEndPoint iep = new IPEndPoint(IPAddress.Any, 0);
178             while (iswork)
179             {
180                 //获得Form1发送过来的数据包
181                 string text = System.Text.Encoding.UTF8.GetString(uc.Receive(ref iep));
182                 if (text.StartsWith("message"))
183                 {
184                     text = text.Substring(7);
185                     int indexof = text.IndexOf("#");
186                     string fromuser = text.Substring(0, indexof);
187                     string message = text.Substring(indexof + 1);
188                     Talking _tk = isHaveTalk(fromuser);
189                     if (_tk != null)
190                     {
191                         this.BeginInvoke(new MethodInvoker(delegate()
192                         {
193                             _tk.Focus();
194                             _tk.AddMessage(message, true);
195                         }));
196                     }
197                     else
198                     {
199                         //Talking talk = new Talking(message);
200                         //talk.UserName = Username;
201                         //talk.ToName = fromuser;
202                         //talk.ToIP = GetIP(fromuser);
203                         //TalkList.Add(talk);
204                         //talk.Show();
205                         this.BeginInvoke(new MethodInvoker(delegate()
206                         {
207                             this.CreatTalking(text);
208                         }));
209                         //Thread th = new Thread(new ParameterizedThreadStart(CreatTalking));
210                         //th.IsBackground = true;
211                         //th.Start(text);
212                     }
213                     //加入ListBox
214                     //this.listBox1.Items.Add(text);
215                 }
216                 else if (text.StartsWith("users"))
217                 {
218                     text = text.Substring(5);
219                     Users = text;
220                     LoadUser();
221                 }
222             }
223         }
224 
225         public void CreatTalking(object _text)
226         {
227             string text = _text.ToString();
228             int indexof = text.IndexOf("#");
229             string fromuser = text.Substring(0, indexof);
230             string message = text.Substring(indexof + 1);
231             Talking talk = new Talking(message);
232             talk.UserName = Username;
233             talk.ToName = fromuser;
234             talk.ToIP = GetIP(fromuser);
235             TalkList.Add(talk);
236             talk.Show();
237         }
238 
239         private string GetIP(string toname)
240         {
241             foreach (User user in userList)
242             {
243                 if (user.UserName == toname)
244                     return user.Ips;
245             }
246             return "";
247         }
248     }
249     public class User
250     {
251         private string userName;
252 
253         public string UserName
254         {
255             get { return userName; }
256             set { userName = value; }
257         }
258         private string ips;
259 
260         public string Ips
261         {
262             get { return ips; }
263             set { ips = value; }
264         }
265     }
266 }

聊天界面

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.Text;
  7 using System.Windows.Forms;
  8 using System.Net.Sockets;
  9 using System.Threading;
 10 
 11 namespace QQClient
 12 {
 13     public partial class Talking : Form
 14     {
 15         public string UserName { get; set; }
 16         public string ToName { get; set; }
 17         public string ToIP { get; set; }
 18 
 19         UdpClient uc;
 20         public Talking()
 21         {
 22             InitializeComponent();
 23         }
 24 
 25         string getmessage = string.Empty;
 26         public Talking(string message)
 27         {
 28             getmessage = message;
 29             InitializeComponent();
 30         }
 31 
 32         private void Talking_Load(object sender, EventArgs e)
 33         {
 34             uc = new UdpClient();
 35             this.Text = "和" + ToName + "聊天中";
 36             if (!string.IsNullOrEmpty(getmessage))
 37             {
 38                 ShowTalking();
 39                 AddMessage(getmessage, true);
 40             }
 41         }
 42 
 43         private void button1_Click(object sender, EventArgs e)
 44         {
 45             string temp = this.textBox1.Text; //保存TextBox文本
 46             //将该文本转化为字节数组
 47             byte[] b = System.Text.Encoding.UTF8.GetBytes("message" + UserName + "#" + temp);
 48             //向本机的8888端口发送数据
 49             string[] _ip = ToIP.Split(':');
 50             uc.Send(b, b.Length, _ip[0], int.Parse(_ip[1]));
 51             AddMessage(temp, false);
 52             this.textBox1.Clear();
 53         }
 54         public void AddMessage(string str, bool isuser)
 55         {
 56             int startindex = this.richTextBox1.Text.Length;
 57 
 58             string message = string.Empty;
 59 
 60             if (isuser)
 61                 message = "【" + ToName + "】  " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\n" + str + "\n";
 62             else
 63                 message = "【" + UserName + "】  " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\n" + str + "\n";
 64             this.richTextBox1.AppendText(message);
 65             this.richTextBox1.Select(startindex, message.Length);
 66             if (isuser)
 67             {
 68                 this.richTextBox1.SelectionAlignment = HorizontalAlignment.Left;
 69             }
 70             else
 71             {
 72                 this.richTextBox1.SelectionAlignment = HorizontalAlignment.Right;
 73             }
 74             this.richTextBox1.Select(this.richTextBox1.Text.Length, 0);
 75         }
 76 
 77         [System.Runtime.InteropServices.DllImport("user32")]
 78         private static extern long FlashWindow(IntPtr hwnd, bool bInvert);
 79 
 80         private static void FlashWindow(object _handle)
 81         {
 82             IntPtr handle = (IntPtr)_handle;
 83             int flashindex = 0;
 84             while (true)
 85             {
 86                 if (flashindex > 5)
 87                     break;
 88                 FlashWindow(handle, true);
 89                 flashindex++;
 90                 Thread.Sleep(500);
 91             }
 92         }
 93 
 94         public void ShowTalking()
 95         {
 96             Thread _thread = new Thread(FlashWindow);
 97             _thread.IsBackground = true;
 98             _thread.Start(this.Handle);
 99         }
100 
101         private void Talking_FormClosed(object sender, FormClosedEventArgs e)
102         {
103             Main.RemoveTalking(this);
104         }
105 
106         private void button2_Click(object sender, EventArgs e)
107         {
108             this.Close();
109         }
110     }
111 }

大致总结下:

tcp必须建立连接才可以进行通信

udp不需要建立通信

但是两者都需要一个监听来接收消息

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券