C#之Socket操作类实例解析
本文展示了一个C#的Socket操作类的完整实例,并附带了用法说明,分享给大家供大家参考之用。具体方法如下:
主要功能代码如下:
usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text; usingSystem.Net.Sockets; usingSystem.Collections; usingSystem.Net; usingSystem.Runtime.Serialization; usingSystem.Runtime.Serialization.Formatters.Binary; usingSystem.IO; usingSystem.Collections.Specialized; usingSystem.Threading; publicclassDuxSocketClient { #region私有字段 ///<summary> ///设置数据缓冲区大小默认1024 ///</summary> privatestaticintm_maxpacket=1024*4; publicdelegatevoidSendFileProgress(intprogress); publicdelegatevoidReceiveFileProgress(intprogress); #endregion #region服务器侦听 ///<summary> ///服务器侦听方法返回null则说明没有链接上 ///</summary> ///<returns>返回一个套接字(Socket)</returns> publicstaticSocketListenerSocket(TcpListenerlistener) { try { Socketsocket=listener.AcceptSocket(); returnsocket; } catch { returnnull; } } ///<summary> ///服务器侦听方法返回null则说明没有链接上 ///</summary> ///<paramname="listener"></param> ///<returns>返回一个网络流</returns> publicstaticNetworkStreamListenerStream(TcpListenerlistener) { try { TcpClientclient=listener.AcceptTcpClient(); returnclient.GetStream(); } catch { returnnull; } } #endregion #region客户端连接 publicstaticSocketConnectSocket(TcpClienttcpclient,IPEndPointipendpoint) { try { tcpclient.Connect(ipendpoint); returntcpclient.Client; } catch { returnnull; } } publicstaticSocketConnectSocket(TcpClienttcpclient,IPAddressipadd,intport) { try { tcpclient.Connect(ipadd,port); returntcpclient.Client; } catch { returnnull; } } publicstaticNetworkStreamConnectStream(TcpClienttcpclient,IPEndPointipendpoint) { try { tcpclient.Connect(ipendpoint); returntcpclient.GetStream(); } catch { returnnull; } } publicstaticNetworkStreamConnectStream(TcpClienttcpclient,IPAddressipadd,intport) { try { tcpclient.Connect(ipadd,port); returntcpclient.GetStream(); } catch { returnnull; } } #endregion #regionSocket接收数据 ///<summary> ///接受固定长度字符串 ///</summary> ///<paramname="socket"></param> ///<paramname="size"></param> ///<returns></returns> publicstaticbyte[]ReceiveFixData(Socketsocket,intsize) { intoffset=0; intrecv=0; intdataleft=size; byte[]msg=newbyte[size]; while(dataleft>0) { recv=socket.Receive(msg,offset,dataleft,0); if(recv==0) { break; } offset+=recv; dataleft-=recv; } returnmsg; } ///<summary> ///接收变长字符串 ///为了处理粘包问题,每次发送数据时包头(数据字节长度)+正文 ///这个发送小数据 ///设置包头的字节为8,不能超过8位数的字节数组 ///</summary> ///<paramname="socket"></param> ///<returns>byte[]数组</returns> publicstaticbyte[]ReceiveVarData(Socketsocket) { //每次接受数据时,接收固定长度的包头,包头长度为8 byte[]lengthbyte=ReceiveFixData(socket,8); //length得到字符长度然后加工处理得到数字 intlength=GetPacketLength(lengthbyte); //得到正文 returnReceiveFixData(socket,length); } ///<summary> ///接收T类对象,反序列化 ///</summary> ///<typeparamname="T">接收T类对象,T类必须是一个可序列化类</typeparam> ///<paramname="socket"></param> ///<returns></returns> publicstaticTReceiveVarData<T>(Socketsocket) { //先接收包头长度固定8个字节 byte[]lengthbyte=ReceiveFixData(socket,8); //得到字节长度 intlength=GetPacketLength(lengthbyte); byte[]bytecoll=newbyte[m_maxpacket]; IFormatterformat=newBinaryFormatter(); MemoryStreamstream=newMemoryStream(); intoffset=0;//接收字节个数 intlastdata=length;//还剩下多少没有接收,初始大小等于实际大小 intreceivedata=m_maxpacket;//每次接收大小 //循环接收 intmark=0;//标记几次接收到的数据为0长度 while(true) { //剩下的字节数是否小于缓存大小 if(lastdata<m_maxpacket) { receivedata=lastdata;//就只接收剩下的字节数 } intcount=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; Tt=(T)format.Deserialize(stream); stream.Close(); returnt; } ///<summary> ///在预先得到文件的文件名和大小 ///调用此方法接收文件 ///</summary> ///<paramname="socket"></param> ///<paramname="path">路径必须存在</param> publicstaticboolReceiveFile(Socketsocket,stringpath,stringfilename,longsize,ReceiveFileProgressprogress) { boolret=false; if(Directory.Exists(path)) { //主要是防止有重名文件 stringsavepath=GetPath(path,filename);//得到文件路径 //缓冲区 byte[]file=newbyte[m_maxpacket]; intcount=0;//每次接收的实际长度 intreceivedata=m_maxpacket;//每次要接收的长度 longoffset=0;//循环接收的总长度 longlastdata=size;//剩余多少还没接收 intmark=0; using(FileStreamfs=newFileStream(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(); } } returnret; } publicstaticboolReceiveFile(Socketsocket,stringpath,stringfilename,longsize) { returnReceiveFile(socket,path,filename,size,null); } ///<summary> ///预先不知道文件名和文件大小用此方法接收 ///此方法对于的发送方法是SendFile() ///</summary> ///<paramname="socket"></param> ///<paramname="path">要保存的目录</param> publicstaticvoidReceiveFile(Socketsocket,stringpath) { //得到包头信息字节数组(文件名+文件大小的字符串长度) //取前8位 byte[]info_bt=ReceiveFixData(socket,8); //得到包头信息字符长度 intinfo_length=GetPacketLength(info_bt); //提取包头信息,(文件名+文件大小的字符串长度) byte[]info=ReceiveFixData(socket,info_length); //得到文件信息字符串(文件名+文件大小) stringinfo_str=System.Text.Encoding.UTF8.GetString(info); string[]strs=info_str.Split('|'); stringfilename=strs[0];//文件名 longlength=Convert.ToInt64(strs[1]);//文件大小 //开始接收文件 ReceiveFile(socket,path,filename,length); } privatestaticintGetPacketLength(byte[]length) { stringstr=System.Text.Encoding.UTF8.GetString(length); str=str.TrimEnd('*');;//("*",""); int_length=0; if(int.TryParse(str,out_length)) { return_length; } else { return0; } } ///<summary> ///得到文件路径(防止有文件名重复) ///如:aaa.txt已经在directory目录下存在,则会得到文件aaa(1).txt ///</summary> ///<paramname="directory">目录名</param> ///<paramname="file">文件名</param> ///<returns>文件路径</returns> staticinti=0; staticstringmarkPath=String.Empty; publicstaticstringGetPath(stringdirectory,stringfile) { if(markPath==String.Empty) { markPath=Path.Combine(directory,file); } stringpath=Path.Combine(directory,file); if(File.Exists(path)) { i++; stringfilename=Path.GetFileNameWithoutExtension(markPath)+"("+i.ToString()+")"; stringextension=Path.GetExtension(markPath); returnGetPath(directory,filename+extension); } else { i=0; markPath=String.Empty; returnpath; } } #endregion #regionSocket发送数据 ///<summary> ///发送固定长度消息 ///发送字节数不能大于int型最大值 ///</summary> ///<paramname="socket"></param> ///<paramname="msg"></param> ///<returns>返回发送字节个数</returns> publicstaticintSendFixData(Socketsocket,byte[]msg) { intsize=msg.Length;//要发送字节长度 intoffset=0;//已经发送长度 intdataleft=size;//剩下字符 intsenddata=m_maxpacket;//每次发送大小 while(true) { //如过剩下的字节数小于每次发送字节数 if(dataleft<senddata) { senddata=dataleft; } intcount=socket.Send(msg,offset,senddata,SocketFlags.None); offset+=count; dataleft-=count; if(offset==size) { break; } } returnoffset; } ///<summary> ///发送变长信息格式包头(包头占8位)+正文 ///</summary> ///<paramname="socket"></param> ///<paramname="contact">发送文本</param> ///<returns></returns> publicstaticintSendVarData(Socketsocket,stringcontact) { //得到字符长度 intsize=System.Text.Encoding.UTF8.GetBytes(contact).Length; //包头字符 stringlength=GetSendPacketLengthStr(size); //包头+正文 byte[]sendbyte=System.Text.Encoding.UTF8.GetBytes(length+contact); //发送 returnSendFixData(socket,sendbyte); } ///<summary> ///发送变成信息 ///</summary> ///<paramname="socket"></param> ///<paramname="bytes"></param> ///<returns></returns> publicstaticintSendVarData(Socketsocket,byte[]bytes) { //得到包头字节 intsize=bytes.Length; stringlength=GetSendPacketLengthStr(size); byte[]lengthbyte=System.Text.Encoding.UTF8.GetBytes(length); //发送包头 SendFixData(socket,lengthbyte);//因为不知道正文是什么编码所以没有合并 //发送正文 returnSendFixData(socket,bytes); } ///<summary> ///发送T类型对象,序列化 ///</summary> ///<typeparamname="T">T类型</typeparam> ///<paramname="socket"></param> ///<paramname="obj">T类型对象,必须是可序列化的</param> ///<returns></returns> publicstaticintSendSerializeObject<T>(Socketsocket,Tobj) { byte[]bytes=SerializeObject(obj); returnSendVarData(socket,bytes); } ///<summary> ///发送文件 ///</summary> ///<paramname="socket">socket对象</param> ///<paramname="path">文件路径</param> ///<paramname="issend">是否发送文件(头)信息,如果当前知道文件[大小,名称]则为false</param> ///<paramname="progress"></param> ///<returns></returns> publicstaticboolSendFile(Socketsocket,stringpath,boolissend,SendFileProgressprogress) { boolret=false; if(File.Exists(path)) { FileInfofileinfo=newFileInfo(path); stringfilename=fileinfo.Name; longlength=fileinfo.Length; //发送文件信息 if(issend) { SendVarData(socket,filename+"|"+length); } //发送文件 longoffset=0; byte[]b=newbyte[m_maxpacket]; intmark=0; using(FileStreamfs=newFileStream(path,FileMode.Open,FileAccess.Read)) { intsenddata=b.Length; longi=length; //循环读取发送 while(true) { intcount=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);//设置等待时间,以免粘包 } } } returnret; } ///<summary> ///发送文件,不需要进度信息 ///</summary> ///<paramname="socket">socket对象</param> ///<paramname="path">文件路径</param> ///<paramname="issend">是否发生(头)信息</param> ///<returns></returns> publicstaticboolSendFile(Socketsocket,stringpath,boolissend) { returnSendFile(socket,path,issend,null); } ///<summary> ///发送文件,不需要进度信息和(头)信息 ///</summary> ///<paramname="socket">socket对象</param> ///<paramname="path">文件路径</param> ///<returns></returns> publicstaticboolSendFile(Socketsocket,stringpath) { returnSendFile(socket,path,false,null); } privatestaticbyte[]SerializeObject(objectobj) { IFormatterformat=newBinaryFormatter(); MemoryStreamstream=newMemoryStream(); format.Serialize(stream,obj); byte[]ret=stream.ToArray(); stream.Close(); returnret; } privatestaticstringGetSendPacketLengthStr(intsize) { stringlength=size.ToString()+"********";//得到size的长度 returnlength.Substring(0,8);//截取前前8位 } #endregion #regionNetworkStream接收数据 //没写 #endregion #regionNetworkStream发送数据 //没写 #endregion }
用法说明:
每个接收的方法都对应着有发送方法
如:
发送方法:
SendFixData(socket,"01");
接收方法:
ReceiveFixData(socket,2);//size就为2
不知道发送文本长度:
stringtxt=????//不知道有多少字符
发送方法:
SendVarData(socket,txt);//有重载版
接收方法:
ReceiveVarData(socket);
希望本文所述实例对大家C#程序设计有所帮助。