C#之Socket操作类实例解析
程序员文章站
2023-12-17 12:26:40
本文展示了一个c#的socket操作类的完整实例,并附带了用法说明,分享给大家供大家参考之用。具体方法如下:
主要功能代码如下:
using system;...
本文展示了一个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> /// 设置数据缓冲区大小 默认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>返回一个网络流</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接收数据 /// <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> /// 接收变长字符串 /// 为了处理粘包问题 ,每次发送数据时 包头(数据字节长度) + 正文 /// 这个发送小数据 /// 设置包头的字节为8,不能超过8位数的字节数组 /// </summary> /// <param name="socket"></param> /// <returns>byte[]数组</returns> public static byte[] receivevardata(socket socket) { //每次接受数据时,接收固定长度的包头,包头长度为8 byte[] lengthbyte = receivefixdata(socket, 8); //length得到字符长度 然后加工处理得到数字 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个字节 byte[] lengthbyte = receivefixdata(socket, 8); //得到字节长度 int length = getpacketlength(lengthbyte); byte[] bytecoll = new byte[m_maxpacket]; iformatter format = new binaryformatter(); memorystream stream = new memorystream(); int offset = 0; //接收字节个数 int lastdata = length; //还剩下多少没有接收,初始大小等于实际大小 int receivedata = m_maxpacket; //每次接收大小 //循环接收 int mark = 0; //标记几次接收到的数据为0长度 while (true) { //剩下的字节数是否小于缓存大小 if (lastdata < m_maxpacket) { receivedata = lastdata; //就只接收剩下的字节数 } 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); //得到文件路径 //缓冲区 byte[] file = new byte[m_maxpacket]; int count = 0; //每次接收的实际长度 int receivedata = m_maxpacket; //每次要接收的长度 long offset = 0; //循环接收的总长度 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++; //连续5次接收为0字节 则跳出循环 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> /// 预先不知道文件名和文件大小 用此方法接收 /// 此方法对于的发送方法是sendfile() /// </summary> /// <param name="socket"></param> /// <param name="path">要保存的目录</param> public static void receivefile(socket socket, string path) { //得到包头信息字节数组 (文件名 + 文件大小 的字符串长度) //取前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已经在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发送数据 /// <summary> /// 发送固定长度消息 /// 发送字节数不能大于int型最大值 /// </summary> /// <param name="socket"></param> /// <param name="msg"></param> /// <returns>返回发送字节个数</returns> public static int sendfixdata(socket socket, byte[] msg) { int size = msg.length; //要发送字节长度 int offset = 0; //已经发送长度 int dataleft = size; //剩下字符 int senddata = m_maxpacket; //每次发送大小 while (true) { //如过剩下的字节数 小于 每次发送字节数 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> /// 发送变长信息 格式 包头(包头占8位) + 正文 /// </summary> /// <param name="socket"></param> /// <param name="contact">发送文本</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); //发送 return sendfixdata(socket, sendbyte); } /// <summary> /// 发送变成信息 /// </summary> /// <param name="socket"></param> /// <param name="bytes"></param> /// <returns></returns> public static int sendvardata(socket socket, byte[] bytes) { //得到包头字节 int size = bytes.length; string length = getsendpacketlengthstr(size); byte[] lengthbyte = system.text.encoding.utf8.getbytes(length); //发送包头 sendfixdata(socket, lengthbyte); //因为不知道正文是什么编码所以没有合并 //发送正文 return sendfixdata(socket, bytes); } /// <summary> /// 发送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> /// 发送文件 /// </summary> /// <param name="socket">socket对象</param> /// <param name="path">文件路径</param> /// <param name="issend">是否发送文件(头)信息,如果当前知道文件[大小,名称]则为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; //发送文件信息 if (issend) { sendvardata(socket, filename + "|" + length); } //发送文件 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; //循环读取发送 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> /// 发送文件,不需要进度信息 /// </summary> /// <param name="socket">socket对象</param> /// <param name="path">文件路径</param> /// <param name="issend">是否发生(头)信息</param> /// <returns></returns> public static bool sendfile(socket socket, string path,bool issend) { return sendfile(socket, path, issend, null); } /// <summary> /// 发送文件,不需要进度信息和(头)信息 /// </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接收数据 //没写 #endregion #region networkstream发送数据 //没写 #endregion }
用法说明:
每个接收的方法都对应着有发送方法
如:
发送方法:
sendfixdata(socket,"01");
接收方法:
receivefixdata(socket,2); //size 就为2
不知道发送文本长度:
string txt = ???? //不知道有多少字符
发送方法:
sendvardata(socket,txt); //有重载版
接收方法:
receivevardata(socket);
希望本文所述实例对大家c#程序设计有所帮助。