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的异步通讯,可以再增加一个协议类,你可以利用两类来实现符合你业务逻辑的协议,相互通讯