不明白Socket通信原理的同学可以参考该文章:https://www.cnblogs.com/zxtceq/p/7728543.html点击打开链接
具体代码的编写参考该博客:https://www.cnblogs.com/sdyinfang/p/5519708.html点击打开链接
粘包和分包:
Socket通信时会对发送的字节数据进行分包和粘包处理,属于一种Socket内部的优化机制。
1.粘包:当发送的字节数据包比较小且频繁发送时,Socket内部会将字节数据进行粘包处理,既将频繁发送的小字节数据打包成 一个整包进行发送,降低内存的消耗。
2.分包:当发送的字节数据包比较大时,Socket内部会将发送的字节数据进行分包处理,降低内存和性能的消耗。
解决方案:将发送的字节数据的长度与字节数据拼接后发送,每次读取字节数据时先读取其长度,然后再对接收到的字节数组进行拆分和拼接处理。
具体实现代码如下:(环境:Visual Studio 2017 C#.NET控制台程序)
服务器端代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
using TCPClient;
namespace TCPServer
{
class Program
{
static Message msg = new Message();
static void Main(string[] args)
{
StartServerAsync();
}
///
/// 异步
///
static void StartServerAsync()
{
//开启服务器
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress iPAddress = IPAddress.Parse("192.168.1.108");
IPEndPoint iPEndPoint = new IPEndPoint(iPAddress, 50000);
serverSocket.Bind(iPEndPoint); //绑定ip和端口号
serverSocket.Listen(0); //服务器端的监听(0代表不限个数)
//Socket client = serverSocket.Accept(); //接收客户端链接,当没有接收到任何客户端链接时中断程序
//异步接收客户端链接并发送消息
serverSocket.BeginAccept(AsyncAcceptCallBack, serverSocket);
Console.ReadKey();
}
static void AsyncAcceptCallBack(IAsyncResult ar)
{
Socket serverSocket = ar.AsyncState as Socket;
Socket client = serverSocket.EndAccept(ar);
//向客户端发送消息
string msgStr = "努力!!奋斗!!!";
byte[] data = System.Text.Encoding.UTF8.GetBytes(msgStr);
client.Send(data);
//异步接收消息(回调函数)
client.BeginReceive(msg.Data, msg.StartIndex, msg.Data.Length, SocketFlags.None, AsyncReceiveCallBack, client);
//client.BeginReceive(msg.Data, 0, 1024, SocketFlags.None, AsyncReceiveCallBack, client);
//继续等待下一个客户端连接(回调函数)
serverSocket.BeginAccept(AsyncAcceptCallBack, serverSocket);
}
static void AsyncReceiveCallBack(IAsyncResult ar)
{
Socket client = null;
try
{
client = ar.AsyncState as Socket;
int count = client.EndReceive(ar); //异步时使用EndReceive接收
if (count == 0) //服务器不会接收空消息
{
client.Close();
return;
}
msg.ReadMessage(count); //读取出第一条字节数据并删除
client.BeginReceive(msg.Data, msg.StartIndex, msg.RemainSize, SocketFlags.None, AsyncReceiveCallBack, client);
}
catch (Exception e)
{
Console.WriteLine(e);
if(client != null) client.Close();
}
}
}
}
Message类代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TCPClient
{
class Message
{
private byte[] m_data = new byte[10240];
private int m_startIndex = 0;
private int m_count = 0; //为一条发送的字节数据的长度
public int Count { get { return m_count; } }
public byte[] Data { get { return m_data; } }
public int StartIndex { get { return m_startIndex; } }
public int RemainSize { get { return m_data.Length - m_count; } }
///
/// 将字节数据与字节长度拼接
///
///
///
public static byte[] GetBytes(string _msg)
{
byte[] msg = System.Text.Encoding.UTF8.GetBytes(_msg);
int length = msg.Length; //字节长度
byte[] newMsg = BitConverter.GetBytes(length).Concat(msg).ToArray(); //将字节长度与msg拼接
return newMsg;
}
///
/// 解析字节数据
///
///
public void ReadMessage(int _count)
{
m_startIndex = 4;
m_count += _count;
UpdataMessage();
}
///
/// 读取完成字节数据后删除第一条字节数据
///
private void UpdataMessage()
{
while (true)
{
int count = BitConverter.ToInt32(m_data, 0);
Console.WriteLine(System.Text.Encoding.UTF8.GetString(m_data, m_startIndex, count));
m_count -= count + 4;
Array.Copy(m_data, count + 4, m_data, 0, m_count);
if (m_count < count + 4) break;
}
m_startIndex = m_count;
}
}
}
客户端代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
namespace TCPClient
{
class Program
{
static void Main(string[] args)
{
TCPClientAsync();
Console.ReadKey();
}
static void TCPClientAsync()
{
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
client.Connect(new IPEndPoint(IPAddress.Parse("192.168.1.108"), 50000));
}
catch (Exception e)
{
Console.WriteLine(e);
return;
}
//从服务器接收消息
byte[] buffer = new byte[1024];
int size = client.Receive(buffer);
string msgReceive = System.Text.Encoding.UTF8.GetString(buffer, 0, size);
Console.Write(msgReceive);
//向服务器发送消息
//while (true)
//{
// string msg = Console.ReadLine();
// if (msg == "e")
// {
// client.Close();
// return;
// }
// Console.Write(msg);
// byte[] data = GetBytes(msg);
// client.Send(data);
//}
for (int i = 0; i < 1000; i++)
{
byte[] data = GetBytes(i.ToString() + "身开始登记卡");
client.Send(data);
}
Console.ReadKey();
client.Close();
}
///
/// 将字节数据与字节长度拼接
///
///
///
public static byte[] GetBytes(string _msg)
{
byte[] msg = System.Text.Encoding.UTF8.GetBytes(_msg);
int length = msg.Length; //字节长度
byte[] newMsg = BitConverter.GetBytes(length).Concat(msg).ToArray(); //将字节长度与msg拼接
return newMsg;
}
}
}