Select
C# 实现Select模型
服务端
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Timers;
using TCPManager.CommunicationPackage;
namespace TCPManager.TCPCommunicationSelect
{
///
/// 开启服务端TCP服务
/// Select模型
///
public class TCPServer : ICommunication
{
Socket _socketServer;
Thread _selectThread;
byte[] _bufferReceive = new byte[1024];
bool _run = true;
object _lockObject = new object();
List m_readSockets = new List();
List m_writeSockets = new List();
List m_errorSockets = new List();
List m_allSockets = new List();
public Action DealData;
public TCPServer(string serverIp, int serverPort)
{
_selectThread = new Thread(() =>
{
ListenClientConnect();
});
_socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
m_allSockets.Add(_socketServer);
m_readSockets.AddRange(m_allSockets);
m_writeSockets.AddRange(m_allSockets);
m_errorSockets.AddRange(m_allSockets);
_socketServer.Bind(new IPEndPoint(IPAddress.Parse(serverIp), serverPort));
_socketServer.Listen(10);
}
private void ListenClientConnect()
{
while (_run)
{
Socket.Select(m_readSockets, m_writeSockets, m_errorSockets, 1000);
// 可读
foreach (var socket in m_readSockets)
{
if (socket == _socketServer)
{
Socket clientSocket = _socketServer.Accept();
if (clientSocket != null)
{
uint dummy = 0;
byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0); // 是否启用Keep-Alive
BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, Marshal.SizeOf(dummy)); // 多长时间开始第一次探测
BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2); // 探测时间间隔
// 打开TCP-KeepAlive
clientSocket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
AddSocket(clientSocket);
DealData(clientSocket.RemoteEndPoint.ToString() + ":连接成功");
}
}
else
{
try
{
int recvNum = socket.Receive(_bufferReceive);
// 服务端应该把不同客户端收到的数据分开处理 避免粘包问题 ParseData未作区分
List msgList = ParseData.Parse(_bufferReceive, recvNum);
if (msgList != null)
{
foreach (var msg in msgList)
{
if (DealData != null)
{
DealData(msg);
}
}
}
// 可以给予回复 或 根据协议转发
socket.Send(PackData.Pack(Encoding.Unicode.GetBytes("OK")));
}
catch// 连接已经断开
{
RemoveSockets(new List() { socket });
}
}
}
// 可写
foreach (var socket in m_writeSockets)
{
}
// 错误
foreach (var socket in m_errorSockets)
{
}
// 这里处理的应该是未通过心跳检测的socket,未测试
List disconnSockets = new List();
foreach (var s in m_allSockets)
{
if (s == _socketServer)
{
continue;
}
else if (s.Connected == false)
{
disconnSockets.Add(s);
}
}
RemoveSockets(disconnSockets);
ResetSockets();
}
// 因为方法没有一直阻塞,因此可以释放资源
Dispose();
}
private void ResetSockets()
{
m_readSockets.Clear();
m_writeSockets.Clear();
m_errorSockets.Clear();
m_readSockets.AddRange(m_allSockets);
m_writeSockets.AddRange(m_allSockets);
m_errorSockets.AddRange(m_allSockets);
}
private void RemoveSockets(List sockets)
{
lock (_lockObject)
{
foreach (var socket in sockets)
{
if (m_allSockets.Contains(socket))
{
if (DealData != null)
{
DealData(socket.RemoteEndPoint.ToString() + ":断开连接");
}
socket.Close();
m_allSockets.Remove(socket);
}
}
}
}
private void AddSocket(Socket socket)
{
lock (_lockObject)
{
if (m_allSockets.Contains(socket) == false)
{
m_allSockets.Add(socket);
}
}
}
///
/// 释放所有资源
///
private void Dispose()
{
foreach (var socket in m_allSockets)
{
socket.Close();
}
}
///
/// 关闭监听线程,释放所有资源
///
public void Close()
{
_run = false;
}
///
/// 开启监听线程
///
public void Run()
{
_selectThread.Start();
}
}
}