常用类之TCP连接类-socket编程
常用类之TCP连接类-socket编程
发布时间:2016-12-28 来源:查字典编辑
摘要:tcp一般用于维持一个可信任的连接,比起udp更为安全可靠,在vs.net,分别有tcpclient和udpclient以及tcpliste...

tcp一般用于维持一个可信任的连接,比起udp更为安全可靠,在vs.net,分别有tcpclient和udpclient以及tcplistener,一般开发中基本可以满足需要,但是这个有个很大的弊端,对于维持一个时间较长的,相互交互的来说,数据处理不是很明朗,vs/net中还有一个socket类,用他来做一个客户/服务器段,同时在接发数据的时候,能相互独立,这需要一个异步通讯过程

先实现服务器段:

usingSystem;

usingSystem.Net;

usingSystem.Net.Sockets;

usingSystem.Text;

usingSystem.Threading;

//Stateobjectforreadingclientdataasynchronously

namespaceTcpServer

{

publicclassStateObject

{

//Clientsocket.

publicSocketworkSocket=null;

//Sizeofreceivebuffer.

publicconstintBufferSize=1024;

//Receivebuffer.

publicbyte[]buffer=newbyte[BufferSize];

//Receiveddatastring.

publicStringBuildersb=newStringBuilder();

}

publicclassAsynchronousSocketListener

{

//Threadsignal.

publicstaticManualResetEventallDone=newManualResetEvent(false);

privatestaticSocketlistener;

privateint_port=9010;

publicAsynchronousSocketListener()

{

}

publicvoidStopListening()

{

listener.Close();

}

publicintPort

{

set

{

_port=value;

}

}

publicvoidStartListening()

{

//Databufferforincomingdata.

byte[]bytes=newByte[1024];

//Establishthelocalendpointforthesocket.

//TheDNSnameofthecomputer

//runningthelisteneris"host.contoso.com".

IPHostEntryipHostInfo=Dns.Resolve(Dns.GetHostName());

IPAddressipAddress=ipHostInfo.AddressList[0];

IPEndPointlocalEndPoint=newIPEndPoint(ipAddress,_port);

//CreateaTCP/IPsocket.

listener=newSocket(AddressFamily.InterNetwork,

SocketType.Stream,ProtocolType.Tcp);

//Bindthesockettothelocalendpointandlistenforincomingconnections.

try

{

listener.Bind(localEndPoint);

listener.Listen(100);

while(true)

{

//Settheeventtononsignaledstate.

allDone.Reset();

//Startanasynchronoussockettolistenforconnections.

Console.WriteLine("接收连接..");

listener.BeginAccept(

newAsyncCallback(AcceptCallback),

listener);

//Waituntilaconnectionismadebeforecontinuing.

allDone.WaitOne();

}

}

catch(Exceptione)

{

Console.WriteLine(e.ToString());

}

Console.WriteLine("nPressENTERtocontinue...");

Console.Read();

}

privatevoidAcceptCallback(IAsyncResultar)

{

//Signalthemainthreadtocontinue.

allDone.Set();

//Getthesocketthathandlestheclientrequest.

Socketlistener=(Socket)ar.AsyncState;

Sockethandler=listener.EndAccept(ar);

//Createthestateobject.

StateObjectstate=newStateObject();

state.workSocket=handler;

handler.BeginReceive(state.buffer,0,StateObject.BufferSize,0,

newAsyncCallback(ReadCallback),state);

}

privatevoidReadCallback(IAsyncResultar)

{

Stringcontent=String.Empty;

//Retrievethestateobjectandthehandlersocket

//fromtheasynchronousstateobject.

StateObjectstate=(StateObject)ar.AsyncState;

Sockethandler=state.workSocket;

intbytesRead=0;

//Readdatafromtheclientsocket.

if(handler.Connected)

{

try

{

bytesRead=handler.EndReceive(ar);

}

catch(Exceptionex)

{

handler.Close();

}

if(bytesRead>0)

{

//Theremightbemoredata,sostorethedatareceivedsofar.

//Checkforend-of-filetag.Ifitisnotthere,read

//moredata.

content=Encoding.ASCII.GetString(

state.buffer,0,bytesRead);

if(content.Length>0&&content.EndsWith("<EOF>"))

{

//Allthedatahasbeenreadfromthe

//client.Displayitontheconsole.

Console.WriteLine("从客户端收到{0}bytes数据.nData:{1}",

content.Length,content);

//Echothedatabacktotheclient.

Send(handler,"-数据确认,已经收到-<EOF>");

}

else

{

//Notalldatareceived.Getmore.

handler.BeginReceive(state.buffer,0,StateObject.BufferSize,0,

newAsyncCallback(ReadCallback),state);

}

}

}

}

privatevoidSend(Sockethandler,Stringdata)

{

//ConvertthestringdatatobytedatausingASCIIencoding.

byte[]byteData=Encoding.UTF8.GetBytes(data);

//Beginsendingthedatatotheremotedevice.

handler.BeginSend(byteData,0,byteData.Length,0,

newAsyncCallback(SendCallback),handler);

}

privatevoidSendCallback(IAsyncResultar)

{

try

{

//Retrievethesocketfromthestateobject.

Sockethandler=(Socket)ar.AsyncState;

//Completesendingthedatatotheremotedevice.

intbytesSent=handler.EndSend(ar);

Console.WriteLine("发送{0}bytes到客户端.",bytesSent);

handler.Shutdown(SocketShutdown.Both);

handler.Close();

}

catch(Exceptione)

{

Console.WriteLine(e.ToString());

}

}

}

}

