最近也在接触SOCKET编程,在当今这样一个网络时代,很多技术都以网络为中心在诞生,至少我认为是这样的,而SOCKET套接字接口,在实现网络通讯上处于关键地位,所以不会SOCKET是不行的。
首先,本文主要是针对那些刚接触SOCKET编程的朋友,如果是高手,就可以不看此文啦,可以去陪陪老婆,比如逛街或看电视...
在开始之前,我们需要预习一些基础知识:
什么是SOCKET套接字?
SOCKET通常有那几种数据格式?
线程的概念?
(以上基本知识我就不讲了,网上这方面资料很多的,大家找资料看下吧)
我要介绍的是一个服务器端+客户端的聊天系统,程序比较简单,我先把程序运行的界面给大家看下:
上面是服务器端运行界面;下面把客户端界面贴给大家看下:
功能比较简单,服务器的端口号可以在“系统菜单”里面的参数配置进行修改的。
看了上面的图,下面我们就给大家把代码贴出来:(因为程序比较简单,所以本人就没有去分层啦)
服务器端代码:
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
9 using System.Net;
10 using System.Net.Sockets;
11 using System.Threading;
12 using System.Xml;
13
14 namespace Server
15 {
16 public partial class ServerMain : Form
17 {
18 public ServerMain()
19 {
20 InitializeComponent();
21 }
22
23 private void ServerMain_Load( object sender, EventArgs e)
24 {
25 this .CmdStar.Enabled = true ;
26 this .CmdStop.Enabled = false ;
27 }
28
29 private void 配置参数ToolStripMenuItem_Click( object sender, EventArgs e)
30 {
31 Set TSet = new Set();
32 TSet.ShowDialog();
33 }
34
35 private void 关于ToolStripMenuItem_Click( object sender, EventArgs e)
36 {
37 About TAbout = new About();
38 TAbout.Show();
39 }
40 /// <summary>
41 /// 获得XML文件中的端口号
42 /// </summary>
43 /// <returns></returns>
44 private int GetPort()
45 {
46 try
47 {
48 XmlDocument TDoc = new XmlDocument();
49 TDoc.Load( " Settings.xml " );
50 string TPort = TDoc.GetElementsByTagName( " ServerPort " )[ 0 ].InnerXml;
51 return Convert.ToInt32(TPort);
52
53 }
54 catch { return 6600 ; } // 默认是6600
55 }
56
57 // 声明将要用到的类
58 private IPEndPoint ServerInfo; // 存放服务器的IP和端口信息
59 private Socket ServerSocket; // 服务端运行的SOCKET
60 private Thread ServerThread; // 服务端运行的线程
61 private Socket[] ClientSocket; // 为客户端建立的SOCKET连接
62 private int ClientNumb; // 存放客户端数量
63 private byte [] MsgBuffer; // 存放消息数据
64
65 private void CmdStar_Click( object sender, EventArgs e)
66 {
67 ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
68 ServerInfo = new IPEndPoint(IPAddress.Any, this .GetPort());
69 ServerSocket.Bind(ServerInfo); // 将SOCKET接口和IP端口绑定
70 ServerSocket.Listen( 10 ); // 开始监听,并且挂起数为10
71
72 ClientSocket = new Socket[ 65535 ]; // 为客户端提供连接个数
73 MsgBuffer = new byte [ 65535 ]; // 消息数据大小
74 ClientNumb = 0 ; // 数量从0开始统计
75
76 ServerThread = new Thread(RecieveAccept); // 将接受客户端连接的方法委托给线程
77 ServerThread.Start(); // 线程开始运行
78
79 CheckForIllegalCrossThreadCalls = false ; // 不捕获对错误线程的调用
80
81 this .CmdStar.Enabled = false ;
82 this .CmdStop.Enabled = true ;
83 this .StateMsg.Text = " 服务正在运行 " + " 运行端口: " + this .GetPort().ToString();
84 this .ClientList.Items.Add( " 服务于 " + DateTime.Now.ToString() + " 开始运行. " );
85 }
86
87 // 接受客户端连接的方法
88 private void RecieveAccept()
89 {
90 while ( true )
91 {
92 ClientSocket[ClientNumb] = ServerSocket.Accept();
93 ClientSocket[ClientNumb].BeginReceive(MsgBuffer, 0 , MsgBuffer.Length, 0 , new AsyncCallback(RecieveCallBack),ClientSocket[ClientNumb]);
94 this .ClientList.Items.Add(ClientSocket[ClientNumb].RemoteEndPoint.ToString() + " 成功连接服务器. " );
95 ClientNumb ++ ;
96 }
97 }
98
99 // 回发数据给客户端
100 private void RecieveCallBack(IAsyncResult AR)
101 {
102 try
103 {
104 Socket RSocket = (Socket)AR.AsyncState;
105 int REnd = RSocket.EndReceive(AR);
106 for ( int i = 0 ; i < ClientNumb; i ++ )
107 {
108 if (ClientSocket[i].Connected)
109 {
110 ClientSocket[i].Send(MsgBuffer, 0 , REnd, 0 );
111 }
112 RSocket.BeginReceive(MsgBuffer, 0 , MsgBuffer.Length, 0 , new AsyncCallback(RecieveCallBack), RSocket);
113
114 }
115 }
116 catch { }
117
118 }
119
120 private void CmdStop_Click( object sender, EventArgs e)
121 {
122 ServerThread.Abort(); // 线程终止
123 ServerSocket.Close(); // 关闭SOCKET
124
125 this .CmdStar.Enabled = true ;
126 this .CmdStop.Enabled = false ;
127 this .StateMsg.Text = " 等待运行 " ;
128 this .ClientList.Items.Add( " 服务于 " + DateTime.Now.ToString() + " 停止运行. " );
129 }
130
131
132
133 }
134 }
客户端代码:
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
9 using System.Net;
10 using System.Net.Sockets;
11
12 namespace Client
13 {
14 public partial class ClientMain : Form
15 {
16 public ClientMain()
17 {
18 InitializeComponent();
19 }
20
21 private IPEndPoint ServerInfo;
22 private Socket ClientSocket;
23 private Byte[] MsgBuffer;
24 private Byte[] MsgSend;
25
26 private void ClientMain_Load( object sender, EventArgs e)
27 {
28 this .CmdSend.Enabled = false ;
29 this .CmdExit.Enabled = false ;
30
31 ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
32 MsgBuffer = new Byte[ 65535 ];
33 MsgSend = new Byte[ 65535 ];
34 CheckForIllegalCrossThreadCalls = false ;
35
36 Random TRand = new Random();
37 this .UserName.Text = " 用户 " + TRand.Next( 10000 ).ToString();
38 }
39
40 private void CmdEnter_Click( object sender, EventArgs e)
41 {
42 ServerInfo = new IPEndPoint(IPAddress.Parse( this .ServerIP.Text), Convert.ToInt32( this .ServerPort.Text));
43
44 try
45 {
46 ClientSocket.Connect(ServerInfo);
47
48 ClientSocket.Send(Encoding.Unicode.GetBytes( " 用户: " + this .UserName.Text + " 进入系统!\n " ));
49
50 ClientSocket.BeginReceive(MsgBuffer, 0 , MsgBuffer.Length, 0 , new AsyncCallback(ReceiveCallBack), null );
51
52 this .SysMsg.Text += " 登录服务器成功!\n " ;
53 this .CmdSend.Enabled = true ;
54 this .CmdEnter.Enabled = false ;
55 this .CmdExit.Enabled = true ;
56 }
57 catch
58 {
59 MessageBox.Show( " 登录服务器失败,请确认服务器是否正常工作! " );
60 }
61 }
62
63 private void ReceiveCallBack(IAsyncResult AR)
64 {
65 try
66 {
67 int REnd = ClientSocket.EndReceive(AR);
68 this .RecieveMsg.AppendText(Encoding.Unicode.GetString(MsgBuffer, 0 , REnd));
69 ClientSocket.BeginReceive(MsgBuffer, 0 , MsgBuffer.Length, 0 , new AsyncCallback(ReceiveCallBack), null );
70
71 }
72 catch
73 {
74 MessageBox.Show( " 已经与服务器断开连接! " );
75 this .Close();
76 }
77
78 }
79
80 private void CmdSend_Click( object sender, EventArgs e)
81 {
82 MsgSend = Encoding.Unicode.GetBytes( this .UserName.Text + " 说:\n " + this .SendMsg.Text + " \n " );
83 if (ClientSocket.Connected)
84 {
85 ClientSocket.Send(MsgSend);
86 this .SendMsg.Text = "" ;
87 }
88 else
89 {
90 MessageBox.Show( " 当前与服务器断开连接,无法发送信息! " );
91 }
92 }
93
94 private void CmdExit_Click( object sender, EventArgs e)
95 {
96 if (ClientSocket.Connected)
97 {
98 ClientSocket.Send(Encoding.Unicode.GetBytes( this .UserName.Text + " 离开了房间!\n " ));
99 ClientSocket.Shutdown(SocketShutdown.Both);
100 ClientSocket.Disconnect( false );
101 }
102 ClientSocket.Close();
103
104 this .CmdSend.Enabled = false ;
105 this .CmdEnter.Enabled = true ;
106 this .CmdExit.Enabled = false ;
107 }
108
109 private void RecieveMsg_TextChanged( object sender, EventArgs e)
110 {
111 this .RecieveMsg.ScrollToCaret();
112 }
113
114 private void SendMsg_KeyDown( object sender, KeyEventArgs e)
115 {
116 if (e.Control && e.KeyValue == 13 )
117 {
118 e.Handled = true ;
119 this .CmdSend_Click( this , null );
120 }
121 }
122
123
124
125
126 }
127 }
我只对服务器端的代码做了注释,客户端就没有写注释了,因为代码是差不多的。区别在于客户端不需要监听,也不需要启用线程进行委托。
关于 ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
这句代码,我想给初学者解释一下,这里“AddressFamily.InterNetwork”表示的是使用IPV4地址,“SocketType.Stream”表示使用的是流格式(另外还有数据包格式和原始套接字格式),“ProtocolType.Tcp”表示使用TCP协议(另外还有很多其它协议,例如大家常看到的UDP协议)。
另外关于SOCKET类中的BeginReceive方法,请大家参考MSDN,里面有详细说明。
希望本人给的这个程序可以起到一个抛砖引玉的作用,不明白之处可以加QQ(17020415)或留言。
备注:
//今天有朋友加我QQ问我有关服务端“Settings.xml”文件的内容部分,我现在把内容贴出来,其实很简单,就是方便服务端修改端口的。
<? xml version="1.0" encoding="utf-8" ?>
< Server >
< ServerPort > 6600 </ ServerPort >
</ Server >
完整的源码我已经放在CSDN上面共享了,地址:http://download.csdn.net/user/lixyvip
本文转自黄聪博客园博客,原文链接:http://www.cnblogs.com/huangcong/archive/2010/03/27/1698434.html,如需转载请自行联系原作者