长时间没有摸这两个协议,写个代码温习下
下面是界面
【服务器界面】
【登陆界面】
【好友列表界面(我登陆了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不需要建立通信
但是两者都需要一个监听来接收消息