本文是本人在做项目期间,简单根据自己所需需求封装的一个TCP通讯的函数类,包含了客户端、服务端,描写的较为简单,很适合新手小白参考学习
服务端:
实现较为简单 主要用于监听客户端等,并将每次连接的客户端套接字,放入了键值对当中,可以通过对用IP地址当键查出套接字,然后发送数据,实现1对1通讯(键值对为TcpKeyValue ),程序中注释部分为心跳包及其他用途。
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace DataCollectionSystem.MenuBar
{
class TcpCommunication
{
private TcpListener listener; // 监听器
private TcpClient client; // 客户端连接
public NetworkStream stream; // 网络流对象
private int heartbeatInterval = 5000; // 心跳检测时间间隔
private byte[] heartBeatMsg = Encoding.ASCII.GetBytes("I am alive!"); // 心跳消息内容
private DateTime lastHeartBeatTime; // 上次接收到心跳消息的时间
//线程锁(注意!!!!!)
private static object myLock = new object();
//存放所有的网络流对象
public Dictionary<string, NetworkStream> TcpKeyValue = new Dictionary<string, NetworkStream>();
//存放所有的客户端连接Client
List<TcpClient> ClientList = new List<TcpClient>();
// 事件委托和事件定义
public delegate void MessageReceivedEventHandler(string message);
public event MessageReceivedEventHandler OnMessageReceived;
public delegate void ClientDisconnectedEventHandler(string IP, int prot);
public event ClientDisconnectedEventHandler OnClientDisconnected;
// 启动监听器并等待客户端连接
public void Start(string localIP, int port)
{
listener = new TcpListener(IPAddress.Parse(localIP), port);
listener.Start();
Console.WriteLine("等待客户端连接...");
while (true) // 循环等待新的客户端连接
{
client = listener.AcceptTcpClient(); // 接受客户端连接请求
ClientList.Add(client);
stream = client.GetStream(); // 获取网络流对象
IPEndPoint remoteIpEndpoint = (IPEndPoint)client.Client.RemoteEndPoint;
string remoteIpAddress = remoteIpEndpoint.Address.ToString();
int remotePort = remoteIpEndpoint.Port;
Console.WriteLine(remoteIpAddress + " " + remotePort);
lock (myLock)
{
if (!TcpKeyValue.ContainsKey(remoteIpAddress))
{
TcpKeyValue.Add(remoteIpAddress, stream);
}
else
{
TcpKeyValue[remoteIpAddress] = stream;
}
}
Console.WriteLine("客户端已连接");
// 初始化心跳检测时间
lastHeartBeatTime = DateTime.Now;
// 创建一个新线程处理客户端请求
Thread t = new Thread(new ThreadStart(HandleClientComm));
t.Start();
启动心跳包线程
//Thread heartbeatThread = new Thread(SendHeartbeat);
//heartbeatThread.IsBackground = true;
//heartbeatThread.Start();
}
}
// 处理客户端消息的方法
private void HandleClientComm()
{
while (true) // 循环接收客户端消息
{
// 接收客户端消息
byte[] buffer = new byte[1024];
int bytesRead = stream.Read(buffer, 0, buffer.Length);
if (bytesRead == 0) // 客户端已断开连接
{
IPEndPoint remoteIpEndpoint = (IPEndPoint)client.Client.RemoteEndPoint;
string remoteIpAddress = remoteIpEndpoint.Address.ToString();
int remotePort = remoteIpEndpoint.Port;
OnClientDisconnected?.Invoke(remoteIpAddress, remotePort); // 触发客户端断开连接事件
ClientList.Remove(client);
TcpKeyValue.Remove(remoteIpAddress);
break;
}
string data = Encoding.ASCII.GetString(buffer, 0, bytesRead);
//接收到消息
OnMessageReceived?.Invoke(data); // 触发收到消息事件
// 发送响应消息给客户端
//byte[] response = System.Text.Encoding.ASCII.GetBytes("我已经收到你的消息了!");
//stream.Write(response, 0, response.Length);
}
client.Dispose();
client.Close();
}
//发送心跳
private void SendHeartbeat()
{
while (true)
{
byte[] heartbeat = Encoding.UTF8.GetBytes("heartbeat");
foreach (KeyValuePair<string, NetworkStream> pair in TcpKeyValue)
{
Console.WriteLine("{0}: {1}", pair.Key, pair.Value);
try
{
// 在这里调用 Write 方法
pair.Value.Write(heartBeatMsg, 0, heartBeatMsg.Length);
}
catch (SocketException ex)
{
IPEndPoint remoteIpEndpoint = (IPEndPoint)client.Client.RemoteEndPoint;
string remoteIpAddress = remoteIpEndpoint.Address.ToString();
int remotePort = remoteIpEndpoint.Port;
OnClientDisconnected?.Invoke(remoteIpAddress, remotePort);
获取引起异常的 TcpClient 对象
//TcpClient client = (TcpClient)ex.SocketErrorCode;
获取连接断开的客户端的 IP 地址
//string clientIP = ((IPEndPoint)client.RemoteEndPoint).Address.ToString();
输出客户端 IP 地址
//Console.WriteLine("客户端 {0} 连接已断开", clientIP);
}
}
Thread.Sleep(heartbeatInterval); // 每隔 5 秒发送一次心跳包
}
}
//结束所有的客户端
public void ClientDispose()
{
foreach (KeyValuePair<string, NetworkStream> pair in TcpKeyValue)
{
pair.Value.Close();
}
foreach (TcpClient client in ClientList)
{
client.Close();
}
}
}
}
服务端使用说明:
服务端代码示例:
客户端:
此为客户端代码,增加了断线重连及提醒委托。
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using Tools;
namespace DataCollectionSystem_attach.Subpage
{
public class TcpClientWrapper
{
private readonly string _serverAddress; //IP地址
private readonly int _port; //端口号
private TcpClient _client; //套接字
private NetworkStream _stream; //网络字节流
private Thread _receiveThread; //线程
public bool _isConnected = false; //判断网络连接
public bool _isRunning; //判断线程是否在运行
string Heartbeat_Data = "OK"; //发送心跳的内容
public delegate void MessageReceivedHandler(string message);
public event MessageReceivedHandler MessageReceived; //接收信息委托
public delegate void CommunicationStatus_NO();
public event CommunicationStatus_NO CStatus_NO; //通讯状态
public delegate void CommunicationStatus_OK();
public event CommunicationStatus_OK CStatus_OK; //接收信息委托
public TcpClientWrapper(string serverAddress, int port)
{
_serverAddress = serverAddress;
_port = port;
}
//连接函数
public void Connect()
{
_client = new TcpClient();
try
{
_client.Connect(_serverAddress, _port);
_stream = _client.GetStream();
_isConnected = true;
_isRunning = true;
//启动心跳检测线程
var heartbeatThread = new Thread(SendHeartbeat);
heartbeatThread.IsBackground = true;
heartbeatThread.Start();
}
catch (Exception)
{
Thread.Sleep(1000);
Reconnect1();
//Console.WriteLine("aaa");
}
启动接收线程
//_receiveThread = new Thread(ReceiveMessage);
//_receiveThread.IsBackground = true;
//_receiveThread.Start();
}
//断开连接释放资源
public void Disconnect()
{
_isConnected = false;
_isRunning = false;
try
{
if (_client != null)
{
_client.Close();
}
}
catch (Exception)
{
}
}
//发送消息
public void SendMessage(byte[] message)
{
if (!_isConnected) return;
try
{
_stream.Write(message, 0, message.Length);
}
catch (Exception e)
{
_isConnected = false;
//Console.WriteLine($"发送消息失败:{e.Message}");
LogHelper.AddErrorLog($"发送消息失败: {e.Message}.");
}
}
//读取消息
private void ReceiveMessage()
{
while (_isRunning)
{
try
{
if (_client.Available > 0)
{
var buffer = new byte[_client.Available];
_stream.Read(buffer, 0, buffer.Length);
//Console.WriteLine($"接收到消息:{Encoding.UTF8.GetString(buffer)}");
MessageReceived?.Invoke(Encoding.UTF8.GetString(buffer)); //委托消息
}
}
catch (Exception e)
{
Console.WriteLine($"接收消息失败:{e.Message}");
Reconnect();
}
Thread.Sleep(50);
}
}
//发送心跳消息
private void SendHeartbeat()
{
while (_isRunning)
{
Thread.Sleep(50);
try
{
//SendMessage(Encoding.UTF8.GetBytes("Heartbeat"));
_stream.Write(Encoding.UTF8.GetBytes(Heartbeat_Data), 0, Heartbeat_Data.Length);
//CStatus_OK?.Invoke();
}
catch (Exception e)
{
//Console.WriteLine($"发送心跳失败:{e.Message}");
CStatus_NO?.Invoke();
Reconnect();
break;
}
Thread.Sleep(5950); //10秒钟发送一次心跳包
}
}
//重连函数
public void Reconnect()
{
Disconnect();
while (!_isConnected)
{
try
{
Connect();
}
catch (Exception e)
{
//Console.WriteLine($"重连失败:{e.Message}");
}
Thread.Sleep(3000); //3秒钟尝试重新连接一次
}
}
//重连函数
public void Reconnect1()
{
Disconnect();
try
{
Connect();
}
catch (Exception e)
{
//Console.WriteLine($"重连失败:{e.Message}");
}
}
}
}
客户端使用说明:
本文中注释部分是预留部分,其中服务端心跳包部分被注释。
本代码只用于互相讨论学习适合新手研究编写网络通讯方面代码,不对之处请大家指正修改。