首页 > 代码库 > 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 }
需要注意的地方:
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 }
列表界面
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 }
聊天界面
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不需要建立通信
但是两者都需要一个监听来接收消息
c# UDP/TCP协议简单实现(简单聊天工具)