一、粘包和分包问题的解决方案问题:出现如图所示的分包时数据分割不够导致的出现问号“?”
字节转换:
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,所以没有进行正常地解析