C#里面提供了Socket类,通过该类,可以很轻松地搭建起Socket服务端和客户端。本文将尽量以最简单的方式、最少的代码量来说明一个简易的Socket服务器是怎么搭建起来的。
首先,大概讲下整个思路和流程:
(1)首先要明白一个概念:整个服务器,实际上就是由一个Socket对象监听端口;
(2)我们会通过一个新的线程监听客户端发起连接请求,这样的好处是避免堵塞主线程;
(3)当与客户端建立起连接之后,我们还会新建线程来专门接受该socket客户端的数据。
先新建各类:
public class SocketServer
{
}
定义一些属性变量:
//端口号
private int SocketServerPort = 5002;
//处理连接请求的线程
private Thread acceptConnectReqThd;
然后写一个服务器启动的函数Start
/// <summary>
/// 启动服务器
/// </summary>
public bool Start()
{
try
{
//创建一个socket对象
Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//获取IP
IPAddress ip = IPAddress.Any;
//创建端口号
IPEndPoint port = new IPEndPoint(ip, SocketServerPort);
//监听
socketWatch.Bind(port);
Console.WriteLine("监听成功");
socketWatch.Listen(20); //设定最大的挂起长度
//新建线程来处理连接请求
acceptConnectReqThd = new Thread(AcceptConnectReqHandler);
acceptConnectReqThd.IsBackground = true;
acceptConnectReqThd.Start(socketWatch); //把socket对象当做参数传递给到线程里面的方法
return true;
}
catch(Exception e)
{
return false;
}
}
执行Start函数之后,我们就创建了一个socket对象,并监听我们设定的端口号5002。并添加了个线程来处理客户端的连接请求。
接下来我们实现AcceptConnectReqHandler方法,即线程驱动的方法:
/// <summary>
/// 连接请求的处理函数
/// </summary>
/// <param name="_socket"></param>
private void AcceptConnectReqHandler(object _socket)
{
try
{
//服务端的socket对象
Socket serverSocket = (Socket)_socket;
while (true)
{
//获取客户端socket。Accept方法处理任何传入的连接请求,并返回可用于与远程主机通信数据的Socket对象,即客户端的socket。
//这一句话会卡主线程。只要没有新的链接进来,就会一直卡主不动(等待中)。
//收到连接事件后,会往下执行,通过while又回到这里继续等待
Socket clientSocket = serverSocket.Accept();
//创建接受客户端消息的线程
Thread acceptMsgReqThd = new Thread(ReciveMsgReqHandler);
acceptMsgReqThd.IsBackground = true;
acceptMsgReqThd.Start(clientSocket);
}
}
catch (Exception e)
{
Console.WriteLine("服务端处理连接事件异常:" + e.ToString());
}
}
这个线程驱动的函数里面,只要收到一个客户端的请求链接,就会再新建一个线程来收数据。有N个客户端链接,就会添加N个线程。
现在我们来实现收数据的方法ReciveMsgReqHandler
/// <summary>
/// 接收客户端socket消息
/// </summary>
/// <param name="_socket"></param>
private void ReciveMsgReqHandler(object _socket)
{
Socket clientSocket = (Socket)_socket;
try
{
while(true)
{
//客户端连接成功后,接受来自客户端的消息
if (clientSocket == null)
{
continue;
}
byte[] buffer = new byte[MAX_SEND_FILE_LENGTH]; //数据缓冲区。
//实际接收到的有效字节数
//Receive也是个卡线程的方法
Console.WriteLine("等待接受客户端的数据:");
int dataLength = clientSocket.Receive(buffer);
Console.WriteLine("接受到客户端的数据,字节数:" + dataLength);
//如果客户端关闭,发送的数据就为空,就跳出循环
if (dataLength == 0)
{
break;
}
//假设收到的是个字符串(先这么假定),转成字符串处理
string strMsg = Encoding.UTF8.GetString(buffer, 1, dataLength - 1);
Console.WriteLine("接受到客户端的消息:" + strMsg);
}
//中止当前线程
Thread.CurrentThread.Abort();
}
catch(Exception e)
{
SocketException socketExp = e as SocketException;
if (socketExp!=null && socketExp.NativeErrorCode == 10054)
{
Console.WriteLine("socket客户端关闭:" + e.ToString());
}
else
{
Console.WriteLine("======接受消息异常:" + e.ToString());
}
//中止当前线程
Thread.CurrentThread.Abort();
}
}
就这样一个简易的socket服务器就搭建起来了。
调用方式:
SGSocketServer socketServer = new SGSocketServer();
socketServer.Start();
当然,要达到工程应用级别,还要做更多一些工作。但千里之行始于足下,有了这个基础之后就可以不断地往里面添加业务逻辑。