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ù)據(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 服務器偵聽 /// <summary> /// 服務器偵聽方法 返回null則說明沒有鏈接上 /// </summary> /// <returns>返回一個套接字(Socket)</returns> public static Socket ListenerSocket(TcpListener listener) { try { Socket socket = listener.AcceptSocket(); return socket; } catch { return null; } } /// <summary> /// 服務器偵聽方法 返回null則說明沒有鏈接上 /// </summary> /// <param name="listener"></param> /// <returns>返回一個網(wǎng)絡流</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ù) /// 設置包頭的字節(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; //標記幾次接收到的數(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> /// 在預先得到文件的文件名和大小 /// 調用此方法接收文件 /// </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> /// 預先不知道文件名和文件大小 用此方法接收 /// 此方法對于的發(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> /// 得到文件路徑(防止有文件名重復) /// 如: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ā)送文件(頭)信息,如果當前知道文件[大小,名稱]則為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); //設置等待時間,以免粘包 } } } 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 }
用法說明:
每個接收的方法都對應著有發(fā)送方法
如:
發(fā)送方法:
SendFixData(socket,"01");
接收方法:
ReceiveFixData(socket,2); //size 就為2
不知道發(fā)送文本長度:
string txt = ???? //不知道有多少字符
發(fā)送方法:
SendVarData(socket,txt); //有重載版
接收方法:
ReceiveVarData(socket);
希望本文所述實例對大家C#程序設計有所幫助。
相關文章
C#中的靜態(tài)字段double.Epsilon實例詳解
double.Epsilon 是C#中的一個靜態(tài)字段,表示 double 數(shù)據(jù)類型的最小可表示的正數(shù)值,這篇文章主要介紹了C#中的靜態(tài)字段double.Epsilon的相關知識,需要的朋友可以參考下2024-01-01C#微信公眾平臺開發(fā)之a(chǎn)ccess_token的獲取存儲與更新
這篇文章主要介紹了C#微信公眾平臺開發(fā)之a(chǎn)ccess_token的獲取存儲與更新的相關資料,需要的朋友可以參考下2016-03-03c# Rank屬性與GetUpperBound方法的深入分析
本篇文章是對c#中的Rank屬性與GetUpperBound方法進行了詳細的分析介紹,需要的朋友參考下2013-06-06