Files
LM-Middleware/HexcalMC/Hexcal/TcpIpServer.cs
T
2025-03-29 16:20:59 +08:00

406 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using HexcalMC.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Timer = System.Windows.Forms.Timer;
namespace HexcalMC.Hexcal
{
public class TcpIpServer
{
public delegate void EventHandlerRaisedMessage(string clientIp, string msg);
public delegate void EventHandlerRaisedStatus(EnumTcpIpServer type, string msg);
public enum EnumTcpIpServer
{
StartListen = 1,
ClientConnect = 2,
SocketException = 8,
ConnectException = 9,
Exception = -1
}
private readonly Dictionary<string, Socket> _dictSocket = new Dictionary<string, Socket>();
private readonly Dictionary<string, Thread> _dictThread = new Dictionary<string, Thread>();
private readonly string _strServerIp = "127.0.0.1"; //服务器的IP地址
private readonly string _strServerPort = "8080"; //端口号
private Socket _mWatchSocket;
private Thread _mWatchThread; // 负责监听客户端连接请求的线程;
/// <summary>
/// 使用模式,默认=1,接收任意数据显示;2=前两个字节为数据长度
/// </summary>
public int UseMode = 1;
//=========================================================================
public TcpIpServer(string serverIp, string serverPort)
{
_strServerIp = serverIp;
_strServerPort = serverPort;
}
public string[] SocketNames => _dictSocket.Keys.ToArray();
public bool ConnectStatus
{
get
{
if (_mWatchSocket == null)
return false;
return _mWatchSocket.Connected && _mWatchThread != null;
}
}
public bool WatchStatus { get; private set; }
public bool StartListen()
{
try
{
//_serverTimer.Tick += ServerTimerLoop; //510,增加时钟,判断是否有断掉的连接。
//_serverTimer.Start();
_mWatchSocket =
new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //创建负责监听的套接字,注意其中的参数;
IPAddress mIpAddr = IPAddress.Parse(_strServerIp); //获得文本框中的IP对象;
IPEndPoint mEndPoint = new IPEndPoint(mIpAddr, int.Parse(_strServerPort)); //创建包含ip和端口号的网络节点对象;
try
{
_mWatchSocket.Bind(mEndPoint); // 将负责监听的套接字绑定到唯一的ip和端口上;
}
catch (SocketException se)
{
MessageBox.Show("异常:" + se.Message);
return false;
}
WatchStatus = true;
_mWatchSocket.Listen(10); // 设置监听队列的长度
_mWatchThread = new Thread(WatchThread); // 创建负责监听的线程
_mWatchThread.IsBackground = true;
_mWatchThread.Start();
RaisedStatus(EnumTcpIpServer.StartListen, "服务器启动监听成功!IP=" + _strServerIp + ", Port=" + _strServerPort);
return true;
}
catch (Exception ex)
{
MessageBox.Show("启动服务器监听失败:" + ex.Message);
return false;
}
}
public void StopListen()
{
try
{
WatchStatus = false;
if (_mWatchSocket != null)
{
_mWatchSocket.Close();
_mWatchSocket.Dispose();
_mWatchSocket = null;
}
_dictThread.Clear();
_dictSocket.Clear();
}
catch
{
throw new Exception("TCP /IP 服务器关闭失败!");
}
}
//监听线程
private void WatchThread()
{
try
{
while (WatchStatus) // 持续不断的监听客户端的连接请求;
{
// 开始监听客户端连接请求,Accept方法会阻断当前的线程;
Socket sokClient = _mWatchSocket.Accept(); // 一旦监听到一个客户端的请求,就返回一个与该客户端通信的套接字;
#region
if (_dictSocket != null)
{
if (_dictSocket.Count != 0)
{
_dictSocket.Values.ToArray();
for (int i = _dictSocket.Values.ToArray().Length - 1; i >= 0; i--)
{
if (_dictSocket.Values.ToArray()[i].Poll(10, SelectMode.SelectRead))
{
RemoveSocketClient(_dictSocket.Keys.ToArray()[i]);
}
}
}
}
#endregion
_dictSocket.Add(sokClient.RemoteEndPoint.ToString(), sokClient); // 将与客户端连接的套接字对象添加到集合中;
RaisedStatus(EnumTcpIpServer.ClientConnect,
"客户端连接成功!:" + sokClient.RemoteEndPoint);
Thread thread = new Thread(ReceiveThread);
thread.IsBackground = true;
thread.Start(sokClient);
_dictThread.Add(sokClient.RemoteEndPoint.ToString(), thread); // 将新建的线程 添加 到线程的集合中去。
}
}
catch (Exception e)
{
//throw new Exception("WatchThread异常" + e);
}
}
public event EventHandler<byte[]> DataReceived;
protected virtual void OnDataReceivedByte(byte[] data)
{
// 触发事件
DataReceived?.Invoke(this, data);
}
//接收线程
private void ReceiveThread(object sokObj)
{
Socket sokClient = sokObj as Socket;
while (WatchStatus)
{
// 定义一个2M的缓存区;
byte[] arrMsgRec = new byte[sokClient.Available];
// 将接受到的数据存入到输入 arrMsgRec中;
int length = -1;
try
{
length = sokClient.Receive(arrMsgRec); // 接收数据,并返回数据的长度;
}
catch (SocketException se)
{
RaisedStatus(EnumTcpIpServer.SocketException, "异常:SocketException=" + se.Message);
RemoveSocketClient(sokClient.RemoteEndPoint.ToString());
return;
}
catch (Exception ex)
{
RaisedStatus(EnumTcpIpServer.ConnectException, "异常:Exception=" + ex.Message);
RemoveSocketClient(sokClient.RemoteEndPoint.ToString());
return;
}
try
{
switch (UseMode)
{
case 1:
if (length > 0)
{
string strData = Encoding.Default.GetString(arrMsgRec); // 将接受到的字节数据转化成字符串;
//strData = strData.Substring(0, length);
//RaisedMessage(sokClient.RemoteEndPoint.ToString(), strData.Replace("\0", "<0>"));
RaisedMessage(sokClient.RemoteEndPoint.ToString(), strData.Replace("\0", "."));
}
;
break;
case 2:
if (length > 0 && arrMsgRec.Length > 2)
{
string strData =
Encoding.Default.GetString(arrMsgRec, 2,
arrMsgRec.Length - 2); // 将接受到的字节数据转化成字符串;
RaisedMessage(sokClient.RemoteEndPoint.ToString(), strData);
}
break;
case 3:
if (length > 0)
{
OnDataReceivedByte(arrMsgRec);
}
break;
}
}
catch (Exception ex)
{
RaisedStatus(EnumTcpIpServer.Exception, "获取Socket数据异常:ex=" + ex.Message);
}
}
}
//删除客户端对象
private void RemoveSocketClient(string strRemoteEndPoint)
{
// 从通信套接字集合中 删除被中断连接的通信套接字;
_dictSocket.Remove(strRemoteEndPoint);
// 从通信线程集合中 删除被中断连接的通信线程对象;
_dictThread.Remove(strRemoteEndPoint);
}
//发送消息
public void SendMessage(string strSocketKey, string strMsg)
{
byte[] arrMsg = Encoding.Default.GetBytes(strMsg);
_dictSocket[strSocketKey].Send(arrMsg);
}
public void SendMessage(string strSocketKey, byte[] arrMsg)
{
_dictSocket[strSocketKey].Send(arrMsg);
}
//发送数据函数(字符串, 自动添加长度(byte格式))
public string SendMessage2(string strSocketKey, string strMsg)
{
try
{
if (_dictSocket[strSocketKey].Connected)
{
byte[] arrLength = new byte[2];
arrLength = BitConverter.GetBytes(Convert.ToInt16(strMsg.Length));
byte[] writeBuffer = Encoding.Default.GetBytes(strMsg);
writeBuffer = arrLength.Concat(writeBuffer).ToArray();
_dictSocket[strSocketKey].Send(writeBuffer);
return "";
}
return "NotConnect";
}
catch (Exception ex)
{
return "error:" + ex.Message;
}
}
public bool TrySendData(Socket socket, byte[] data)
{
int sentBytes = socket.Send(data);
return sentBytes == data.Length; // 检查是否完全发送成功
}
public void SendMessageToAllClients(string strMsg)
{
byte[] arrMsg = Encoding.Default.GetBytes(strMsg);
foreach (Socket soc in _dictSocket.Values)
{
bool success = TrySendData(soc, arrMsg);
if (!success)
{
// 如果发送失败,进行重发逻辑
int attemptCount = 0;
const int maxAttempts = 3;
while (!success && attemptCount < maxAttempts)
{
success = TrySendData(soc, arrMsg);
attemptCount++;
DebugDfn.AddLogText("正在重发");
}
}
}
}
public void SendMessageToAllClients(byte[] arrMsg)
{
foreach (Socket soc in _dictSocket.Values)
{
soc.Send(arrMsg);
}
}
public event EventHandlerRaisedStatus OnRaisedStatus;
// 异步或同步触发自定义事件,并在目标控件是 Windows Forms 控件的情况下添加到目标控件的消息队列中。它主要的目的是使得自定义事件处理程序在UI线程上执行,以避免线程上的卡顿或UI更新问题。
private void RaisedStatus(EnumTcpIpServer returnType, string msg)
{
try
{
if (OnRaisedStatus != null)
{
if (OnRaisedStatus.Target is Control)
{
Control targetForm = OnRaisedStatus.Target as Control;
targetForm.BeginInvoke(OnRaisedStatus, returnType, msg);
}
else
{
OnRaisedStatus(returnType, msg);
}
}
}
catch (Exception)
{
}
}
public event EventHandlerRaisedMessage OnRaisedMessage;
private void RaisedMessage(string clientIp, string msg)
{
try
{
if (OnRaisedMessage != null)
{
if (OnRaisedMessage.Target is Control)
{
Control targetForm = OnRaisedMessage.Target as Control;
targetForm.BeginInvoke(OnRaisedMessage, clientIp, msg);
}
else
{
OnRaisedMessage(clientIp, msg);
}
}
}
catch (Exception)
{
}
}
#region
private readonly Timer _serverTimer = new Timer();
private void ServerTimerLoop(object sender, EventArgs e)
{
_serverTimer.Interval = 20000; //监听timer的间隔
if (_dictSocket != null)
{
if (_dictSocket.Count != 0)
{
_dictSocket.Values.ToArray();
for (int i = _dictSocket.Values.ToArray().Length - 1; i >= 0; i--)
{
if (_dictSocket.Values.ToArray()[i]
.Poll(100, SelectMode.SelectRead)) //10毫秒,检查套接字状态, SelectMode 参数指定要监视的套接字的类别。
{
// DictSocket.Remove(DictSocket.Keys.ToArray()[i]);
RaisedStatus(EnumTcpIpServer.ConnectException,
"连接已断开:" + _dictSocket.Keys.ToArray()[i]); //刷新界面显示,触发一个连接异常状态枚举,并将消息传递为异常的状态
RemoveSocketClient(_dictSocket.Keys.ToArray()[i]);
}
}
}
}
}
#endregion
}
}