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 _dictSocket = new Dictionary(); private readonly Dictionary _dictThread = new Dictionary(); private readonly string _strServerIp = "127.0.0.1"; //服务器的IP地址 private readonly string _strServerPort = "8080"; //端口号 private Socket _mWatchSocket; private Thread _mWatchThread; // 负责监听客户端连接请求的线程; /// /// 使用模式,默认=1,接收任意数据显示;2=前两个字节为数据长度 /// 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; _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 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++; } } } } 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(10, SelectMode.SelectRead)) //10毫秒,检查套接字状态, SelectMode 参数指定要监视的套接字的类别。 { // DictSocket.Remove(DictSocket.Keys.ToArray()[i]); RaisedStatus(EnumTcpIpServer.ConnectException, "连接已断开:" + _dictSocket.Keys.ToArray()[i]); //刷新界面显示,触发一个连接异常状态枚举,并将消息传递为异常的状态 RemoveSocketClient(_dictSocket.Keys.ToArray()[i]); } } } } } #endregion 增加时钟,判断是否有断掉的连接。 } }