首页 > 代码库 > c# UDP/TCP协议简单实现(简单聊天工具)

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 ClientTcp142     {143         //设置网络流局部对象144         private NetworkStream ns;145         //声明类型为MyDelegate的事件MyEvent146         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 }
View Code

需要注意的地方:

    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 QQClient13 {14     public partial class Login : Form15     {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             try49             {50                 tc = new TcpClient(ipstr, int.Parse(poitstr));51             }52             catch53             {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 }
View Code

列表界面

  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             try101             {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             catch113             { }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                 else130                 {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                     else198                     {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                     //加入ListBox214                     //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 User250     {251         private string userName;252 253         public string UserName254         {255             get { return userName; }256             set { userName = value; }257         }258         private string ips;259 260         public string Ips261         {262             get { return ips; }263             set { ips = value; }264         }265     }266 }
View Code

聊天界面

  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 }
View Code

 

 

大致总结下:

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

udp不需要建立通信

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

 

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