使用Socket套接字进行网络通信,其实在日常生活中用处是非常多的,比如我们用的QQ/联众这些程序都是通过套接字来进行通信,但是由于B/S开发的风靡,大家正在慢慢的淡忘着它,介绍套接字通信的书籍也非常的少,本文作者希望通过一系列简单的程序让大家慢慢了解套接字,并通过介绍的内容来写一个简单的谈天工具。
在Socket编程中常用的协议主要有UDP与TCP这二种,学习过一点网络知识的同学都知识如果想要保证数据的安全到达那么我们一般采用TCP协议,如果为了保证数据的快速高效那么就要采用UDP协议,所谓的UDP不安全是相对TCP来说的。
- 创建一个简单的UDP客户/服务器通信程序
在这个程序中我们将创建一个简单的解决方案实现基本的网络谈天,在这个解决方案中我们将包含二个应用程序。一个客户端应用程序用于发送信息,一个服务器端用于接收信息,在开发的过程中我们可能会遇到许多的难题(基本上是围绕着线程出来的,因为现在大部分的程序员以B/S开发为主,慢慢的生疏了C/S开发,线程这一块可能是个问题)。
写一个UDP的客户端,界面如下图:
发送按钮的事件内容代码如下:
1: private void button1_Click(object sender, EventArgs e)
2: {
3: UdpClient udpClient=new UdpClient();
4: udpClient.Connect(txtHost.Text,8080);
5: byte[] sendBytes = Encoding.UTF8.GetBytes(this.textBox1.Text);
6: udpClient.Send(sendBytes, sendBytes.Length);
7: }
首先我们创建一个UdpClient的实例用于发送数据包,通过connect方法连接到指定的UDP服务器上,8080是我所指定的端口,您也可以自已写一个TEXTBOX自已指定端口,但这个端口号一定必须与服务器的端口相对应,之后创建一个sendBytes的bytes数组,在这里我们使用到了Encoding这个类中的GetBytes方法,在这里我们所采用的编码是UTF-8,确定下来要发送的bytes数组之后我们就可以通过,Send方法来发送了。
从一个客户端程序上,大家可以看的出来建立一个UDP的客户端非常的容易。那么有一点点难度的可能就是服务器的创建了,如果您对线程不是特别了解的话,我建议你用Console程序来做服务器端,因为它会省下很多windows应用程序上跨线程调用的麻烦,但是在这里我还是以windows程序为主,因为你早晚都要接触windows多线程编程。
写一个服务器端应用程序
在这个程序中我们主要是将在客户端接受上来的数据进行解码与显示。
在这个服务器端主要是用到了几个重要的成员方法,由于整体使用了多线程,我先把精简下来的程序写上来,慢慢介绍,在文章最后我会把源码贴上来。
1: public void ServerThread(object argu)
2: {
3: UdpClient udpClient = new UdpClient(8080);
4: while (true)
5: {
6: IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
7: byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);
8: string returnData = Encoding.UTF8.GetString(receiveBytes);
9: returnData +=RemoteIpEndPoint.Address;
10:
11: }
12: }
ServerThread()成员方法主要是用到创建一个消息循环接收客户端发送上来的信息,代码比较简单我就不多做解释了,为了保证不影响窗体界面的重绘,我们将其改成多线程的程序,首先我们在窗体一载入的时候就加载这个方法,并且是一个单独的线。
1: private void Form1_Load(object sender, EventArgs e)
2: {
3: th2 = new Thread(new ParameterizedThreadStart(ServerThread));
4: ThreadArgu argu = new ThreadArgu();
5: argu.lstBox = this.listBox1;
6:
7: th2.Start((Object)argu);
8: th2.IsBackground = true;
9: }
1: //当启动线程时,向线程函数传送的信息
2: class ThreadArgu
3: {
4: public ListBox lstBox; //用于显示工作进度的进度条对象
5: }
6: //代表两个线程对象
7: Thread th2;
8: //让进度条显示进度的委托,用于跨线程调用
9: public delegate void ShowProgressDelegate(ListBox tempListBox, string value);
10: //设置进度条的进度值,用于跨线程委托调用
11: private void SetProgressBarValue(ListBox prog, string value)
12: {
13: prog.Items.Add(value);
14: }
由于我们使用了线程所以ServerThread()需要重写
1: public void ServerThread(object argu)
2: {
3: UdpClient udpClient = new UdpClient(8080);
4: while (true)
5: {
6: IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
7: byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);
8: string returnData = Encoding.UTF8.GetString(receiveBytes);
9: returnData +=RemoteIpEndPoint.Address;
10: (argu as ThreadArgu).lstBox.Invoke(new ShowProgressDelegate(SetProgressBarValue), new object[] { (argu as ThreadArgu).lstBox, returnData });
11:
12: }
13: }
1: using System;
2: using System.Collections.Generic;
3: using System.ComponentModel;
4: using System.Data;
5: using System.Drawing;
6: using System.Linq;
7: using System.Text;
8: using System.Windows.Forms;
9: using System.Net.Sockets;
10: using System.Net;
11: using System.Threading;
12: namespace WindowsFormsApplication3
13: {
14: //让进度条显示进度的委托,用于跨线程调用
15: public delegate void ShowProgressDelegate(ListBox tempListBox, string value);
16: public partial class Form1 : Form
17: {
18: //当启动线程时,向线程函数传送的信息
19: class ThreadArgu
20: {
21: public ListBox lstBox; //用于显示工作进度的进度条对象
22: }
23: //代表两个线程对象
24: Thread th2;
25: public Form1()
26: {
27: InitializeComponent();
28: }
29:
30: private void Form1_Load(object sender, EventArgs e)
31: {
32: th2 = new Thread(new ParameterizedThreadStart(ServerThread));
33: ThreadArgu argu = new ThreadArgu();
34: argu.lstBox = this.listBox1;
35:
36: th2.Start((Object)argu);
37: th2.IsBackground = true;
38: }
39: public void ServerThread(object argu)
40: {
41: UdpClient udpClient = new UdpClient(8080);
42: while (true)
43: {
44: IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
45: byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);
46: string returnData = Encoding.UTF8.GetString(receiveBytes);
47: returnData +=RemoteIpEndPoint.Address;
48: (argu as ThreadArgu).lstBox.Invoke(new ShowProgressDelegate(SetProgressBarValue), new object[] { (argu as ThreadArgu).lstBox, returnData });
49:
50: }
51: }
52: //设置进度条的进度值,用于跨线程委托调用
53: private void SetProgressBarValue(ListBox prog, string value)
54: {
55: prog.Items.Add(value);
56: }
57:
58: private void Form1_FormClosing(object sender, FormClosingEventArgs e)
59: {
60:
61: }
62:
63:
64:
65: }
66: }