这几天闲着没事在MSDN上面看了一下Socket,也借鉴了很多别人写的文章和例子,然后自己写了一个Demo,简单的处理了一下粘包。
粘包的处理方式就是客户端每一次发送数据之前都在数据的前面加了4个字节,存放本次数据的长度。服务端用一个集合存放接收到的数据,然后用递归的方式处理集合里面的数据。
这个demo很简单,无奈本人能力有限,所以希望路过的大神能够指点一二,菜鸟不胜感激。
以下代码经过测试是可以运行的。
服务端代码:
internal class SocketState
{
//文件存放路径
private String FilePath = @"E:\";
public Socket workSocket = null;
public const int BufferSize = 1024;
//每次接收到的数据
public byte[] Data = new byte[BufferSize];
//数据缓冲区,所有接收的数据都放这里面统一处理
public List<byte> Buffer = new List<byte>();
private FileStream file;
public FileStream GetFileStream()
{
if (file == null)
file = new FileStream(
Path.Combine(FilePath, FileName),
FileMode.Create,
FileAccess.Write);
return file;
}
//文件名称
public String FileName { get; set; }
//文件大小
public long Length { get; set; }
public void Clear()
{
FileName = string.Empty;
Length = 0;
file = null;
}
}
class Program
{
public static ManualResetEvent allDone = new ManualResetEvent(false);
static void Main(string[] args)
{
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(new IPEndPoint(IPAddress.Any, 38888));
listener.Listen(10);
Console.WriteLine("开始监听...");
while (true)
{
allDone.Reset();
listener.BeginAccept(AsyncAcceptCallback, listener);
allDone.WaitOne();
}
}
catch (Exception e)
{
throw e;
}
Console.ReadLine();
}
private static void AsyncAcceptCallback(IAsyncResult ar)
{
allDone.Set();
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
SocketState state = new SocketState();
state.workSocket = handler;
try
{
handler.BeginReceive(state.Data, 0, SocketState.BufferSize, 0, AsyncReceiveCallback, state);
}
catch (Exception)
{
}
}
private static void AsyncReceiveCallback(IAsyncResult ar)
{
SocketState state = (SocketState)ar.AsyncState;
Socket handler = state.workSocket;
int bytesLength = handler.EndReceive(ar);
if (bytesLength > 0)
{
byte[] resultData = new byte[bytesLength];
//获取真正收到的字节
Array.Copy(state.Data, 0, resultData, 0, bytesLength);
ProcessBuffer(resultData, ref state);
try
{
handler.BeginReceive(state.Data, 0, SocketState.BufferSize, 0, AsyncReceiveCallback, state);
}
catch (Exception)
{
}
}
}
private static void ProcessBuffer(byte[] data, ref SocketState state)
{
if (data != null && data.Length > 0)
{
//将接收到的字节追加到集合里面,等待统一处理
state.Buffer.AddRange(data);
}
if (state.Buffer.Count < 4) return;
//获取前4个字节,获取本次的字节长度
string tmep = Encoding.UTF8.GetString(state.Buffer.GetRange(0, 4).ToArray());
int length = int.Parse(tmep);
//如果集合里面的字节长度不够,说明本次send的数据还没获取完,所以跳过不处理继续接收
if (state.Buffer.Count >= (length + 4))
{
//定义用来存放本次数据的字节数组
byte[] content = new byte[length];
//移除前4个字节(也就是头)
state.Buffer.RemoveRange(0, 4);
//从集合中将本次数据复制到content中
Array.Copy(state.Buffer.ToArray(), 0, content, 0, length);
//对本次数据进行处理
ProcessData(content, ref state);
//从集合中移除本次的数据
state.Buffer.RemoveRange(0, length);
//递归继续处理剩下的集合
ProcessBuffer(null, ref state);
}
}
//因为客户端的发送顺序是:文件名、字节大小、文件、[END]
//所以这里也就按照这个逻辑对数据进行处理
private static void ProcessData(byte[] data, ref SocketState state)
{
if (string.IsNullOrEmpty(state.FileName))
{
state.FileName = Encoding.UTF8.GetString(data);
}
else if (state.Length == 0)
{
state.Length = long.Parse(Encoding.UTF8.GetString(data));
}
else if (Compera(data))
{
Console.WriteLine(string.Format("文件【{0}】接收完成", state.FileName));
state.Clear();
}
else WriteFile(data, state.GetFileStream());
}
private static void WriteFile(byte[] data, FileStream file)
{
file.Write(data, 0, data.Length);
file.Flush();
}
private static bool Compera(byte[] data)
{
return (data.Length <= 5 && Encoding.UTF8.GetString(data) == "[END]");
}
}
客户端代码:
class Program
{
static void Main(string[] args)
{
string[] paths = { @"E:\Tools\GhostWin7x64v9.9.iso", @"E:\Tools\Ubuntu12.10.iso" };
Socket sc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
byte[] data =null;
try
{
sc.Connect("127.0.0.1", 38888);
foreach (var item in paths)
{
using (FileStream file = new FileStream(item, FileMode.Open, FileAccess.Read, FileShare.None))
{
Console.WriteLine("开始发送文件【{0}】,请等待……", Path.GetFileName(item));
data = Encoding.UTF8.GetBytes(Path.GetFileName(item));
data = DataManager(data, data.Length);
sc.Send(data);
data = Encoding.UTF8.GetBytes(file.Length.ToString());
data = DataManager(data, data.Length);
sc.Send(data);
int count = 0;
int sent = 0;
byte[] fileData = new byte[1024];
while ((count = file.Read(fileData, 0, 1024)) != 0)
{
sent += count;
byte[] temp = DataManager(fileData, count);
sc.Send(temp);
}
data = Encoding.UTF8.GetBytes("[END]");
data = DataManager(data, data.Length);
sc.Send(data);
Console.WriteLine("文件【{0}】发送完成", Path.GetFileName(item));
}
}
sc.Shutdown(SocketShutdown.Both);
sc.Close();
}
catch (Exception e)
{
throw e;
}
Console.ReadLine();
}
//给数据加上4个字节的头信息,头信息里面就是存的本次数据有多少字节
private static byte[] DataManager(byte[] data,int length)
{
byte[] header = new byte[4];
header = Encoding.UTF8.GetBytes(length.ToString());
byte[] result = new byte[length + 4];
Array.Copy(header, 0, result, 0, header.Length);
Array.Copy(data, 0, result, 4, length);
return result;
}
}