自己仿照某商業組件開發的通訊模塊,簡單的調用便可實現傳文件,即時通訊等內容.
如圖,只需要綁定ip便可以通訊
組件下載地址:點擊打開鏈接
調用的方法:
class Program
{
public static DataDispatcher dd;
static void Main(string[] args)
{
dd = new DataDispatcher("192.168.167.105:8003");// 這裏填寫自己的ip
dd.OnDataArrived += dd_OnDataArrived;
}
static void dd_OnDataArrived(byte[] in_listVariablePool)
{
Console.WriteLine("收到數據!");
VariablePool vp = new VariablePool();
vp.Init(in_listVariablePool);
string ip = (string)vp.Get("IP",0);// 獲取發送方的IP
// 以上是接受數據
// 下方是發送數據
vp.Init();
vp.Put("IP", ip);
vp.Put("string", DateTime.Now.ToString());
dd.Send(vp.GetVariablePool(),ip);
}
}
完整源代碼:
0.目錄:
1.主要通訊層DataDispatcher:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using SocketByUDPClassLibrary;
namespace UDPClassLibrary
{
// 定義一個接受到數據時響應的委託
public delegate void OnDataArrivedDelegate(byte[] in_listVariablePool);
public class DataDispatcher
{
// 定義完成事件
public event OnDataArrivedDelegate OnDataArrived;
// 接收池
private Dictionary<int, DataPools> m_listInDataPools;
// 發送池
private Dictionary<int, DataPools> m_listOutDataPools;
// 本機IP地址
private IPEndPoint m_ipSelfIP;
// 接收時的服務變量
private Socket m_SocketService;
private Thread m_threadStartService;
// 線程鎖
private Mutex m_mutexPick;
// 併發緩解
private int m_iPush = 1;
// 等待時間(毫秒)
private int m_iWait = 500;
/// <summary>
/// 構造方法
/// </summary>
public DataDispatcher(string in_strIPInfo)
{
// 獲得IP
m_ipSelfIP = StrToEndPoint(in_strIPInfo);
// 數據池初始化
m_listInDataPools = new Dictionary<int, DataPools>();
m_listOutDataPools = new Dictionary<int, DataPools>();
// 啓動服務
m_threadStartService = new Thread(StartService);
m_threadStartService.Start();
m_mutexPick = new Mutex();
Thread threadTimer=new Thread(MyTime);
threadTimer.Start();
}
/// <summary>
/// 啓動服務
/// </summary>
public void StartService()
{
// 使用Socket服務
m_SocketService = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
// 綁定網絡地址
m_SocketService.Bind(m_ipSelfIP);
// 客戶機IP
IPEndPoint senderPoint = new IPEndPoint(IPAddress.Any, 0);
EndPoint senderEndPoint = (EndPoint)(senderPoint);
// 數據大小
int iDataSize;
// 字節數據
byte[] bData = new byte[2048];
List<byte[]> listIncomeDatas = new List<byte[]>();
Thread threadAllotPack;
object objCustomParm;
// 循環監聽是否取到包
while (true)
{
bData = new byte[2048];
//接收信息
iDataSize = m_SocketService.ReceiveFrom(bData, ref senderEndPoint);
// 如果是數據包 接收,如果是重發請求包 重發
if (iDataSize == 2048)
{
// 自定義參數
objCustomParm = new CustomParm(bData, senderEndPoint);
// 添加線程
threadAllotPack = new Thread(AllotPack);
// 分配收到的包
threadAllotPack.Start(objCustomParm);
}
else
{
// 根據信息重發
SendByInfo((ResendInfo)SetByteToValue(bData));
Console.WriteLine("收到請求包");
}
}
}
/// <summary>
/// 發送數據
/// </summary>
/// <param name="in_listToSendData"></param>
/// <param name="in_strDesIP"></param>
public void Send(byte[] in_listToSendData, string in_strDesIP)
{
List<byte> listByte = new List<byte>();
listByte.AddRange(in_listToSendData);
// 設置目標IP,設置TCP端口號
IPEndPoint ipEndPoint = StrToEndPoint(in_strDesIP);
// 調用包組件
ByePacketUtil stPacketUtil = new ByePacketUtil();
// 聲明包組變量
List<byte[]> listToSendDatas = stPacketUtil.SplitPacket(listByte);
// 添加到發送池
m_listOutDataPools.Add(stPacketUtil.GetJointHeadID(listToSendDatas[0]), new DataPools(0, 0, null, 30000, listToSendDatas));
foreach (var blistPackage in listToSendDatas)
{
//循環發送每個包
byte[] bSends = new byte[2048];
bSends = blistPackage.ToArray();
// 發送
m_SocketService.SendTo(bSends, bSends.Length, SocketFlags.None, ipEndPoint);
// 緩解併發壓力
Thread.Sleep(m_iPush);
}
}
private void SendByInfo(ResendInfo in_resendInfo)
{
int iKey = in_resendInfo.Key;
int iIndex = in_resendInfo.Index;
// 調用包組件
ByePacketUtil packetUtil = new ByePacketUtil();
if (m_listOutDataPools.ContainsKey(iKey))
{
foreach (var bItem in m_listOutDataPools[iKey].PacketParking)
{
// 得到包的Index
int iPacketIndex = packetUtil.GetJointHeadIndex(bItem);
if (iPacketIndex==iIndex)
{
// 發送
m_SocketService.SendTo(bItem, bItem.Length, SocketFlags.None, in_resendInfo.MyEndPoint);
}
}
}
}
/// <summary>
/// 分配收到的包
/// </summary>
/// <param name="in_iData"></param>
private void AllotPack(object in_objCustomParm)
{
byte[] listDataIn = ((CustomParm)in_objCustomParm).ParmBytes;
EndPoint endPointIn = ((CustomParm)in_objCustomParm).ParmEndPoint;
// 默認等待時間(毫秒)
int iLeftDef = 30000;
// 調用包組件
ByePacketUtil packetUtil = new ByePacketUtil();
// 得到包的ID
int iPacketID = packetUtil.GetJointHeadID(listDataIn);
// 得到包的Index
int iPacketIndex = packetUtil.GetJointHeadIndex(listDataIn);
// 得到包的份數
int iPacketCount = packetUtil.GetJointHeadCount(listDataIn);
m_mutexPick.WaitOne();
Console.WriteLine("收到" + iPacketIndex + "/" + iPacketCount);
// 操作接收池
if (m_listInDataPools.ContainsKey(iPacketID))
{
// 存在就存一條
m_listInDataPools[iPacketID].PacketParking.Add(listDataIn);
m_listInDataPools[iPacketID].ExistPack++;
m_listInDataPools[iPacketID].LeftTime = iLeftDef;
}
else
{
// 不存在就新建一條
List<byte[]> listTempDatas = new List<byte[]>();
listTempDatas.Add(listDataIn);
m_listInDataPools.Add(iPacketID, new DataPools(1, iPacketCount, endPointIn, iLeftDef, listTempDatas));
}
// 檢查接收此包後是否有完整大包
if (m_listInDataPools[iPacketID].PacketParking.Count() == iPacketCount)
{
// 臨時變量池
List<byte> listTempPool;
// 若大包完整執行封包
listTempPool = packetUtil.JointPacket(m_listInDataPools[iPacketID].PacketParking);
if (OnDataArrived != null)
{
// 事件拋出數據
OnDataArrived(listTempPool.ToArray());
}
Console.WriteLine("開始組包");
// 刪除相關數據
RemoveByKey(iPacketID);
}
m_mutexPick.ReleaseMutex();
}
/// <summary>
/// 每1秒執行一次丟包檢查
/// </summary>
private void MyTime()
{
do
{
Thread.Sleep(m_iWait);
CheckTimesIn();
} while (true);
}
/// <summary>
/// 檢查接收池包否完整,若超時則釋放資源
/// </summary>
private void CheckTimesIn()
{
foreach (int iItemKey in m_listInDataPools.Keys)
{
DataPools stValue = m_listInDataPools[iItemKey];
stValue.LeftTime -= m_iWait;
if (stValue.LeftTime <= 0)
{
RemoveByKey(iItemKey);
}
else if (stValue.LeftTime <= 27000)
{
if (stValue.ExistPack < stValue.TotalPack)
{
List<ResendInfo> listResendInfos = FindLostPack(iItemKey, m_listInDataPools[iItemKey].EndPoint);
// 請求重發
Resend(listResendInfos);
}
}
}
}
/// <summary>
/// 檢查發送池包是否超時,若超時則釋放資源
/// </summary>
private void CheckTimesOut()
{
foreach (int iItemKey in m_listOutDataPools.Keys)
{
DataPools stValue = m_listOutDataPools[iItemKey];
stValue.LeftTime -= m_iWait;
if (stValue.LeftTime <= 0)
{
m_listOutDataPools.Remove(iItemKey);
}
}
}
private void Resend(List<ResendInfo> in_listResendInfos)
{
foreach (var stResendInfo in in_listResendInfos)
{
// 緩解併發壓力
Thread.Sleep(m_iPush);
byte[] bSends = SetValueToByte(stResendInfo);
// 發送
m_SocketService.SendTo(bSends, bSends.Length, SocketFlags.None, stResendInfo.EndPoint);
Console.WriteLine("重發:" + stResendInfo.Index+"---"+stResendInfo.Key);
}
}
/// <summary>
/// 轉換出值字節(序列化)
/// </summary>
/// <param name="in_strFieldName">值名字</param>
/// <returns></returns>
public byte[] SetValueToByte(Object in_objValue)
{
var memoryStream = new MemoryStream();
var formatter = new BinaryFormatter();
formatter.Serialize(memoryStream, in_objValue);
byte[] buffer = memoryStream.ToArray();
memoryStream.Close();
return buffer;
}
/// <summary>
/// 反序列化
/// </summary>
/// <param name="in_strFieldName">值名字</param>
/// <returns></returns>
public Object SetByteToValue(byte[] in_listValue)
{
var memoryStream = new MemoryStream(in_listValue);
var formatter = new BinaryFormatter();
Object obj = formatter.Deserialize(memoryStream);
memoryStream.Close();
return obj;
}
/// <summary>
/// 生成重發請求列表
/// </summary>
/// <param name="in_iPackKey"></param>
/// <returns></returns>
private List<ResendInfo> FindLostPack(int in_iPackKey,EndPoint in_EndPoint)
{
// 返回變量
List<ResendInfo> listRes = new List<ResendInfo>();
// 得到總包數
int iCount = ((DataPools)m_listInDataPools[in_iPackKey]).TotalPack;
// 總包數布爾值
bool[] listAllTable = new bool[iCount];
List<byte[]> listPools = m_listInDataPools[in_iPackKey].PacketParking;
// 調用包組件
ByePacketUtil packetUtil = new ByePacketUtil();
foreach (byte[] listItem in listPools)
{
int i = packetUtil.GetJointHeadIndex(listItem);
listAllTable[i] = true;
}
for (int i = 0; i < iCount; i++)
{
if (listAllTable[i] == false)
{
listRes.Add(new ResendInfo(in_iPackKey, in_EndPoint, i, m_ipSelfIP));
}
}
return listRes;
}
/// <summary>
/// 依據ID刪除檢查池的條目
/// </summary>
/// <param name="in_itemKey"></param>
private void RemoveByKey(int in_itemKey)
{
m_listInDataPools.Remove(in_itemKey);
}
/// <summary>
/// 字符串轉ip+端口
/// </summary>
/// <returns></returns>
private IPEndPoint StrToEndPoint(string in_strIPandPort)
{
string strIP = in_strIPandPort.Split(':')[0];
string strPort = in_strIPandPort.Split(':')[1];
IPAddress stIP = IPAddress.Parse(strIP);
IPEndPoint resEndPoint = new IPEndPoint(stIP, int.Parse(strPort));
return resEndPoint;
}
}
}
2變量池(存放接收的原始對象):VariablePool
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
namespace SocketByUDPClassLibrary
{
public class VariablePool
{
private List<byte> m_listTransports;
private int m_iPost;
private static int PostInitValue = 2;
/// <summary>
/// 返回變量池
/// </summary>
/// <returns></returns>
public byte[] GetVariablePool()
{
SetCount();
return this.m_listTransports.ToArray();
}
/// <summary>
/// Put時初始化
/// </summary>
public void Init()
{
m_listTransports = new List<byte>();
m_listTransports.AddRange(BitConverter.GetBytes(new ushort()));
}
/// <summary>
/// Get時初始化
/// </summary>
/// <param name="in_listData">接收到的List</param>
public void Init(byte[] in_listData)
{
m_listTransports = new List<byte>();
m_listTransports.AddRange(in_listData);
}
/// <summary>
/// 取得下一個值
/// </summary>
/// <param name="in_iPost"></param>
/// <returns></returns>
public byte[] GetNextData()
{
byte bLength = m_listTransports[m_iPost];
byte[] listResData = new byte[bLength];
int iNextOne = 1;
Array.Copy(m_listTransports.ToArray(), m_iPost + iNextOne, listResData, 0, bLength);
m_iPost += bLength + iNextOne;
return listResData;
}
/// <summary>
/// 獲得最大重複值數量
/// </summary>
/// <returns></returns>
public int GetCount()
{
byte[] bUshot = new byte[2];
bUshot[0] = m_listTransports[0];
bUshot[1] = m_listTransports[1];
return BitConverter.ToInt16(bUshot, 0);
}
/// <summary>
/// 修改最大值
/// </summary>
public void SetCount()
{
Dictionary<string, int> listCountMap = new Dictionary<string, int>();
string strName;
m_iPost = PostInitValue;
do
{
strName = Encoding.Default.GetString(GetNextData());
if (listCountMap.ContainsKey(strName))
{
// 如果包含就增加1
listCountMap[strName]++;
}
else
{
// 不包含就新鍵一條
listCountMap.Add(strName, 1);
}
// 取值過程,只需要下標變動,不需要接收返回值
GetNextData();
// 當下標越界時結束循環
} while (m_iPost < m_listTransports.Count);
// 找出最大值並修改
ushort usMax = (ushort)listCountMap.Values.Max();
byte[] bUshot = BitConverter.GetBytes(usMax);
m_listTransports[0] = bUshot[0];
m_listTransports[1] = bUshot[1];
}
/// <summary>
/// 從變量池取得對象
/// </summary>
/// <param name="in_strFieldName">字段名</param>
/// <param name="in_iIndex">下標</param>
/// <returns></returns>
public object Get(string in_strFieldName, int in_iIndex)
{
int iCount = -1;
List<byte> listResData = new List<byte>();
string strName;
byte[] listValue;
m_iPost = PostInitValue;
do
{
strName = Encoding.Default.GetString(GetNextData());
if (strName == in_strFieldName)
{
// 匹配字段名
iCount++;
}
listValue = GetNextData();
if (iCount == in_iIndex)
{
// 匹配下標
return SetByteToValue(listValue);
}
// 當下標越界時結束循環
} while (m_iPost < m_listTransports.Count);
return null;
}
/// <summary>
/// 轉換出字段名字節
/// </summary>
/// <param name="in_strFieldName">字段名字</param>
/// <returns></returns>
public byte[] SetNameToByte(string in_strFieldName)
{
return System.Text.Encoding.ASCII.GetBytes(in_strFieldName);
}
/// <summary>
/// 爲變量池添加對象
/// </summary>
/// <param name="in_strFieldName"></param>
/// <param name="in_objValue"></param>
public void Put(string in_strFieldName, Object in_objValue)
{
// 分別轉換出字段長度,字段,值長度,值
byte[] listFieldName = SetNameToByte(in_strFieldName);
byte bFieldNameLength = (byte)listFieldName.Length;
byte[] listValue = SetValueToByte(in_objValue);
byte bValueLength = (byte)listValue.Length;
m_listTransports.Add(bFieldNameLength);
m_listTransports.AddRange(listFieldName);
m_listTransports.Add(bValueLength);
m_listTransports.AddRange(listValue);
}
/// <summary>
/// 轉換出值字節(序列化)
/// </summary>
/// <param name="in_strFieldName">值名字</param>
/// <returns></returns>
public byte[] SetValueToByte(Object in_objValue)
{
var memoryStream = new MemoryStream();
var formatter = new BinaryFormatter();
formatter.Serialize(memoryStream, in_objValue);
byte[] buffer = memoryStream.ToArray();
memoryStream.Close();
return buffer;
}
/// <summary>
/// 反序列化
/// </summary>
/// <param name="in_strFieldName">值名字</param>
/// <returns></returns>
public Object SetByteToValue(byte[] in_listValue)
{
var memoryStream = new MemoryStream(in_listValue);
var formatter = new BinaryFormatter();
Object obj = formatter.Deserialize(memoryStream);
memoryStream.Close();
return obj;
}
}
}
3.拆包解包工具類ByePacketUtil
using System;
using System.Collections.Generic;
using System.Linq;
namespace SocketByUDPClassLibrary
{
public class ByePacketUtil
{
private int PacketMaxSize = 2048;
private int UserMaxSize = 2040;
private int HeadSize = 8;
private int HeadIndexSize = 2;
private int HeadCountSize = 2;
private int HeadLengthSize = 2;
private int HeadIDSize = 2;
/// <summary>
/// 拆包
/// </summary>
/// <param name="in_listPools"></param>
/// <returns></returns>
public List<byte[]> SplitPacket(List<byte> in_listPools)
{
List<byte[]> listResBytes = new List<byte[]>();
// 得到總長度
int iPoolLenght = in_listPools.Count();
Int16 HeadIndex = -1;
Int16 HeadCount = GetCount(iPoolLenght);
Int16 HeadLength = 0;
Int16 HeadID = CreateID();
//剩下的長度
int iLeftLenght = in_listPools.Count();
bool bIsOver = false;
do
{
// 序列自增
HeadIndex++;
// 判斷剩下的長度是否還要再分一個包
if (iLeftLenght > UserMaxSize)
{
// 多個包就是HeadLength=UserMaxSize=2042
HeadLength = (Int16)(UserMaxSize);
}
else
{
// 否則HeadLength等於剩下的大小
HeadLength = (Int16)iLeftLenght;
// 標記循環結束
bIsOver = true;
}
// 建立一個包
List<byte> listResData = new List<byte>();
// 添加包頭
listResData.AddRange(CreateHead(HeadIndex, HeadCount, HeadLength, HeadID));
byte[] listPatyData = new byte[HeadLength];
// 依照HeadLength長度剪切in_listPools
Array.Copy(in_listPools.ToArray(), iPoolLenght - iLeftLenght, listPatyData, 0, (long)HeadLength);
//添加剩下內容
listResData.AddRange(listPatyData);
while (listResData.Count<2048)
{
listResData.AddRange(new byte[2048-listResData.Count]);
}
listResBytes.Add(listResData.ToArray());
// 剩下的長度減掉單前包的長度
iLeftLenght -= HeadLength;
} while (!bIsOver);
return listResBytes;
}
/// <summary>
/// 封包
/// </summary>
/// <param name="in_listBytes"></param>
/// <returns></returns>
public List<byte> JointPacket(List<byte[]> in_listBytes)
{
List<byte> listResDatas = new List<byte>();
Dictionary<short, List<byte>> listPacketMap = new Dictionary<short, List<byte>>();
//
short iCount = GetJointHeadCount(in_listBytes[0]);
foreach (byte[] listData in in_listBytes)
{
// 得到索引
short iHeadIndex = GetJointHeadIndex(listData);
// 得到長度
short iHeadLength = GetJointHeadLength(listData);
byte[] listToAdd = new byte[iHeadLength];
Array.Copy(listData, HeadSize, listToAdd, 0, iHeadLength);
// 添加一段到字典
List<byte> listSignal = new List<byte>();
listSignal.AddRange(listToAdd);
listPacketMap.Add(iHeadIndex, listSignal);
}
// 循環按照順序添加到列表
for (short i = 0; i < iCount; i++)
{
listResDatas.AddRange(listPacketMap[i].ToArray());
}
return listResDatas;
}
/// <summary>
/// 封包時得到包序號
/// </summary>
/// <param name="listData"></param>
/// <returns></returns>
public short GetJointHeadIndex(byte[] listData)
{
byte[] listIndex = new byte[HeadIndexSize];
Array.Copy(listData, 0, listIndex, 0, HeadIndexSize);
return BitConverter.ToInt16(listIndex, 0);
}
/// <summary>
/// 封包時得到總包數
/// </summary>
/// <param name="listData"></param>
/// <returns></returns>
public short GetJointHeadCount(byte[] listData)
{
byte[] listCount = new byte[HeadCountSize];
Array.Copy(listData, HeadIndexSize, listCount, 0, HeadCountSize);
return BitConverter.ToInt16(listCount, 0);
}
/// <summary>
/// 封包時得到內容長度
/// </summary>
/// <param name="listData"></param>
/// <returns></returns>
private short GetJointHeadLength(byte[] listData)
{
byte[] listLength = new byte[HeadLengthSize];
Array.Copy(listData, HeadIndexSize + HeadCountSize, listLength, 0, HeadLengthSize);
return BitConverter.ToInt16(listLength, 0);
}
/// <summary>
/// 封包時得到包ID
/// </summary>
/// <param name="listData"></param>
/// <returns></returns>
public short GetJointHeadID(byte[] listData)
{
byte[] listID = new byte[HeadIndexSize];
Array.Copy(listData, HeadIndexSize + HeadCountSize + HeadLengthSize, listID, 0, HeadIndexSize);
return BitConverter.ToInt16(listID, 0);
}
/// <summary>
/// 創建一個包頭
/// </summary>
/// <param name="in_iHeadIndex">序號</param>
/// <param name="in_iHeadCount">總數</param>
/// <param name="in_iHeadLength">內容長度</param>
/// <returns></returns>
public byte[] CreateHead(Int16 in_iHeadIndex, Int16 in_iHeadCount, Int16 in_iHeadLength, Int16 in_iHeadID)
{
List<byte> bHead = new List<byte>();
byte[] bHeadIndex = new byte[HeadIndexSize];
byte[] bHeadCount = new byte[HeadCountSize];
byte[] bHeadLength = new byte[HeadLengthSize];
byte[] bHeadID = new byte[HeadIDSize];
bHeadIndex = BitConverter.GetBytes(in_iHeadIndex);
bHeadCount = BitConverter.GetBytes(in_iHeadCount);
bHeadLength = BitConverter.GetBytes(in_iHeadLength);
bHeadID = BitConverter.GetBytes(in_iHeadID);
bHead.AddRange(bHeadIndex);
bHead.AddRange(bHeadCount);
bHead.AddRange(bHeadLength);
bHead.AddRange(bHeadID);
return bHead.ToArray();
}
/// <summary>
/// 拆包時統計包的數量
/// </summary>
/// <param name="iPoolLenght"></param>
/// <returns></returns>
private short GetCount(int iPoolLenght)
{
return (short)(iPoolLenght / (PacketMaxSize - HeadSize) + 1);
}
/// <summary>
/// 生成一個2字節的ID
/// </summary>
/// <returns></returns>
private short CreateID()
{
return (short)(new Random().Next(65535));
}
}
}
4.一些自定義變量:CustomParm,DataPools,ResendInfo
class CustomParm
{
public byte[] ParmBytes { get; set; }
public EndPoint ParmEndPoint { get; set; }
public CustomParm(byte[] ParmBytes, EndPoint ParmEndPoint)
{
this.ParmBytes = ParmBytes;
this.ParmEndPoint = ParmEndPoint;
}
}
class DataPools
{
public int ExistPack { get; set; }
public int TotalPack { get; set; }
public EndPoint EndPoint { get; set; }
public int LeftTime { get; set; }
public List<byte[]> PacketParking { get; set; }
public DataPools(int ExistPack, int TotalPack, EndPoint EndPoint, int LeftTime, List<byte[]> PacketParking)
{
this.ExistPack = ExistPack;
this.TotalPack = TotalPack;
this.EndPoint = EndPoint;
this.LeftTime = LeftTime;
this.PacketParking = PacketParking;
}
}
[Serializable]// 注意這裏的可序列化標誌
class ResendInfo
{
public int Key { get; set; }
public EndPoint EndPoint { get; set; }
public int Index { get; set; }
public EndPoint MyEndPoint { get; set; }
public ResendInfo(int Key, EndPoint EndPoint, int Index, EndPoint MyEndPoint)
{
this.Key = Key;
this.EndPoint = EndPoint;
this.Index = Index;
this.MyEndPoint = MyEndPoint;
}
}