来源:http://hi.baidu.com/jiang_yy_jiang/blog/item/3bf0f9fa75c7c913a9d31144.html
项目结构图 服务端程序
客户端效果
下面这个实例是一个完整的使用Socket实现的聊天(只限于局域网,如果能提供一个高权限的IP就可以实现类似QQ聊天),其中的原理是:首先开启服务端,打开侦听(任何端口为6600的IP),下面实现的代码:服务端+客户端【VS2005 C#.NET 2.0】
【服务端】三个窗体:About.cs,ServerMain.cs,Set.cs
ServerMain.cs窗体代码
using
System;
using
System.Text;
using
System.Windows.Forms;
using
System.Net;
using
System.Net.Sockets;
using
System.Threading;
using
System.Xml;
namespace
Server
{
public
partial
class
ServerMain : Form
{
public
ServerMain()
{
InitializeComponent();
}
private
void
ServerMain_Load(
object
sender, EventArgs e)
{
this
.CmdStar.Enabled
=
true
;
this
.CmdStop.Enabled
=
false
;
}
private
void
配置参数ToolStripMenuItem_Click(
object
sender, EventArgs e)
{
Set TSet
=
new
Set();
TSet.ShowDialog();
}
private
void
关于ToolStripMenuItem_Click(
object
sender, EventArgs e)
{
About TAbout
=
new
About();
TAbout.Show();
}
///
<summary>
///
获得XML文件中的端口号
///
</summary>
///
<returns></returns>
private
int
GetPort()
{
try
{
XmlDocument TDoc
=
new
XmlDocument();
TDoc.Load(
"
Settings.xml
"
);
string
TPort
=
TDoc.GetElementsByTagName(
"
ServerPort
"
)[
0
].InnerXml;
return
Convert.ToInt32(TPort);
}
catch
{
return
6600
; }
//
默认是6600
}
//
声明将要用到的类
private
IPEndPoint ServerInfo;
//
存放服务器的IP和端口信息
private
Socket ServerSocket;
//
服务端运行的SOCKET
private
Thread ServerThread;
//
服务端运行的线程
private
Socket[] ClientSocket;
//
为客户端建立的SOCKET连接
private
int
ClientNumb;
//
存放客户端数量
private
byte
[] MsgBuffer;
//
存放消息数据
private
void
CmdStar_Click(
object
sender, EventArgs e)
{
ServerSocket
=
new
Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//
提供一个 IP 地址,指示服务器应侦听所有网络接口上的客户端活动
IPAddress ip
=
IPAddress.Any;
ServerInfo
=
new
IPEndPoint(ip,
this
.GetPort());
ServerSocket.Bind(ServerInfo);
//
将SOCKET接口和IP端口绑定
ServerSocket.Listen(
10
);
//
开始监听,并且挂起数为10
ClientSocket
=
new
Socket[
65535
];
//
为客户端提供连接个数
MsgBuffer
=
new
byte
[
65535
];
//
消息数据大小
ClientNumb
=
0
;
//
数量从0开始统计
ServerThread
=
new
Thread(
new
ThreadStart(RecieveAccept));
//
将接受客户端连接的方法委托给线程
ServerThread.Start();
//
线程开始运行
CheckForIllegalCrossThreadCalls
=
false
;
//
不捕获对错误线程的调用
this
.CmdStar.Enabled
=
false
;
this
.CmdStop.Enabled
=
true
;
this
.StateMsg.Text
=
"
服务正在运行...
"
+
"
运行端口:
"
+
this
.GetPort().ToString();
this
.ClientList.Items.Add(
"
服务于
"
+
DateTime.Now.ToString()
+
"
开始运行.
"
);
}
//
接受客户端连接的方法
private
void
RecieveAccept()
{
while
(
true
)
{
//
Accept 以同步方式从侦听套接字的连接请求队列中提取第一个挂起的连接请求,然后创建并返回新的 Socket。
//
在阻止模式中,Accept 将一直处于阻止状态,直到传入的连接尝试排入队列。连接被接受后,原来的 Socket 继续将传入的连接请求排入队列,直到您关闭它。
ClientSocket[ClientNumb]
=
ServerSocket.Accept();
ClientSocket[ClientNumb].BeginReceive(MsgBuffer,
0
, MsgBuffer.Length, SocketFlags.None,
new
AsyncCallback(RecieveCallBack),ClientSocket[ClientNumb]);
lock
(
this
.ClientList)
{
this
.ClientList.Items.Add(ClientSocket[ClientNumb].RemoteEndPoint.ToString()
+
"
成功连接服务器.
"
);
}
ClientNumb
++
;
}
}
//
回发数据给客户端
private
void
RecieveCallBack(IAsyncResult AR)
{
try
{
Socket RSocket
=
(Socket)AR.AsyncState;
int
REnd
=
RSocket.EndReceive(AR);
//
对每一个侦听的客户端端口信息进行接收和回发
for
(
int
i
=
0
; i
<
ClientNumb; i
++
)
{
if
(ClientSocket[i].Connected)
{
//
回发数据到客户端
ClientSocket[i].Send(MsgBuffer,
0
, REnd,SocketFlags.None);
}
//
同时接收客户端回发的数据,用于回发
RSocket.BeginReceive(MsgBuffer,
0
, MsgBuffer.Length,
0
,
new
AsyncCallback(RecieveCallBack), RSocket);
}
}
catch
{ }
}
private
void
CmdStop_Click(
object
sender, EventArgs e)
{
ServerThread.Abort();
//
线程终止
ServerSocket.Close();
//
关闭socket
this
.CmdStar.Enabled
=
true
;
this
.CmdStop.Enabled
=
false
;
this
.StateMsg.Text
=
"
等待运行...
"
;
this
.ClientList.Items.Add(
"
服务于
"
+
DateTime.Now.ToString()
+
"
停止运行.
"
);
}
private
void
ServerMain_FormClosed(
object
sender, FormClosedEventArgs e)
{
ServerThread.Abort();
//
线程终止
ServerSocket.Close();
//
关闭SOCKET
Application.Exit();
}
}
}
Set.cs代码
using
System;
using
System.Text;
using
System.Windows.Forms;
using
System.Xml;
namespace
Server
{
public
partial
class
Set : Form
{
public
Set()
{
InitializeComponent();
}
private
void
Set_Load(
object
sender, EventArgs e)
{
this
.GetPort();
}
private
void
GetPort()
{
try
{
XmlDocument TDoc
=
new
XmlDocument();
TDoc.Load(
"
Settings.xml
"
);
string
TPort
=
TDoc.GetElementsByTagName(
"
ServerPort
"
)[
0
].InnerXml;
this
.Port.Text
=
TPort;
}
catch
{ }
}
private
void
CmdSave_Click(
object
sender, EventArgs e)
{
try
{
XmlDocument TDoc
=
new
XmlDocument();
TDoc.Load(
"
Settings.xml
"
);
XmlElement Root
=
TDoc.DocumentElement;
XmlElement newElem
=
TDoc.CreateElement(
"
ServerPort
"
);
newElem.InnerXml
=
this
.Port.Text;
Root.ReplaceChild(newElem, Root.LastChild);
TDoc.Save(
"
Settings.xml
"
);
MessageBox.Show(
"
参数保存成功!
"
);
this
.Close();
}
catch
{
MessageBox.Show(
"
参数写入XML文件不成功!
"
);
}
}
}
}
附:
Settings.xml
<?
xml version="1.0" encoding="UTF-8" standalone="yes"
?>
<
ServerPort
>
6600
</
ServerPort
>
******************************************************************************************************
【客户端代码】ClientMain.cs窗体
using
System;
using
System.Text;
using
System.Windows.Forms;
using
System.Net;
using
System.Net.Sockets;
namespace
Client
{
public
partial
class
ClientMain : Form
{
public
ClientMain()
{
InitializeComponent();
}
private
IPEndPoint ServerInfo;
private
Socket ClientSocket;
//
信息接收缓存
private
Byte[] MsgBuffer;
//
信息发送存储
private
Byte[] MsgSend;
private
void
ClientMain_Load(
object
sender, EventArgs e)
{
this
.CmdSend.Enabled
=
false
;
this
.CmdExit.Enabled
=
false
;
//
定义一个IPV4,TCP模式的Socket
ClientSocket
=
new
Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
MsgBuffer
=
new
Byte[
65535
];
MsgSend
=
new
Byte[
65535
];
//
允许子线程刷新数据
CheckForIllegalCrossThreadCalls
=
false
;
this
.UserName.Text
=
Environment.MachineName;
}
private
void
CmdEnter_Click(
object
sender, EventArgs e)
{
//
服务端IP和端口信息设定,这里的IP可以是127.0.0.1,可以是本机局域网IP,也可以是本机网络IP
ServerInfo
=
new
IPEndPoint(IPAddress.Parse(
this
.ServerIP.Text), Convert.ToInt32(
this
.ServerPort.Text));
try
{
//
客户端连接服务端指定IP端口,Sockket
ClientSocket.Connect(ServerInfo);
//
将用户登录信息发送至服务器,由此可以让其他客户端获知
ClientSocket.Send(Encoding.Unicode.GetBytes(
"
用户:
"
+
this
.UserName.Text
+
"
进入系统!\n
"
));
//
开始从连接的Socket异步读取数据。接收来自服务器,其他客户端转发来的信息
//
AsyncCallback引用在异步操作完成时调用的回调方法
ClientSocket.BeginReceive(MsgBuffer,
0
, MsgBuffer.Length, SocketFlags.None,
new
AsyncCallback(ReceiveCallBack),
null
);
this
.SysMsg.Text
+=
"
登录服务器成功!\n
"
;
this
.CmdSend.Enabled
=
true
;
this
.CmdEnter.Enabled
=
false
;
this
.CmdExit.Enabled
=
true
;
}
catch
{
MessageBox.Show(
"
登录服务器失败,请确认服务器是否正常工作!
"
);
}
}
private
void
ReceiveCallBack(IAsyncResult AR)
{
try
{
//
结束挂起的异步读取,返回接收到的字节数。 AR,它存储此异步操作的状态信息以及所有用户定义数据
int
REnd
=
ClientSocket.EndReceive(AR);
lock
(
this
.RecieveMsg)
{
this
.RecieveMsg.AppendText(Encoding.Unicode.GetString(MsgBuffer,
0
, REnd));
}
ClientSocket.BeginReceive(MsgBuffer,
0
, MsgBuffer.Length,
0
,
new
AsyncCallback(ReceiveCallBack),
null
);
}
catch
{
MessageBox.Show(
"
已经与服务器断开连接!
"
);
this
.Close();
}
}
//
点击发送之后没有直接添加到信息列表中,而是传到服务器,由服务器转发给每个客户端
private
void
CmdSend_Click(
object
sender, EventArgs e)
{
MsgSend
=
Encoding.Unicode.GetBytes(
this
.UserName.Text
+
"
说:\n
"
+
this
.SendMsg.Text
+
"
\n
"
);
if
(ClientSocket.Connected)
{
//
将数据发送到连接的 System.Net.Sockets.Socket。
ClientSocket.Send(MsgSend);
this
.SendMsg.Text
=
""
;
}
else
{
MessageBox.Show(
"
当前与服务器断开连接,无法发送信息!
"
);
}
}
private
void
CmdExit_Click(
object
sender, EventArgs e)
{
if
(ClientSocket.Connected)
{
ClientSocket.Send(Encoding.Unicode.GetBytes(
this
.UserName.Text
+
"
离开了房间!\n
"
));
//
禁用发送和接受
ClientSocket.Shutdown(SocketShutdown.Both);
//
关闭套接字,不允许重用
ClientSocket.Disconnect(
false
);
}
ClientSocket.Close();
this
.CmdSend.Enabled
=
false
;
this
.CmdEnter.Enabled
=
true
;
this
.CmdExit.Enabled
=
false
;
}
private
void
RecieveMsg_TextChanged(
object
sender, EventArgs e)
{
this
.RecieveMsg.ScrollToCaret();
}
private
void
SendMsg_KeyDown(
object
sender, KeyEventArgs e)
{
if
(e.Control
&&
e.KeyValue
==
13
)
{
e.Handled
=
true
;
this
.CmdSend_Click(
this
,
null
);
}
}
}
}