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#程序设计有所帮助。