長時間沒有摸這兩個協議,寫個代碼溫習下
下面是界面
【服務器界面】
【登陸界面】
【好友列表界面(我登陸了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不需要建立通信
但是兩者都需要一個監聽來接收消息