c#TCP服务器
TCP本身有头尾,如下图所示
https://www.cnblogs.com/wangjun8868/p/7160661.html
对于TCP粘包问题,有以下几种解决办法:
1.定长分包:即每一个数据包都规定了一个固定的长度。在服务端接收数据时,每次读取固定长度的数据,直到读取到所有数据。这种方法简单粗暴,代码实现也较为简单,但是存在一些弊端:比如当接收数据的长度不够固定长度时,需要等待缓冲区填满,此时会出现阻塞等待的情况。即使缓冲区已经填满,有时也会出现最后一个数据包不满固定长度的情况,需要特殊处理。
2.特殊分隔符分包:即定义特殊字符或字符集合用作分隔符,当服务端接收到这个分隔符时,就可以确定一个数据包已经完成。这种方法也有一些弊端:比如特殊字符出现在数据体中,就会出现分包错误。
3.消息头+消息体分包:可以在消息头中定义消息体长度,服务端在接收到消息头后,就可以知道消息体的长度,在读取完整的消息体之后,再进行处理。这种方法相对更加安全可靠。
以下是使用第三种方法的示例代码:
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
public class Server
{
private TcpListener _server;
private List<TcpClient> _clients = new List<TcpClient>();
private bool _isRunning = false;
public Server(int port)
{
_server = new TcpListener(IPAddress.Any, port);
}
public void Start()
{
_server.Start();
_isRunning = true;
Console.WriteLine("Server started");
while (_isRunning)
{
if (_server.Pending())
{
TcpClient client = _server.AcceptTcpClient();
Console.WriteLine("Client connected: {0}", client.Client.RemoteEndPoint);
Thread t = new Thread(() => HandleClient(client));
t.Start();
}
Thread.Sleep(10);
}
}
public void Stop()
{
_isRunning = false;
_server.Stop();
foreach (TcpClient client in _clients)
client.Close();
Console.WriteLine("Server stopped");
}
private void HandleClient(TcpClient client)
{
_clients.Add(client);
BinaryReader reader = new BinaryReader(client.GetStream());
while (client.Connected)
{
try
{
// 读取消息头
int length = reader.ReadInt32();
// 读取消息体
byte[] buffer = reader.ReadBytes(length);
// 处理消息
ProcessMessage(buffer);
}
catch (EndOfStreamException)
{
break;
}
catch (IOException e)
{
Console.WriteLine("IOException: {0}", e.Message);
break;
}
Thread.Sleep(10);
}
_clients.Remove(client);
client.Close();
Console.WriteLine("Client disconnected: {0}", client.Client.RemoteEndPoint);
}
private void ProcessMessage(byte[] message)
{
// 处理消息
}
}