丛林战争项目六之在客户端发送数据的时候加上数据长度

本文介绍了解决TCP通信中粘包和分包问题的方法,通过在客户端发送数据时附加数据长度来确保数据完整性和准确性。文章详细展示了如何使用C#实现这一过程,并给出了客户端和服务端的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、粘包和分包问题的解决方案问题:出现如图所示的分包时数据分割不够导致的出现问号“?”


字节转换:

1.字符串转成字节数组 System.Text.Encoding.UTF8.GetBytes

2.Int32转成字节数BitConverter.GetBytes

Encoding.UTF8.GetBytes()会将参数的每一个字符进行转换,与值的大小无关,只与内容有关

BitConverter.GetBytes()处理的都是值类型的,比如布尔值类型的、double类型的、float类型的,并将其转换成字节。会当作一个值进行处理,会根据值的大小进行转换

运行后可以发现:

1.英文字母都是占有一个字节


2.数字占有一个字节



3.中文占有三个字节



二、在客户端发送数据的时候加上数据长度

1、在TCP Client上点击鼠标右键-->添加-->新建项-->类-->填写类的名字


Message的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TCP_Client
{
    class Message
    {
        public static byte[] GetBytes(string data)
        {
            byte[] dataBytes = Encoding.UTF8.GetBytes(data);                  //将数据转换成字节数组
            int dataLength = dataBytes.Length;                                //获取数据的长度
            byte[] lengthBytes = BitConverter.GetBytes(dataLength);           //将数据长度转换成byte数组
            byte[] newBytes = lengthBytes.Concat(dataBytes).ToArray();        //将数组lengthBytes和dataBytes进行融合,转换成一个新的数组.长度在前,数据在后
            return newBytes;
        }
    }
}


客户端的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;

namespace TCP_Client
{
    class Program
    {
        static void Main(string[] args)
        {
            StartClientAnsync();
   
        }

        static void StartClientAnsync()
        {
            //创建socket
            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress ipAddress = IPAddress.Parse("192.168.1.7");               //创建ip
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 180);              //绑定端口号
            clientSocket.Connect(ipEndPoint);                                   //与远程主机建立连接

            //向客户端发送一条消息
            byte[] data = new byte[1024];
            int count = clientSocket.Receive(data);                             //接收数据
            String msg = Encoding.UTF8.GetString(data, 0, count);
            Console.Write(msg);


            for (int i=0;i<100;i++)
            {
                clientSocket.Send(Message.GetBytes(i.ToString()));	      //发送数据时调用Message中的方法
            }

            Console.ReadKey();
            clientSocket.Close();                                               //
        }

      
    }
}

服务端的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;

namespace TCP_WebServer
{
    class Program
    {
        static void Main(string[] args)
        {
            startServerSync();                                      //调用服务端
            Console.ReadKey();                                      //调用完后让程序暂停一下
        }
        static void startServerSync()
        {
            //服务端的开发
            //创建一个Socket
            Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //192.168.1.7这个是本机的ipv4地址,这个地址是会变动的,路由器运行时间久了就会变化
            //127.0.0.1是一个万用地址,如果是在本机上使用则是本机的地址,如果换一台机器,则是另一台机器的地址
            //IPAddress:xxx.xx.xx.xx   IpEndPoint:xxx.xx.xx.xx:prot
            //IPAddress iPAddress = new IPAddress(new byte[] { 192,168,1,7});       //这种方式通过传递一个数组将地址解析出来,但是这种方式不推荐
            IPAddress ipAddress = IPAddress.Parse("192.168.1.7");                   //创建ip
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 180);                 //绑定端口号
            serverSocket.Bind(ipEndPoint);                                          //绑定ip和端口号
            serverSocket.Listen(10);                                                //参数backLog为队列中最多可以处理监听多少个
            //Socket clientSocket = serverSocket.Accept();                          //接收一个客户端连接
            serverSocket.BeginAccept(AcceptCallBack,serverSocket);                  //该方法调用后会等待客户端的连接,将serverSocket作为一个参数进行传递
                               
        }


        //接收到客户端的连接
        static void AcceptCallBack(IAsyncResult ar)
        {
            Socket serverSocket = ar.AsyncState as Socket;
            Socket clientSocket = serverSocket.EndAccept(ar);                   //完成接收

            //向客户端发送一条消息
            String msg = "Hello client!你好......";                             //需要传递的内容
            byte[] data = System.Text.Encoding.UTF8.GetBytes(msg);              //因为含有中文,所以需要使用一个可以将中文解析成byte数组的方法
            clientSocket.Send(data);                                            //发送消息

            clientSocket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveCallBack, clientSocket);
            serverSocket.BeginAccept(AcceptCallBack,serverSocket);              //继续等待下一个接收

        }

        static byte[] dataBuffer = new byte[1024];
        //收到客户端的消息
        static void ReceiveCallBack(IAsyncResult ar)
        {
            Socket clientSocket = null;                          
            try
            {
                clientSocket = ar.AsyncState as Socket;                          //通过IAsyncResult获取到clientSocket
                int count = clientSocket.EndReceive(ar);                         //结束接收
                if(count == 0)
                {
                    clientSocket.Close();                                        //如果接收到的数据长度是0,则关闭客户端
                    return;
                }
                String msg = Encoding.UTF8.GetString(dataBuffer, 0, count);      //获取接收到的数据
                Console.WriteLine("从客户端接收到数据:" + msg);                 //输出获取到的数据
                //上面的代码只会完成一条上面消息的接收,要想重复调用,需要重新调用一下下面的方法,至此就完成了一个循环
                clientSocket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveCallBack, clientSocket);
            }catch(Exception e)
            {
                Console.WriteLine(e);
                if (clientSocket != null)
                {
                    clientSocket.Close();
                }
            }
            finally
            {
               
            }
        }

    }
}

运行后的结果:当客户端被启动后,将数据发送出去时进行了长度的添加并进行了发送,由于发送时使用的是UTF-8,所以没有进行正常地解析





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值