您的位置:首页 > 理论基础 > 计算机网络

.Net网络通讯编程[利用Socket实现字串、文件、序列化对象传输]--类设计2

2012-03-28 09:15 911 查看
本案例使用.Net Socket的Tcp、Udp实现字串、文件、各种序列化对象的网络传输,同时封装了Tcp的粘包、半包处理细节,定义了网络封包格式,在发送端和接收端无需考虑内部传输细节。以下是类设计:

网络封包服务类设计抽象类提供Tcp、Udp共有的行为和特征,Tcp、Udp发包和收包的细节不同,所以发包方法和收包方法定义为抽象方法去子类实现

提供网络封包传输服务的核心类代码:

示范代码using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.IO;

namespace TcpLabCommon
{
public delegate void TcpAsynHandler1(NetPacket packet);
public delegate void TcpAsynHandler2(NetPacketHead packetHead);
/// <summary>
/// 网络封包Tcp异步服务类
/// </summary>
public class NetPacketTcpAsynService : NetPacketService
{
/// <summary>
/// 发包前事件
/// </summary>
public event TcpAsynHandler1 OnBeforeSendPacket;

/// <summary>
/// 发包后事件
/// </summary>
public event TcpAsynHandler2 OnAfterSendPacket;

/// <summary>
/// 收到网络封包后的事件
/// </summary>
public event TcpAsynHandler1 OnReceivedPacket;

public NetPacketTcpAsynService(NetworkStream netStream) : base(netStream) { }

/// <summary>
/// 该方法的返回值总是为空.提取一个完整网络包然后返回,返回的封包需要通过注册OnReceivedPacket事件的函数获取
///     如果收到的包是STRING类型,NetPacket.Data是发送的字符串
///     如果收到的包是BINARY类型,NetPacket.Data是发送的文件名长度+文件名+文件内容,请调用ParseFile函数进行解析
///     如果收到的包是COMPLEX类型,NetPacket.Data是发序列化的对象,可以直接转型使用
/// </summary>
/// <returns></returns>
public override NetPacket ReceivePacket()
{

//判断是否满足一个完整封包大小
if (IsFullNetPacket())//如果有完整封包就返回
{
NetPacket packet = PickNetPacket();
if (OnReceivedPacket != null) //判断事件是否注册,如果注册调用回调函数传递收到的封包
{
OnReceivedPacket(packet);
}
//return null;//提取到一个封包后应该及时返回
}

//【缓冲区不满足一个完整封包大小则继续从网络流读取数据,异步读取】
_netStream.BeginRead(_tempBuffer, 0, BUFFER_SIZE, new AsyncCallback(AsyncCallbackReadFromNetStream),_netStream);

return null;
}

/// <summary>
/// 从网络流异步读取数据回调函数
/// </summary>
/// <param name="result"></param>
private void AsyncCallbackReadFromNetStream(IAsyncResult result)
{
NetworkStream netStream = (NetworkStream)result.AsyncState;
int readLen = netStream.EndRead(result);
//判断读取的字节数+缓冲区已有字节数是否超过缓冲区总大小
if (readLen + _netDataOffset > _netDataBuffer.Length)
{
if (IsFullNetPacketHead())//如果缓冲区数据满足一个包头数据大小,则可以计算出本次接收的包需要的缓冲区大小,从而实现一次调整大小
{
Array.Resize<Byte>(ref _netDataBuffer, FullNetPacketSize);
}
else //不满足一个完整的网络封包的大小
{
Array.Resize<Byte>(ref _netDataBuffer, _netDataBuffer.Length + BUFFER_SIZE * 2);
}
}
//将新读取的数据拷贝到缓冲区
Array.Copy(_tempBuffer, 0, _netDataBuffer, _netDataOffset, readLen);
//修改"网络数据实际长度"
_netDataOffset += readLen;

ReceivePacket();
}

/// <summary>
/// 发包[发送Tcp包/发送Udp数据报]
/// </summary>
/// <param name="packet"></param>
protected override void SendPacket(NetPacket packet)
{
if (packet == null || packet.Data == null || packet.PacketHead == null)
return;

if (OnBeforeSendPacket != null)
OnBeforeSendPacket(packet);

MemoryStream mStream = new MemoryStream();

#region【计算包体长度】
if (packet.PacketHead.Len == 0)
{
switch (packet.PacketHead.PType)
{
case PacketType.STRING:
packet.PacketHead.Len = Encoding.Default.GetBytes(Convert.ToString(packet.Data)).Length;
break;
case PacketType.BINARY:
packet.PacketHead.Len = ((Byte[])packet.Data).Length;
break;
case PacketType.COMPLEX:
packet.PacketHead.Len = GetCanSerializableObjectSize(packet.Data);
break;
default:
break;
}
}

#endregion

#region【写入包头】
mStream.Write(BitConverter.GetBytes(packet.PacketHead.Version), 0, Marshal.SizeOf(packet.PacketHead.Version));
mStream.Write(BitConverter.GetBytes((Int32)packet.PacketHead.PType), 0, sizeof(Int32));
mStream.Write(BitConverter.GetBytes(packet.PacketHead.Len), 0, Marshal.SizeOf(packet.PacketHead.Len));
#endregion

#region【写入包体】
byte[] buffer = null;
switch (packet.PacketHead.PType)
{
case PacketType.STRING:
buffer = Encoding.Default.GetBytes(Convert.ToString(packet.Data));
break;
case PacketType.BINARY:
buffer = (byte[])packet.Data;
break;
case PacketType.COMPLEX:
MemoryStream m = new MemoryStream();
SerializeHelper<BinarySerializeHelper>().Serialize(m, packet.Data);
m.Position = 0;
buffer = new byte[m.Length];
m.Read(buffer, 0, (Int32)m.Length);
break;
}
if (buffer != null)
mStream.Write(buffer, 0, buffer.Length);

#endregion

#region【将内存流一次写入网络流,异步写入】
mStream.Seek(0, SeekOrigin.Begin);
_netStream.BeginWrite(mStream.GetBuffer(), 0, (Int32)mStream.Length, new AsyncCallback(AsyncCallbackWriteToNetStream), new WriteNetStreamASyncCallbackParam{ netStream = _netStream, packetHead = packet.PacketHead });
#endregion
}

/// <summary>
/// 写入网络流异步回调函数
/// </summary>
/// <param name="result"></param>
private void AsyncCallbackWriteToNetStream(IAsyncResult result)
{
WriteNetStreamASyncCallbackParam p = (WriteNetStreamASyncCallbackParam)result.AsyncState;
p.netStream.EndWrite(result);
if (OnAfterSendPacket != null)//事件处理,如果注册了该事件,则执行回调函数
OnAfterSendPacket(p.packetHead);
}
}

/// <summary>
/// 写入网络流异步回调参数
/// </summary>
sealed class  WriteNetStreamASyncCallbackParam
{
/// <summary>
/// 网络流
/// </summary>
internal NetworkStream netStream;
/// <summary>
/// 包头
/// </summary>
internal NetPacketHead packetHead;
}
}


时间关系我没有编写Udp异步传输子类
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