C++Socket通讯样例(服务端)

1. 创建Socket实例并开启。

cs 复制代码
private int OpenTcp(int port, string ip = "")
{
    //1. 开启服务端
    try
    {
        _tcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IPAddress ipAddr = IPAddress.Any;
        if (ip != "" && ip != string.Empty)
        {
            ipAddr = IPAddress.Parse(ip);
        }
        _tcpServer.Bind(new IPEndPoint(ipAddr, port));
        _tcpServer.Listen();
        _isListening = true;

        //开启线程
        Thread listenThread = new Thread(new ThreadStart(AcceptClients));
        listenThread.Start();
    }

    catch (Exception ex)
    {
        Log.Instance.Error("TcpServer setup fail!\n" + CommonMsg.Space + ex.Message);
    }
    return 0;
}

2. 客户端连接------AcceptClients实现。

cs 复制代码
private void AcceptClients()
{
    while (_isListening)
    {
        //单客户端
        _tcpSocket = _tcpServer.Accept();
        if (_tcpSocket != null)
        {
            Thread clientThread = new Thread(SocketReceive);
            clientThread.Start(_tcpSocket);
            Log.Instance.Info($"Socket connected!  ( {_tcpSocket.LocalEndPoint} )");
        }
        Thread.Sleep(300);
    }
}

3. 接收客户端信息------SocketReceive实现。

(仅仅将接收的数据放到队列中,不加处理,否则可能耗时而影响通讯接收)

cs 复制代码
private void SocketReceive(object? obj)
{
    Socket? tcpClient = (Socket?)obj;
    if (tcpClient == null) return;

    //以下代码不放入线程中
    while (true)
    {
        if (tcpClient.Connected)
        {
            int available = tcpClient.Available;
            if (available > 0)
            {
                byte[] buffer = new byte[available];
                int dataSize = tcpClient.Receive(buffer);
                if (dataSize != available) { MessageBox.Show("Socket received data not equal to available data"); break; }

                _queueMsgRecv.Enqueue(buffer);
            }
            
        }
        Thread.Sleep(5);
    }
    tcpClient.Close();
}

4. 开启数据处理线程------OpenMsgParseThread

(不断从接收队列中取数据,根据通讯协议进行解析,并处理)

cs 复制代码
private void OpenMsgParseThread()      
{
    Task.Run(() =>
    {
        while (true)
        {
            while (_queueMsgRecv.TryDequeue(out byte[]? buffer))
            {
                if (buffer != null && buffer.Length > 0)
                {
                    //查询list 中是否存在结束符
                    while (true)
                    {
                        List<byte> bufList = buffer.ToList();
                        int flagIdx = bufList.IndexOf(0x04);
                        if (flagIdx == -1)
                        {
                            //不存在结束符
                            _msgValid += Encoding.UTF8.GetString(buffer);
                            break;
                        }
                        else
                        {
                            //存在结束符
                            _msgValid += Encoding.UTF8.GetString(buffer.Take(flagIdx).ToArray());
                            buffer = buffer.Skip(flagIdx + 1).Take(buffer.Length - flagIdx - 1).ToArray();
                            try
                            {
                                JObject jObj = JObject.Parse(_msgValid);  //这个地方 如果发来的数据格式有问题,或者不完整,解析会出现异常
                                ProcessRecvMsg(jObj);
                            }
                            catch (Exception ex)
                            {
                                Log.Instance.Error("Msg: " + _msgValid);
                                _msgValid = "";
                                byte[] data = LeadlapTool.CreateComnData(MsgToken.TcpDataParseException, null, false, ex.Message);
                                SendMsg(data);
                                continue;
                            }
                            _msgValid = "";
                            continue;
                        }
                    }
                }
            }
            Thread.Sleep(5);
        }
    });
}

5. 开启数据发送线程------OpenMsgSendThread

(不断从发送队列取数据,执行发送)

cs 复制代码
private void OpenMsgSendThread()
{
    Task.Run(() =>
    {
        while (true)
        {
            if (_tcpSocket.Connected)
            {
                lock (_locker)
                {
                    bool ret = _queueMsgSend.TryDequeue(out byte[]? data);
                    if (ret && data != null)
                    {
                        _tcpSocket.Send(data);
                    }
                }
            }
            Thread.Sleep(10);
        }
    });
}

6. 在需要发送数据处执行发送操作------SendMsg

cs 复制代码
lock (_locker)
{
    _queueMsgSend.Enqueue(data);
}

7. 关于数据解析

通常需要约定好帧头、帧尾,防止数据丢包、粘包情况。

(1)描述:数据为 byte[],以 0x04 为结束符。

(2)分析:如果数据发送由于阻塞,导致粘包;或者如果发送数据量过大,分好几次才接收完整,导致拆包;

(3)解决:粘包------对数据包解析识别结束符 0x04 并分割,分别解析。

拆包------对数据包叠加,直至识别到结束符 0x04,整合后解析。

(4)数据类型转换。

0x04 作为 byte比较。用 List 的 IndexOf 函数判断,若是-1,则不存在;否则返回 序号。

byte[] --> List: buffer.ToList();

List --> byte[]: bufList.ToArray();

byte[] --> string: Encoding.UTF8.GetString(buffer);

string --> byte[]: Encoding.UTF8.GetBytes(msg);

buffer.Take(flagIdx).ToArray();

相关推荐
数据皮皮侠AI1 小时前
中国城市可再生能源数据集(2005-2021)|顶刊 Sci Data 11 种能源面板
大数据·人工智能·笔记·能源·1024程序员节
Eiceblue2 小时前
使用 C# 将 Excel 转换为 Markdown 表格(含批量转换示例)
开发语言·c#·excel
AIwenIPgeolocation3 小时前
IP地址数据服务:赋能游戏行业体验优化与精细化运营
网络协议·tcp/ip·游戏
TechWayfarer4 小时前
2026年IP归属地查询平台选型指南:金融风控、异地登录、离线库全场景实测
网络·网络协议·tcp/ip
不会编程的懒洋洋4 小时前
WPF XAML+布局+控件
xml·开发语言·c#·视觉检测·wpf·机器视觉·视图
唐青枫4 小时前
别再层层传参了!C#.NET AsyncLocal 异步上下文透传实战
c#·.net
2401_873479405 小时前
深度解析IP查询工具与普通IP库的核心区别:选型指南与业务场景对照
网络协议·tcp/ip·php
明如正午5 小时前
【C#】托管调试助手 “PInvokeStackImbalance“:的调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。
c#
Eiceblue6 小时前
C# 如何实现 Word 转 Excel ?分享两种实用方法
c#·word·excel
天才少女爱迪生6 小时前
word格式规范检测+自动修改【python】
python·c#·word