具体调用如下:

stringp="";

AsynchronousSocketListener_server=newAsynchronousSocketListener();

_server.StartListening();

if((p=Console.ReadLine().ToLower())!="exit")

{

_server.StopListening();

}

紧接着实现客户端,客户端稍微复杂点,用一个session类来维持一个会话过程,coder类实现多种编码,Datagram类定义一个具体的数据报文,默认为64个字节大小,

usingSystem;

usingSystem.Collections;

usingSystem.Runtime.InteropServices;

usingSystem.Diagnostics;

usingSystem.Net.Sockets;

usingSystem.Net;

usingSystem.Text;

usingSystem.Threading;

usingSystem.Data;

usingSystem.Xml;

usingSystem.Xml.XPath;

namespaceClient

{

#region通讯对象

publicdelegatevoidNetEvent(objectsender,NetEventArgse);

publicclassCSocket

{

#region字段

///<summary>

///客户端与服务器之间的会话类

///</summary>

privateSession_session;

///<summary>

///客户端是否已经连接服务器

///</summary>

privatebool_isConnected=false;

privatebool_isEcho=false;

privateStringBuildersb=newStringBuilder();

///<summary>

///接收数据缓冲区大小64K

///</summary>

publicconstintDefaultBufferSize=64*1024;

///<summary>

///报文解析器

///</summary>

privateDatagramResolver_resolver;

///<summary>

///通讯格式编码解码器

///</summary>

privateCoder_coder;

///<summary>

///接收数据缓冲区

///</summary>

privatebyte[]_recvDataBuffer=newbyte[DefaultBufferSize];

publicManualResetEventallDone=newManualResetEvent(false);

#endregion

#region事件定义

//需要订阅事件才能收到事件的通知,如果订阅者退出,必须取消订阅

///<summary>

///已经连接服务器事件

///</summary>

///<summary>

///接收到数据报文事件

///</summary>

publiceventNetEventReceivedDatagram;

publiceventNetEventDisConnectedServer;

publiceventNetEventConnectedServer;

///<summary>

///连接断开事件

///</summary>

#endregion

#region属性

///<summary>

///返回客户端与服务器之间的会话对象

///</summary>

publicSessionClientSession

{

get

{

return_session;

}

}

///<summary>

///返回客户端与服务器之间的连接状态

///</summary>

publicboolIsConnected

{

get

{

return_isConnected;

}

}

publicboolIsEchoBack

{

get

{

return_isEcho;

}

}

///<summary>

///数据报文分析器

///</summary>

publicDatagramResolverResovlver

{

get

{

return_resolver;

}

set

{

_resolver=value;

}

}

///<summary>

///编码解码器

///</summary>

publicCoderServerCoder

{

get

{

return_coder;

}

}

#endregion

#region公有方法

///<summary>

///默认构造函数,使用默认的编码格式

///</summary>

publicCSocket()

{

_coder=newCoder(Coder.EncodingMothord.gb2312);

}

///<summary>

///构造函数,使用一个特定的编码器来初始化

///</summary>

///<paramname="_coder">报文编码器</param>

publicCSocket(Codercoder)

{

_coder=coder;

}

///<summary>

///连接服务器

///</summary>

///<paramname="ip">服务器IP地址</param>

///<paramname="port">服务器端口</param>

publicvirtualvoidConnect(stringip,intport)

{

if(IsConnected)

{

Close();

}

Socketnewsock=newSocket(AddressFamily.InterNetwork,

SocketType.Stream,ProtocolType.Tcp);

IPEndPointiep=newIPEndPoint(IPAddress.Parse(ip),port);

newsock.BeginConnect(iep,newAsyncCallback(Connected),newsock);

}

///<summary>

///发送数据报文

///</summary>

///<paramname="datagram"></param>

publicvirtualvoidSend(stringdatagram)

{

try

{

if(datagram.Length==0)

{

return;

}

allDone.WaitOne();

//获得报文的编码字节

byte[]data=_coder.GetEncodingBytes(datagram);

_session.ClientSocket.BeginSend(data,0,data.Length,SocketFlags.None,

newAsyncCallback(SendDataEnd),_session.ClientSocket);

}

catch(Exceptionex)

{

Console.WriteLine(ex.ToString());

}

}

///<summary>

///关闭连接

///</summary>

publicvirtualvoidClose()

{

if(!_isConnected)

{

return;

}

_session.Close();

_session=null;

_isConnected=false;

}

#endregion

#region受保护方法

///<summary>

///数据发送完成处理函数

///</summary>

///<paramname="iar"></param>

protectedvirtualvoidSendDataEnd(IAsyncResultiar)

{

try

{

Socketremote=(Socket)iar.AsyncState;

intsent=remote.EndSend(iar);

}

catch(Exceptionex)

{

Console.WriteLine(ex.ToString());

}

}

///<summary>

///建立Tcp连接后处理过程

///</summary>

///<paramname="iar">异步Socket</param>

protectedvirtualvoidConnected(IAsyncResultiar)

{

Socketsocket=(Socket)iar.AsyncState;

//返回一个与之廉洁的连接

socket.EndConnect(iar);

//创建新的会话

_session=newSession(socket);

_isConnected=true;

allDone.Set();

try

{

_session.ClientSocket.BeginReceive(_recvDataBuffer,0,

DefaultBufferSize,SocketFlags.None,

newAsyncCallback(RecvData),socket);}

catch(Exceptionex)

{

socket.Close();

}

}

///<summary>

///数据接收处理函数

///</summary>

///<paramname="iar">异步Socket</param>

publicstringrecevie()

{

returnthis.sb.ToString();

}

protectedvirtualvoidRecvData(IAsyncResultiar)

{

Socketremote=(Socket)iar.AsyncState;

try

{

stringreceivedData="";

intrecv=remote.EndReceive(iar);

if(recv>0)

{

receivedData=System.Text.Encoding.UTF8.GetString(_recvDataBuffer,0,recv);

if(receivedData.EndsWith("<EOF>"))

{

_isEcho=true;

sb.Append(receivedData);

this._session.Datagram=receivedData;

if(ReceivedDatagram==null)

{

ReceivedDatagram(this,newNetEventArgs(_session));

}

Console.WriteLine(string.Format("{0},来自{1}",receivedData,_session.ClientSocket.RemoteEndPoint.ToString()));

this.allDone.Set();

}

else

{

Console.WriteLine("listen");

_session.ClientSocket.BeginReceive(_recvDataBuffer,0,DefaultBufferSize,SocketFlags.None,

newAsyncCallback(RecvData),_session.ClientSocket);

}

}

}

catch(SocketExceptionex)

{

Console.WriteLine(ex.ToString());

}

}

#endregion

}

///<summary>

///通讯编码格式提供者,为通讯服务提供编码和解码服务

///你可以在继承类中定制自己的编码方式如:数据加密传输等

///</summary>

publicclassCoder

{

///<summary>

///编码方式

///</summary>

privateEncodingMothord_encodingMothord;

protectedCoder()

{

}

publicCoder(EncodingMothordencodingMothord)

{

_encodingMothord=encodingMothord;

}

publicenumEncodingMothord

{

gb2312=0,

Default,

Unicode,

UTF8,

ASCII,

}

///<summary>

///通讯数据解码

///</summary>

///<paramname="dataBytes">需要解码的数据</param>

///<returns>编码后的数据</returns>

publicvirtualstringGetEncodingString(byte[]dataBytes,intsize)

{

switch(_encodingMothord)

{

caseEncodingMothord.gb2312:

{

returnEncoding.GetEncoding("gb2312").GetString(dataBytes,0,size);

}

caseEncodingMothord.Default:

{

returnEncoding.Default.GetString(dataBytes,0,size);

}

caseEncodingMothord.Unicode:

{

returnEncoding.Unicode.GetString(dataBytes,0,size);

}

caseEncodingMothord.UTF8:

{

returnEncoding.UTF8.GetString(dataBytes,0,size);

}

caseEncodingMothord.ASCII:

{

returnEncoding.ASCII.GetString(dataBytes,0,size);

}

default:

{

throw(newException("未定义的编码格式"));

}

}

}

///<summary>

///数据编码

///</summary>

///<paramname="datagram">需要编码的报文</param>

///<returns>编码后的数据</returns>

publicvirtualbyte[]GetEncodingBytes(stringdatagram)

{

switch(_encodingMothord)

{

caseEncodingMothord.gb2312:

{

returnEncoding.GetEncoding("gb2312").GetBytes(datagram);

}

caseEncodingMothord.Default:

{

returnEncoding.Default.GetBytes(datagram);

}

caseEncodingMothord.Unicode:

{

returnEncoding.Unicode.GetBytes(datagram);

}

caseEncodingMothord.UTF8:

{

returnEncoding.UTF8.GetBytes(datagram);

}

caseEncodingMothord.ASCII:

{

returnEncoding.ASCII.GetBytes(datagram);

}

default:

{

throw(newException("未定义的编码格式"));

}

}

}

}

///<summary>

///数据报文分析器,通过分析接收到的原始数据,得到完整的数据报文.

///继承该类可以实现自己的报文解析方法.

///通常的报文识别方法包括:固定长度,长度标记,标记符等方法

///本类的现实的是标记符的方法,你可以在继承类中实现其他的方法

///</summary>

publicclassDatagramResolver

{

///<summary>

///报文结束标记

///</summary>

privatestringendTag;

///<summary>

///返回结束标记

///</summary>

stringEndTag

{

get

{

returnendTag;

}

}

///<summary>

///受保护的默认构造函数,提供给继承类使用

///</summary>

protectedDatagramResolver()

{

}

///<summary>

///构造函数

///</summary>

///<paramname="endTag">报文结束标记</param>

publicDatagramResolver(stringendTag)

{

if(endTag==null)

{

throw(newArgumentNullException("结束标记不能为null"));

}

if(endTag=="")

{

throw(newArgumentException("结束标记符号不能为空字符串"));

}

this.endTag=endTag;

}

///<summary>

///解析报文

///</summary>

///<paramname="rawDatagram">原始数据,返回未使用的报文片断,

///该片断会保存在Session的Datagram对象中</param>

///<returns>报文数组,原始数据可能包含多个报文</returns>

publicvirtualstring[]Resolve(refstringrawDatagram)

{

ArrayListdatagrams=newArrayList();

//末尾标记位置索引

inttagIndex=-1;

while(true)

{

tagIndex=rawDatagram.IndexOf(endTag,tagIndex+1);

if(tagIndex==-1)

{

break;

}

else

{

//按照末尾标记把字符串分为左右两个部分

stringnewDatagram=rawDatagram.Substring(

0,tagIndex+endTag.Length);

datagrams.Add(newDatagram);

if(tagIndex+endTag.Length>=rawDatagram.Length)

{

rawDatagram="";

break;

}

rawDatagram=rawDatagram.Substring(tagIndex+endTag.Length,

rawDatagram.Length-newDatagram.Length);

//从开始位置开始查找

tagIndex=0;

}

}

string[]results=newstring[datagrams.Count];

datagrams.CopyTo(results);

returnresults;

}

}

///<summary>

///客户端与服务器之间的会话类

///

///版本:1.1

///替换版本:1.0

///

///说明:

///会话类包含远程通讯端的状态,这些状态包括Socket,报文内容,

///客户端退出的类型(正常关闭,强制退出两种类型)

///</summary>

publicclassSession:ICloneable

{

#region字段

///<summary>

///会话ID

///</summary>

privateSessionId_id;

///<summary>

///客户端发送到服务器的报文

///注意:在有些情况下报文可能只是报文的片断而不完整

///</summary>

privatestring_datagram;

///<summary>

///客户端的Socket

///</summary>

privateSocket_cliSock;

///<summary>

///客户端的退出类型

///</summary>

privateExitType_exitType;

///<summary>

///退出类型枚举

///</summary>

publicenumExitType

{

NormalExit,

ExceptionExit

};

#endregion

#region属性

///<summary>

///返回会话的ID

///</summary>

publicSessionIdID

{

get

{

return_id;

}

}

///<summary>

///存取会话的报文

///</summary>

publicstringDatagram

{

get

{

return_datagram;

}

set

{

_datagram=value;

}

}

///<summary>

///获得与客户端会话关联的Socket对象

///</summary>

publicSocketClientSocket

{

get

{

return_cliSock;

}

}

///<summary>

///存取客户端的退出方式

///</summary>

publicExitTypeTypeOfExit

{

get

{

return_exitType;

}

set

{

_exitType=value;

}

}

#endregion

#region方法

///<summary>

///使用Socket对象的Handle值作为HashCode,它具有良好的线性特征.

///</summary>

///<returns></returns>

publicoverrideintGetHashCode()

{

return(int)_cliSock.Handle;

}

///<summary>

///返回两个Session是否代表同一个客户端

///</summary>

///<paramname="obj"></param>

///<returns></returns>

publicoverrideboolEquals(objectobj)

{

SessionrightObj=(Session)obj;

return(int)_cliSock.Handle==(int)rightObj.ClientSocket.Handle;

}

///<summary>

///重载ToString()方法,返回Session对象的特征

///</summary>

///<returns></returns>

publicoverridestringToString()

{

stringresult=string.Format("Session:{0},IP:{1}",

_id,_cliSock.RemoteEndPoint.ToString());

//result.C

returnresult;

}

///<summary>

///构造函数

///</summary>

///<paramname="cliSock">会话使用的Socket连接</param>

publicSession(SocketcliSock)

{

Debug.Assert(cliSock!=null);

_cliSock=cliSock;

_id=newSessionId((int)cliSock.Handle);

}

///<summary>

///关闭会话

///</summary>

publicvoidClose()

{

Debug.Assert(_cliSock!=null);

//关闭数据的接受和发送

_cliSock.Shutdown(SocketShutdown.Both);

//清理资源

_cliSock.Close();

}

#endregion

#regionICloneable成员

objectSystem.ICloneable.Clone()

{

SessionnewSession=newSession(_cliSock);

newSession.Datagram=_datagram;

newSession.TypeOfExit=_exitType;

returnnewSession;

}

#endregion

}

///<summary>

///唯一的标志一个Session,辅助Session对象在Hash表中完成特定功能

///</summary>

publicclassSessionId

{

///<summary>

///与Session对象的Socket对象的Handle值相同,必须用这个值来初始化它

///</summary>

privateint_id;

///<summary>

///返回ID值

///</summary>

publicintID

{

get

{

return_id;

}

}

///<summary>

///构造函数

///</summary>

///<paramname="id">Socket的Handle值</param>

publicSessionId(intid)

{

_id=id;

}

///<summary>

///重载.为了符合Hashtable键值特征

///</summary>

///<paramname="obj"></param>

///<returns></returns>

publicoverrideboolEquals(objectobj)

{

if(obj!=null)

{

SessionIdright=(SessionId)obj;

return_id==right._id;

}

elseif(this==null)

{

returntrue;

}

else

{

returnfalse;

}

}

///<summary>

///重载.为了符合Hashtable键值特征

///</summary>

///<returns></returns>

publicoverrideintGetHashCode()

{

return_id;

}

///<summary>

///重载,为了方便显示输出

///</summary>

///<returns></returns>

publicoverridestringToString()

{

return_id.ToString();

}

}

///<summary>

///服务器程序的事件参数,包含了激发该事件的会话对象

///</summary>

publicclassNetEventArgs:EventArgs

{

#region字段

///<summary>

///客户端与服务器之间的会话

///</summary>

privateSession_client;

#endregion

#region构造函数

///<summary>

///构造函数

///</summary>

///<paramname="client">客户端会话</param>

publicNetEventArgs(Sessionclient)

{

if(null==client)

{

throw(newArgumentNullException());

}

_client=client;

}

#endregion

#region属性

///<summary>

///获得激发该事件的会话对象

///</summary>

publicSessionClient

{

get

{

return_client;

}

}

#endregion

}

#endregion

}

