C#之Socket操作類實例解析
更新時間:2014年08月26日 17:32:10 投稿:shichen2014
這篇文章主要介紹了C#的Socket操作類用法,需要的朋友可以參考下
本文展示了一個C#的Socket操作類的完整實例,并附帶了用法說明,分享給大家供大家參考之用。具體方法如下:
主要功能代碼如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Collections;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Collections.Specialized;
using System.Threading;
public class DuxSocketClient
{
#region 私有字段
/// <summary>
/// 設(shè)置數(shù)據(jù)緩沖區(qū)大小 默認1024
/// </summary>
private static int m_maxpacket = 1024 * 4;
public delegate void SendFileProgress(int progress);
public delegate void ReceiveFileProgress(int progress);
#endregion
#region 服務(wù)器偵聽
/// <summary>
/// 服務(wù)器偵聽方法 返回null則說明沒有鏈接上
/// </summary>
/// <returns>返回一個套接字(Socket)</returns>
public static Socket ListenerSocket(TcpListener listener)
{
try
{
Socket socket = listener.AcceptSocket();
return socket;
}
catch
{
return null;
}
}
/// <summary>
/// 服務(wù)器偵聽方法 返回null則說明沒有鏈接上
/// </summary>
/// <param name="listener"></param>
/// <returns>返回一個網(wǎng)絡(luò)流</returns>
public static NetworkStream ListenerStream(TcpListener listener)
{
try
{
TcpClient client = listener.AcceptTcpClient();
return client.GetStream();
}
catch
{
return null;
}
}
#endregion
#region 客戶端連接
public static Socket ConnectSocket(TcpClient tcpclient, IPEndPoint ipendpoint)
{
try
{
tcpclient.Connect(ipendpoint);
return tcpclient.Client;
}
catch
{
return null;
}
}
public static Socket ConnectSocket(TcpClient tcpclient, IPAddress ipadd, int port)
{
try
{
tcpclient.Connect(ipadd, port);
return tcpclient.Client;
}
catch
{
return null;
}
}
public static NetworkStream ConnectStream(TcpClient tcpclient, IPEndPoint ipendpoint)
{
try
{
tcpclient.Connect(ipendpoint);
return tcpclient.GetStream();
}
catch
{
return null;
}
}
public static NetworkStream ConnectStream(TcpClient tcpclient, IPAddress ipadd, int port)
{
try
{
tcpclient.Connect(ipadd, port);
return tcpclient.GetStream();
}
catch
{
return null;
}
}
#endregion
#region Socket接收數(shù)據(jù)
/// <summary>
/// 接受固定長度字符串
/// </summary>
/// <param name="socket"></param>
/// <param name="size"></param>
/// <returns></returns>
public static byte[] ReceiveFixData(Socket socket, int size)
{
int offset = 0;
int recv = 0;
int dataleft = size;
byte[] msg = new byte[size];
while (dataleft > 0)
{
recv = socket.Receive(msg, offset, dataleft, 0);
if (recv == 0)
{
break;
}
offset += recv;
dataleft -= recv;
}
return msg;
}
/// <summary>
/// 接收變長字符串
/// 為了處理粘包問題 ,每次發(fā)送數(shù)據(jù)時 包頭(數(shù)據(jù)字節(jié)長度) + 正文
/// 這個發(fā)送小數(shù)據(jù)
/// 設(shè)置包頭的字節(jié)為8,不能超過8位數(shù)的字節(jié)數(shù)組
/// </summary>
/// <param name="socket"></param>
/// <returns>byte[]數(shù)組</returns>
public static byte[] ReceiveVarData(Socket socket)
{
//每次接受數(shù)據(jù)時,接收固定長度的包頭,包頭長度為8
byte[] lengthbyte = ReceiveFixData(socket, 8);
//length得到字符長度 然后加工處理得到數(shù)字
int length = GetPacketLength(lengthbyte);
//得到正文
return ReceiveFixData(socket, length);
}
/// <summary>
/// 接收T類對象,反序列化
/// </summary>
/// <typeparam name="T">接收T類對象,T類必須是一個可序列化類</typeparam>
/// <param name="socket"></param>
/// <returns></returns>
public static T ReceiveVarData<T>(Socket socket)
{
//先接收包頭長度 固定8個字節(jié)
byte[] lengthbyte = ReceiveFixData(socket, 8);
//得到字節(jié)長度
int length = GetPacketLength(lengthbyte);
byte[] bytecoll = new byte[m_maxpacket];
IFormatter format = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
int offset = 0; //接收字節(jié)個數(shù)
int lastdata = length; //還剩下多少沒有接收,初始大小等于實際大小
int receivedata = m_maxpacket; //每次接收大小
//循環(huán)接收
int mark = 0; //標(biāo)記幾次接收到的數(shù)據(jù)為0長度
while (true)
{
//剩下的字節(jié)數(shù)是否小于緩存大小
if (lastdata < m_maxpacket)
{
receivedata = lastdata; //就只接收剩下的字節(jié)數(shù)
}
int count = socket.Receive(bytecoll,0,receivedata,0);
if (count > 0)
{
stream.Write(bytecoll, 0, count);
offset += count;
lastdata -= count;
mark = 0;
}
else
{
mark++;
if (mark == 10)
{
break;
}
}
if (offset == length)
{
break;
}
}
stream.Seek(0, SeekOrigin.Begin); //必須要這個 或者stream.Position = 0;
T t = (T)format.Deserialize(stream);
stream.Close();
return t;
}
/// <summary>
/// 在預(yù)先得到文件的文件名和大小
/// 調(diào)用此方法接收文件
/// </summary>
/// <param name="socket"></param>
/// <param name="path">路徑必須存在</param>
public static bool ReceiveFile(Socket socket, string path, string filename, long size,ReceiveFileProgress progress)
{
bool ret = false;
if (Directory.Exists(path))
{
//主要是防止有重名文件
string savepath = GetPath(path, filename); //得到文件路徑
//緩沖區(qū)
byte[] file = new byte[m_maxpacket];
int count = 0; //每次接收的實際長度
int receivedata = m_maxpacket; //每次要接收的長度
long offset = 0; //循環(huán)接收的總長度
long lastdata = size; //剩余多少還沒接收
int mark = 0;
using (FileStream fs = new FileStream(savepath, FileMode.OpenOrCreate, FileAccess.Write))
{
if (size > 0)
{
while (true)
{
if (lastdata < receivedata)
{
receivedata = Convert.ToInt32(lastdata);
}
count = socket.Receive(file, 0, receivedata, SocketFlags.None);
if (count > 0)
{
fs.Write(file, 0, count);
offset += count;
lastdata -= count;
mark = 0;
}
else
{
mark++; //連續(xù)5次接收為0字節(jié) 則跳出循環(huán)
if (mark ==10)
{
break;
}
}
//接收進度
if (progress != null)
{
progress(Convert.ToInt32(((Convert.ToDouble(offset) / Convert.ToDouble(size)) * 100)));
}
//接收完畢
if (offset == size)
{
ret = true;
break;
}
}
}
fs.Close();
}
}
return ret;
}
public static bool ReceiveFile(Socket socket, string path, string filename, long size)
{
return ReceiveFile(socket, path, filename, size,null);
}
/// <summary>
/// 預(yù)先不知道文件名和文件大小 用此方法接收
/// 此方法對于的發(fā)送方法是SendFile()
/// </summary>
/// <param name="socket"></param>
/// <param name="path">要保存的目錄</param>
public static void ReceiveFile(Socket socket, string path)
{
//得到包頭信息字節(jié)數(shù)組 (文件名 + 文件大小 的字符串長度)
//取前8位
byte[] info_bt = ReceiveFixData(socket, 8);
//得到包頭信息字符長度
int info_length = GetPacketLength(info_bt);
//提取包頭信息,(文件名 + 文件大小 的字符串長度)
byte[] info = ReceiveFixData(socket, info_length);
//得到文件信息字符串 (文件名 + 文件大小)
string info_str = System.Text.Encoding.UTF8.GetString(info);
string[] strs = info_str.Split('|');
string filename = strs[0]; //文件名
long length = Convert.ToInt64(strs[1]); //文件大小
//開始接收文件
ReceiveFile(socket, path, filename, length);
}
private static int GetPacketLength(byte[] length)
{
string str = System.Text.Encoding.UTF8.GetString(length);
str = str.TrimEnd('*'); ;//("*", "");
int _length = 0;
if (int.TryParse(str, out _length))
{
return _length;
}
else
{
return 0;
}
}
/// <summary>
/// 得到文件路徑(防止有文件名重復(fù))
/// 如:aaa.txt已經(jīng)在directory目錄下存在,則會得到文件aaa(1).txt
/// </summary>
/// <param name="directory">目錄名</param>
/// <param name="file">文件名</param>
/// <returns>文件路徑</returns>
static int i = 0;
static string markPath = String.Empty;
public static string GetPath(string directory, string file)
{
if (markPath == String.Empty)
{
markPath = Path.Combine(directory, file);
}
string path = Path.Combine(directory, file);
if (File.Exists(path))
{
i++;
string filename = Path.GetFileNameWithoutExtension(markPath) + "(" + i.ToString() + ")";
string extension = Path.GetExtension(markPath);
return GetPath(directory, filename + extension);
}
else
{
i = 0;
markPath = String.Empty;
return path;
}
}
#endregion
#region Socket發(fā)送數(shù)據(jù)
/// <summary>
/// 發(fā)送固定長度消息
/// 發(fā)送字節(jié)數(shù)不能大于int型最大值
/// </summary>
/// <param name="socket"></param>
/// <param name="msg"></param>
/// <returns>返回發(fā)送字節(jié)個數(shù)</returns>
public static int SendFixData(Socket socket, byte[] msg)
{
int size = msg.Length; //要發(fā)送字節(jié)長度
int offset = 0; //已經(jīng)發(fā)送長度
int dataleft = size; //剩下字符
int senddata = m_maxpacket; //每次發(fā)送大小
while (true)
{
//如過剩下的字節(jié)數(shù) 小于 每次發(fā)送字節(jié)數(shù)
if (dataleft < senddata)
{
senddata = dataleft;
}
int count = socket.Send(msg, offset, senddata, SocketFlags.None);
offset += count;
dataleft -= count;
if (offset == size)
{
break;
}
}
return offset;
}
/// <summary>
/// 發(fā)送變長信息 格式 包頭(包頭占8位) + 正文
/// </summary>
/// <param name="socket"></param>
/// <param name="contact">發(fā)送文本</param>
/// <returns></returns>
public static int SendVarData(Socket socket, string contact)
{
//得到字符長度
int size = System.Text.Encoding.UTF8.GetBytes(contact).Length;
//包頭字符
string length = GetSendPacketLengthStr(size);
//包頭 + 正文
byte[] sendbyte = System.Text.Encoding.UTF8.GetBytes(length + contact);
//發(fā)送
return SendFixData(socket, sendbyte);
}
/// <summary>
/// 發(fā)送變成信息
/// </summary>
/// <param name="socket"></param>
/// <param name="bytes"></param>
/// <returns></returns>
public static int SendVarData(Socket socket, byte[] bytes)
{
//得到包頭字節(jié)
int size = bytes.Length;
string length = GetSendPacketLengthStr(size);
byte[] lengthbyte = System.Text.Encoding.UTF8.GetBytes(length);
//發(fā)送包頭
SendFixData(socket, lengthbyte); //因為不知道正文是什么編碼所以沒有合并
//發(fā)送正文
return SendFixData(socket, bytes);
}
/// <summary>
/// 發(fā)送T類型對象,序列化
/// </summary>
/// <typeparam name="T">T類型</typeparam>
/// <param name="socket"></param>
/// <param name="obj">T類型對象,必須是可序列化的</param>
/// <returns></returns>
public static int SendSerializeObject<T>(Socket socket, T obj)
{
byte[] bytes = SerializeObject(obj);
return SendVarData(socket, bytes);
}
/// <summary>
/// 發(fā)送文件
/// </summary>
/// <param name="socket">socket對象</param>
/// <param name="path">文件路徑</param>
/// <param name="issend">是否發(fā)送文件(頭)信息,如果當(dāng)前知道文件[大小,名稱]則為false</param>
/// <param name="progress"></param>
/// <returns></returns>
public static bool SendFile(Socket socket, string path,bool issend,SendFileProgress progress)
{
bool ret = false;
if (File.Exists(path))
{
FileInfo fileinfo = new FileInfo(path);
string filename = fileinfo.Name;
long length = fileinfo.Length;
//發(fā)送文件信息
if (issend)
{
SendVarData(socket, filename + "|" + length);
}
//發(fā)送文件
long offset = 0;
byte[] b = new byte[m_maxpacket];
int mark = 0;
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
int senddata = b.Length;
long i = length;
//循環(huán)讀取發(fā)送
while (true)
{
int count = fs.Read(b, 0, senddata);
if (count > 0)
{
socket.Send(b, 0, count, SocketFlags.None);
offset += count;
mark = 0;
}
else
{
mark++;
if (mark == 10)
{
break;
}
}
if (progress != null)
{
progress(Convert.ToInt32(((Convert.ToDouble(offset) / Convert.ToDouble(length)) * 100)));
}
if (offset == length)
{
break;
}
Thread.Sleep(50); //設(shè)置等待時間,以免粘包
}
}
}
return ret;
}
/// <summary>
/// 發(fā)送文件,不需要進度信息
/// </summary>
/// <param name="socket">socket對象</param>
/// <param name="path">文件路徑</param>
/// <param name="issend">是否發(fā)生(頭)信息</param>
/// <returns></returns>
public static bool SendFile(Socket socket, string path,bool issend)
{
return SendFile(socket, path, issend, null);
}
/// <summary>
/// 發(fā)送文件,不需要進度信息和(頭)信息
/// </summary>
/// <param name="socket">socket對象</param>
/// <param name="path">文件路徑</param>
/// <returns></returns>
public static bool SendFile(Socket socket, string path)
{
return SendFile(socket, path, false, null);
}
private static byte[] SerializeObject(object obj)
{
IFormatter format = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
format.Serialize(stream, obj);
byte[] ret = stream.ToArray();
stream.Close();
return ret;
}
private static string GetSendPacketLengthStr(int size)
{
string length = size.ToString() + "********"; //得到size的長度
return length.Substring(0, 8); //截取前前8位
}
#endregion
#region NetworkStream接收數(shù)據(jù)
//沒寫
#endregion
#region NetworkStream發(fā)送數(shù)據(jù)
//沒寫
#endregion
}
用法說明:
每個接收的方法都對應(yīng)著有發(fā)送方法
如:
發(fā)送方法:
SendFixData(socket,"01");
接收方法:
ReceiveFixData(socket,2); //size 就為2
不知道發(fā)送文本長度:
string txt = ???? //不知道有多少字符
發(fā)送方法:
SendVarData(socket,txt); //有重載版
接收方法:
ReceiveVarData(socket);
希望本文所述實例對大家C#程序設(shè)計有所幫助。
相關(guān)文章
C#中的靜態(tài)字段double.Epsilon實例詳解
double.Epsilon 是C#中的一個靜態(tài)字段,表示 double 數(shù)據(jù)類型的最小可表示的正數(shù)值,這篇文章主要介紹了C#中的靜態(tài)字段double.Epsilon的相關(guān)知識,需要的朋友可以參考下2024-01-01
C#微信公眾平臺開發(fā)之a(chǎn)ccess_token的獲取存儲與更新
這篇文章主要介紹了C#微信公眾平臺開發(fā)之a(chǎn)ccess_token的獲取存儲與更新的相關(guān)資料,需要的朋友可以參考下2016-03-03
c# Rank屬性與GetUpperBound方法的深入分析
本篇文章是對c#中的Rank屬性與GetUpperBound方法進行了詳細的分析介紹,需要的朋友參考下2013-06-06

