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();

相关推荐
zxy28472253011 小时前
C#的视觉库Halcon入门示例
c#·图像识别·halcon·机器视觉
此生只爱蛋4 小时前
【Linux】TCP机制
网络·网络协议·tcp/ip
打码人的日常分享4 小时前
IPD项目质量体系管理方案
大数据·运维·人工智能·信息可视化·1024程序员节
Henry Zhu1235 小时前
VPP中ARP实现第二章:IP Neighbor(IP邻居)模块详解
运维·网络·tcp/ip
老蒋新思维5 小时前
创客匠人峰会实录:AI 赋能 IP 定位破局 —— 创始人知识变现的差异化增长路径
网络·人工智能·网络协议·tcp/ip·数据挖掘·创始人ip·创客匠人
abcefg_h6 小时前
Cookie,Session的介绍和如何保持TCP连接
网络·网络协议·tcp/ip·golang
c#上位机6 小时前
halcon区域变换—shape_trans
图像处理·算法·计算机视觉·c#·halcon
FuckPatience7 小时前
C# PropertyGrid(属性编辑框)利用DisplayAttribute实现多语言
c#
渡我白衣7 小时前
Select的优化:poll
开发语言·网络·c++·人工智能·网络协议·tcp/ip·网络安全
αSIM0V8 小时前
计算机网络-ISO/OSI 和TCP/IP
网络·tcp/ip·计算机网络