using Sy
stem;
using Sy
stem.Collection
s.Generi
c;
using Sy
stem.ComponentModel;
using Sy
stem.Data;
using Sy
stem.Drawing;
using Sy
stem.Linq;
using Sy
stem.Text;
using Sy
stem.Window
s.Forms;
using Sy
stem.Net
.sockets;
using Sy
stem.Net;
using Sy
stem.Threading;
using Sy
stem.IO;
namespace SocketServer
{
public partial
class Main : Form
{
SynchronizationCo
ntext _syncCo
ntext;
Sy
stem.Timer
s.Timer _timer;
// 信息结束符,用于判断是否完整地读取了用户发送的信息(要与客户端的信息结束符相对应)
private
String _endMarker =
"^";
// 服务端监听的 socket
private Socket _listener;
// 实例化 ManualResetEvent, 设置其初始状态为非终止状态(可入状态)
private ManualResetEvent _connectDone =
new ManualResetEvent(
false);
// 客户端 Socket 列表
private List<ClientSocketPacket> _clientList =
new List<ClientSocketPacket>(
);
public Main()
{
initializeComponent(
);
// UI 线程
_syncCo
ntext = SynchronizationCo
ntext.Current;
// 启动后台线程去运行 Socket 服务
Thread thread =
new Thread(
new ThreadStart(StartupSocketServer)
);
thread.Is
BACkground =
true;
thread.Start(
);
}
private
void StartupSocketServer()
{
// 每 10 秒运行一次计时器所指定的方法
_timer =
new Sy
stem.Timer
s.Timer(
);
_timer.Interval = 10000d;
_timer.Elapsed +=
new Sy
stem.Timer
s.ElapsedEventHandler(_timer_Elapsed
);
_timer.Start(
);
// 初始化 socket , 然后与端口绑定, 然后对端口进行监听
_listener =
new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tc
p);
_listener
.bind(
new IPEndPoint(I
paddres
s.Any,4518)
);
// Silverlight 2.0 使用 Socket 只能连接 4502-4534 端口
_listener.Listen(100
);
while (
true)
{
// 重置 ManualResetEvent,由此线程来控制 ManualResetEvent,其它到这里来的线程请等待
// 为求简单易懂,本例实际上只有主线程会在这里循环运行
_connectDone.Reset(
);
// 开始接受客户端传入的连接
_listener
.beginAccept(
new AsyncCall
BACk(OnClientConnect),
null);
// 阻止当前线程,直到当前 ManualResetEvent 调用 Set 发出继续信号
_connectDone.WaitOne(
);
}
}
private
void _timer_Elapsed(
object sender,Sy
stem.Timer
s.ElapsedEventArgs
E)
{
// 每 10 秒给所有连入的客户端发送一次消息
SendData(
String.Format(
"webabcd 对所有人说:大家好! 【信息来自服务端 {0}】",
datetiR_551_11845@e.
Now.To
String(
"hh:mm:ss"))
);
}
private
void OnClientConnect(IAsyncResult asyn
C)
{
// 当前 ManualResetEvent 调用 Set 以发出继续信号,从而允许继续执行一个或多个等待线程
_connectDone.Set(
);
ClientSocketPacket client =
new ClientSocketPacket(
);
// 完成接受客户端传入的连接的这个异步操作,并返回客户端连入的 socket
client
.socket = _listener.EndAccept(async
);
// 将客户端连入的 Socket 放进客户端 Socket 列表
_clientList.Add(client
);
SendData(
"一个新的客户端已经成功连入服务器。。。 【信息来自服务端】");
try
{
// 开始接收客户端传入的数据
client
.socket
.beginReceive(client
.buffer,client
.buffer.Length,SocketFlag
s.None,
new AsyncCall
BACk(OnDataReceived),client
);
}
catch (SocketException e
X)
{
// 处理异常
HandleException(client,e
X);
}
}
private
void OnDataReceived(IAsyncResult asyn
C)
{
ClientSocketPacket client = async.AsyncState
as ClientSocketPacket;
int count = 0;
try
{
// 完成接收数据的这个异步操作,并返回接收的字节数
if (client
.socket.Connected)
count = client
.socket.EndReceive(async
);
}
catch (SocketException e
X)
{
HandleException(client,e
X);
}
// 把接收到的数据添加进收到的字节集合内
// 本例采用UTF8编码,中文占用3字节,英文占用1字节,缓冲区为32字节
// 所以如果直接把当前缓冲区转成字符串的话可能会出现乱码,所以要等接收完用户发送的全部信息后再转成字符串
foreach (
byte b
in client
.buffer.Take(count))
{
if (b == 0)
conTinue;
// 如果是空字节则不做处理
client.ReceivedByte.Add(b
);
}
// 把当前接收到的数据转换为字符串。用于判断是否包含自定义的结束符
String received
String = UTF8Encoding.UTF8.Get
String(client
.buffer,count
);
// 如果该 Socket 在网络缓冲区中没有排队的数据 并且 接收到的数据中有自定义的结束符时
if (client
.socket.Connected && client
.socket.Available == 0 && received
String.Contains(_endMarker))
{
// 把收到的字节集合转换成字符串(去掉自定义结束符)
// 然后清除掉字节集合中的内容,以准备接收用户发送的下一条信息
String content = UTF8Encoding.UTF8.Get
String(client.ReceivedByte.ToArray()
);
content = content.
replace(_endMarker,""
);
client.ReceivedByte.Clear(
);
// 发送数据到所有连入的客户端,并在服务端做记录
SendData(content
);
_syncCo
ntext.Post(ResultCall
BACk,content
);
}
try
{
// 继续开始接收客户端传入的数据
if (client
.socket.Connected)
client
.socket
.beginReceive(client
.buffer,client
);
}
catch (SocketException e
X)
{
HandleException(client,e
X);
}
}
@H_
450_1225@/// <sum
Mary>
@H_
450_1225@///
发送数据到所有连入的客户端
@H_
450_1225@/// </sum
Mary>
@H_
450_1225@/// <param name=
"data">
需要发送的数据</param>
private
void SendData(
String data)
{
byte[] byteData = UTF8Encoding.UTF8.GetBytes(data
);
foreach (ClientSocketPacket client
in _clientList)
{
if (client
.socket.Connected)
{
try
{
// 如果某客户端 Socket 是连接状态,则向其发送数据
client
.socket
.beginSend(byteData,byteData.Length,
new AsyncCall
BACk(OnDataSent),client
);
}
catch (SocketException e
X)
{
HandleException(client,e
X);
}
}
else
{
// 某 Socket 断开了连接的话则将其关闭,并将其清除出客户端 Socket 列表
// 也就是说每次向所有客户端发送消息的时候,都会从客户端 Socket 集合中清除掉已经关闭了连接的 Socket
client
.socket.Close(
);
_clientList.Remove(client
);
}
}
}
private
void OnDataSent(IAsyncResult asyn
C)
{
ClientSocketPacket client = async.AsyncState
as ClientSocketPacket;
try
{
// 完成将信息发送到客户端的这个异步操作
if (client
.socket.Connected)
client
.socket.EndSend(async
);
}
catch (SocketException e
X)
{
HandleException(client,e
X);
}
}
@H_
450_1225@/// <sum
Mary>
@H_
450_1225@///
处理 SocketException 异常
@H_
450_1225@/// </sum
Mary>
@H_
450_1225@/// <param name=
"client">
导致异常的 ClientSocketPacket</param>
@H_
450_1225@/// <param name=
"ex">
SocketException</param>
private
void HandleException(ClientSocketPacket client,SocketException e
X)
{
// 在服务端记录异常信息,关闭导致异常的 Socket,并将其清除出客户端 Socket 列表
_syncCo
ntext.Post(ResultCall
BACk,client
.socket.RemoteEndPoint.To
String() +
" - " + ex.
messag
E);
client
.socket.Close(
);
_clientList.Remove(client
);
}
private
void ResultCall
BACk(
object result)
{
// 输出相关信息
tx
tmsg.Text += result.To
String() +
"\r\n";
}
}
}