具体调用为:

usingSystem;

usingSystem.Collections;

usingSystem.Diagnostics;

usingSystem.Net.Sockets;

usingSystem.Net;

usingSystem.Text;

usingSystem.Threading;

namespacetest

{

///<summary>

///Class1的摘要说明。

///</summary>

classClass1

{

///<summary>

///应用程序的主入口点。

///</summary>

[STAThread]

staticvoidMain(string[]args)

{

//

//TODO:在此处添加代码以启动应用程序

//

stringop="";

while((op=Console.ReadLine())!="exit")

{

if(op!="")

{

s(op);

}

}

}

staticvoids(stringd)

{

Client.CSocket_socket=newClient.CSocket();

_socket.Connect("192.168.0.100",9010);

_socket.Send(d+"<EOF>");

sdds=newsd();

_socket.ReceivedDatagram+=newClient.NetEvent(ds.asd);

}

}

classsd

{

publicvoidasd(objectsend,Client.NetEventArgse)

{

}

}

}

用<eof>标记来说明一段报文的结束,同时在各个阶段可以构造事件让两个类更通用些,基本上完成了socket的异步通讯,可以再增加一个协议类,你可以利用两类来实现符合你业务逻辑的协议,相互通讯

推荐文章
猜你喜欢
附近的人在看
推荐阅读
拓展阅读
相关阅读
网友关注
最新C#教程学习
热门C#教程学习
编程开发子分类