C# 超简单的创建高并发、高性能TCP服务器,可用于上位机、PLC、游戏服务器等

说起TCP大家肯定都不陌生,传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793 定义。
TCP旨在适应支持多网络应用的分层协议层次结构。 连接到不同但互连的计算机通信网络的主计算机中的成对进程之间依靠TCP提供可靠的通信服务。TCP假设它可以从较低级别的协议获得简单的,可能不可靠的数据报服务。 原则上,TCP应该能够在从硬线连接到分组交换或电路交换网络的各种通信系统之上操作。

那么

对于C#又应该怎么使用TCP呢,实际上微软已经设置了TCP协议的抽象模型类,也就是大家熟知的Socket,以及更高级的TcpClient等,诸如此类的使用,这里也不做多解释。但是对于高并发高性能等问题,这些偏底层的类就需要自己开发了,所以这也是我自己开发RRQMSocket的初衷,希望这个程序集能给你带来惊喜。


一、概述

首先我们先来介绍一下该程序集的一些功能

  1. 完全开源,免费商业使用。
  2. 简单易用
  3. 多线程。
  4. 多地址监听(可以一次性监听多个IP及端口)
  5. 适配器预处理,一键式解决分包粘包、对象解析(如HTTP,Json)等。
  6. 超简单的同步发送、异步发送、接收等操作。
  7. 基于事件驱动,让每一步操作尽在掌握。
  8. 高性能(服务器每秒可接收2.5G流量,约200w条信息)

二、程序集源码、Demo下载

2.1 源码位置

三、安装

Nuget安装RRQMSocket即可,有疑问的小伙伴具体步骤详看链接博客。

VS、Unity安装和使用Nuget包

四、创建TcpService

TcpService是TCP服务器基类,但是不参与实际的数据交互,实际的数据交互由SocketClient完成,所以TcpService的功能只是配置、激活、管理、注销SocketClient类实例。

TcpService service = new TcpService();
service.Connecting += (client, e) => { };//有客户端正在连接
service.Connected += (client, e) => { };//有客户端连接
service.Disconnected += (client, e) => { };//有客户端断开连接
service.Received += (client, byteBlock, requestInfo) =>
{
    //从客户端收到信息
    string mes = Encoding.UTF8.GetString(byteBlock.Buffer, 0, byteBlock.Len);
    Console.WriteLine($"已从{client.ID}接收到信息:{mes}");

    client.Send(mes);//将收到的信息直接返回给发送方

    //client.Send("id",mes);//将收到的信息返回给特定ID的客户端

    var clients = service.GetClients();
    foreach (var targetClient in clients)//将收到的信息返回给在线的所有客户端。
    {
        if (targetClient.ID!=client.ID)
        {
            targetClient.Send(mes);
        }
    }
};
                                                                                                             
service.Setup(new RRQMConfig()//载入配置     
    .SetListenIPHosts(new IPHost[] { new IPHost("127.0.0.1:7789"), new IPHost(7790) })//同时监听两个地址
    .SetMaxCount(10000)
    .SetThreadCount(100))
    .Start();//启动

五、创建客户端

TcpClient tcpClient = new TcpClient();
tcpClient.Connected += (client, e) => { };//成功连接到服务器
tcpClient.Disconnected += (client, e) => { };//从服务器断开连接,当连接不成功时不会触发。
tcpClient.Received += (client, byteBlock, requestInfo) =>
{
    //从服务器收到信息
    string mes = Encoding.UTF8.GetString(byteBlock.Buffer, 0, byteBlock.Len);
    Console.WriteLine($"接收到信息:{mes}");
};

//声明配置
RRQMConfig config = new RRQMConfig();
config.SetRemoteIPHost(new IPHost("127.0.0.1:7789"))
    .UsePlugin()
    .SetBufferLength(1024*10);

//载入配置
tcpClient.Setup(config);
tcpClient.Connect();
tcpClient.Send("RRQM");

六、总结

实际上,RRQMSocket的方便之处,远不仅仅是接收和发送。它能一键式解决粘分包问题,也能超简单的解决自定义的协议数据。所以,更多的使用说明,请看详细的说明文档

  • 8
    点赞
  • 74
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 15
    评论
1、在C#中,不用去面对完成端口的操作系统内核对象,Microsoft已经为我们提供了SocketAsyncEventArgs类,它封装了IOCP的使用。请参考:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socketasynceventargs.aspx?cs-save-lang=1&cs-lang=cpp#code-snippet-1。 2、我的SocketAsyncEventArgsPool类使用List对象来存储对客户端来通信的SocketAsyncEventArgs对象,它相当于直接使用内核对象时的IoContext。我这样设计比用堆栈来实现的好处理是,我可以在SocketAsyncEventArgsPool池中找到任何一个与服务器连接的客户,主动向它发信息。而用堆栈来实现的话,要主动给客户发信息,则还要设计一个结构来存储已连接上服务器的客户。 3、对每一个客户端不管还发送还是接收,我使用同一个SocketAsyncEventArgs对象,对每一个客户端来说,通信是同步进行的,也就是说服务器高度保证同一个客户连接上要么在投递发送请求,并等待;或者是在投递接收请求,等待中。本例只做echo服务器,还未考虑由服务器主动向客户发送信息。 4、SocketAsyncEventArgs的UserToken被直接设定为被接受的客户端Socket。 5、没有使用BufferManager 类,因为我在初始化时给每一个SocketAsyncEventArgsPool中的对象分配一个缓冲区,发送时使用Arrary.Copy来进行字符拷贝,不去改变缓冲区的位置,只改变使用的长度,因此在下次投递接收请求时恢复缓冲区长度就可以了!如果要主动给客户发信息的话,可以new一个SocketAsyncEventArgs对象,或者在初始化中建立几个来专门用于主动发送信息,因为这种需求一般是进行信息群发,建立一个对象可以用于很多次信息发送,总体来看,这种花销不大,还减去了字符拷贝和消耗。
下面是一个 C# 上位机PLC 基于 Modbus TCP 通讯的程序示例: ```csharp using System; using System.Net.Sockets; using System.Threading; namespace ModbusTcp { class Program { static void Main(string[] args) { // PLC IP地址和端口号 string ip = "192.168.1.100"; int port = 502; // 创建TCP连接 TcpClient client = new TcpClient(ip, port); // 创建Modbus协议对象 ModbusTcpProtocol protocol = new ModbusTcpProtocol(client); // 连接到PLC protocol.Connect(); // 读取PLC寄存器值 ushort[] values = protocol.ReadHoldingRegisters(0, 10); // 输出读取到的值 for (int i = 0; i < values.Length; i++) { Console.WriteLine("Register {0}: {1}", i, values[i]); } // 关闭连接 protocol.Disconnect(); client.Close(); } } // Modbus TCP 协议类 class ModbusTcpProtocol { TcpClient client; // TCP客户端对象 NetworkStream stream; // 网络流对象 // 构造函数 public ModbusTcpProtocol(TcpClient client) { this.client = client; this.stream = client.GetStream(); } // 连接到PLC public void Connect() { // 发送连接请求 byte[] connectRequest = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x0A }; stream.Write(connectRequest, 0, connectRequest.Length); // 读取响应 byte[] response = new byte[12]; stream.Read(response, 0, response.Length); // 检查响应是否为连接确认 if (response[7] != 0x03 || response[8] != 0x00 || response[9] != 0x00 || response[10] != 0x00 || response[11] != 0x0A) { throw new Exception("Failed to connect to PLC"); } } // 关闭连接 public void Disconnect() { // 发送断开连接请求 byte[] disconnectRequest = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x0A }; stream.Write(disconnectRequest, 0, disconnectRequest.Length); } // 读取保持寄存器 public ushort[] ReadHoldingRegisters(ushort startAddress, ushort numRegisters) { // 发送读取请求 byte[] request = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, (byte)(startAddress >> 8), (byte)startAddress, (byte)(numRegisters >> 8), (byte)numRegisters }; stream.Write(request, 0, request.Length); // 读取响应 byte[] response = new byte[9 + numRegisters * 2]; stream.Read(response, 0, response.Length); // 解析响应 ushort[] values = new ushort[numRegisters]; for (int i = 0; i < numRegisters; i++) { values[i] = (ushort)(response[9 + i * 2] << 8 | response[10 + i * 2]); } return values; } } } ``` 需要注意的是,这只是一个简单的示例程序,实际应用中需要根据具体的设备和通讯方式进行修改和调试。
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

若汝棋茗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值