C完整的通信代码点对点,点对多,同步,异步,UDP,TCP教学文稿.doc
Good is good, but better carries it.精益求精,善益求善。C完整的通信代码点对点,点对多,同步,异步,UDP,TCP-C#完整的通信代码(点对点,点对多,同步,异步,UDP,TCP).txt20如果你努力去发现美好,美好会发现你;如果你努力去尊重他人,你也会获得别人尊重;如果你努力去帮助他人,你也会得到他人的帮助。生命就像一种回音,你送出什么它就送回什么,你播种什么就收获什么,你给予什么就得到什么。C#codenamespaceUDPServerclassProgramstaticvoidMain(stringargs)intrecv;bytedata=newbyte1024;/构建TCP服务器/得到本机IP,设置TCP端口号IPEndPointipep=newIPEndPoint(IPAddress.Any,8001);Socketnewsock=newSocket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);/绑定网络地址newsock.Bind(ipep);Console.WriteLine("ThisisaServer,hostnameis0",Dns.GetHostName();/等待客户机连接Console.WriteLine("Waitingforaclient.");/得到客户机IPIPEndPointsender=newIPEndPoint(IPAddress.Any,0);EndPointRemote=(EndPoint)(sender);recv=newsock.ReceiveFrom(data,refRemote);Console.WriteLine("Messagereceivedfrom0:",Remote.ToString();Console.WriteLine(Encoding.ASCII.GetString(data,0,recv);/客户机连接成功后,发送欢迎信息stringwelcome="Welcome!"/字符串与字节数组相互转换data=Encoding.ASCII.GetBytes(welcome);/发送信息newsock.SendTo(data,data.Length,SocketFlags.None,Remote);while(true)data=newbyte1024;/发送接受信息recv=newsock.ReceiveFrom(data,refRemote);Console.WriteLine(Encoding.ASCII.GetString(data,0,recv);newsock.SendTo(data,recv,SocketFlags.None,Remote);C#codeusingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Net;usingSystem.Net.Sockets;namespaceUDPClientclassProgramstaticvoidMain(stringargs)bytedata=newbyte1024;stringinput,stringData;/构建TCP服务器Console.WriteLine("ThisisaClient,hostnameis0",Dns.GetHostName();/设置服务IP,设置TCP端口号IPEndPointipep=newIPEndPoint(IPAddress.Parse("127.0.0.1"),8001);/定义网络类型,数据连接类型和网络协议UDPSocketserver=newSocket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);stringwelcome="Hello!"data=Encoding.ASCII.GetBytes(welcome);server.SendTo(data,data.Length,SocketFlags.None,ipep);IPEndPointsender=newIPEndPoint(IPAddress.Any,0);EndPointRemote=(EndPoint)sender;data=newbyte1024;intrecv=server.ReceiveFrom(data,refRemote);Console.WriteLine("Messagereceivedfrom0:",Remote.ToString();Console.WriteLine(Encoding.ASCII.GetString(data,0,recv);while(true)input=Console.ReadLine();if(input="exit")break;server.SendTo(Encoding.ASCII.GetBytes(input),Remote);data=newbyte1024;recv=server.ReceiveFrom(data,refRemote);stringData=Encoding.ASCII.GetString(data,0,recv);Console.WriteLine(stringData);Console.WriteLine("StoppingClient.");server.Close();C#codeTCPClientTCPClient类提供了一种使用TCP协议连接到某个端点的简化方法。它还通过NetworkStream对象展现在连接过程中读取或写入的数据。请参见下面从QuickStart文档中摘录的日期/时间客户机示例。使用C#编写usingSystem;usingSystem.Net;usingSystem.Net.Sockets;usingSystem.IO;usingSystem.Text;classClientpublicstaticvoidMain(Stringargs)TCPClienttcpc=newTCPClient();Byteread=newByte32;if(args.Length!=1)Console.WriteLine(“请在命令行中指定服务器名称”);return;Stringserver=args0;/验证服务器是否存在if(DNS.GetHostByName(server)=null)Console.WriteLine(“找不到服务器:”+服务器);return;/尝试连接到服务器if(tcpc.Connect(server,13)=-1)Console.WriteLine(“无法连接到服务器:”+服务器);return;/获取流Streams=tcpc.GetStream();/读取流并将它转换为ASCII码形式intbytes=s.Read(read,0,read.Length);StringTime=Encoding.ASCII.GetString(read);/显示数据Console.WriteLine(“已接收到的”+字节+“字节”);Console.WriteLine(“当前日期和时间是:”+时间);tcpc.Close();TCPListenerTCPListener类便于在来自某个客户机的TCP连接的特定套接字上进行侦听的工作。请参见下面包括在QuickStart文档中的日期/时间服务器示例。使用C#编写usingSystem;usingSystem.Net;usingSystem.Net.Sockets;usingSystem.Text;classServerpublicstaticvoidMain()DateTimenow;StringstrDateLine;EncodingASCII=Encoding.ASCII;/在端口13进行侦听TCPListenertcpl=newTCPListener(13);tcpl.Start();Console.WriteLine(“正在等待客户进行连接”);Console.WriteLine(“请按Ctrl+c退出.”);while(true)/接收会阻塞,直到有人连接上Sockets=tcpl.Accept();/获取当前的日期和时间并将它连接成一个字符串now=DateTime.Now;strDateLine=now.ToShortDateString()+""+now.ToLongTimeString();/将该字符串转换成一个字节数组并发送它BytebyteDateLine=ASCII.GetBytes(strDateLine.ToCharArray();s.Send(byteDateLine,byteDateLine.Length,0);Console.WriteLine(“发送”+strDateLine);#region"Download:FiletransferFROMftpserver"/<summary>/CopyafilefromFTPservertolocal/</summary>/<paramname="sourceFilename">Targetfilename,ifrequired</param>/<paramname="localFilename">Fullpathofthelocalfile</param>/<returns></returns>/<remarks>Targetcanbeblank(usesamefilename),orjustafilename/(assumescurrentdirectory)orafullpathandfilename</remarks>publicboolDownload(stringsourceFilename,stringlocalFilename,boolPermitOverwrite)/2.determinetargetfileFileInfofi=newFileInfo(localFilename);returnthis.Download(sourceFilename,fi,PermitOverwrite);/VersiontakinganFtpFileInfopublicboolDownload(FtpFileInfofile,stringlocalFilename,boolpermitOverwrite)returnthis.Download(file.FullName,localFilename,permitOverwrite);/AnotherversiontakingFtpFileInfoandFileInfopublicboolDownload(FtpFileInfofile,FileInfolocalFI,boolpermitOverwrite)returnthis.Download(file.FullName,localFI,permitOverwrite);/Versiontakingstring/FileInfopublicboolDownload(stringsourceFilename,FileInfotargetFI,boolpermitOverwrite)/1.checktargetif(targetFI.Exists&&!(permitOverwrite)throw(newApplicationException("Targetfilealreadyexists");/2.checksourcestringtarget;if(sourceFilename.Trim()="")throw(newApplicationException("Filenotspecified");elseif(sourceFilename.Contains("/")/treatasafullpathtarget=AdjustDir(sourceFilename);else/treatasfilenameonly,usecurrentdirectorytarget=CurrentDirectory+sourceFilename;stringURI=Hostname+target;/3.performcopySystem.Net.FtpWebRequestftp=GetRequest(URI);/Setrequesttodownloadafileinbinarymodeftp.Method=System.Net.WebRequestMethods.Ftp.DownloadFile;ftp.UseBinary=true;/openrequestandgetresponsestreamusing(FtpWebResponseresponse=(FtpWebResponse)ftp.GetResponse()using(StreamresponseStream=response.GetResponseStream()/looptoread&writetofileusing(FileStreamfs=targetFI.OpenWrite()trybytebuffer=newbyte2048;intread=0;doread=responseStream.Read(buffer,0,buffer.Length);fs.Write(buffer,0,read);while(!(read=0);responseStream.Close();fs.Flush();fs.Close();catch(Exception)/catcherroranddeletefileonlypartiallydownloadedfs.Close();/deletetargetfileasit'sincompletetargetFI.Delete();throw;responseStream.Close();response.Close();returntrue;#endregion简单的UDP收发.发送C#codetrySockets=newSocket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);/向此网段发广播包intUDPListenerPort=8082;IPAddressbroadcast=IPAddress.Parse("192.168.0.255");/此处根据IP及子网掩码改为相应的广播IPstringts="ThisisUPDstringforsending"bytesendbuf=Encoding.ASCII.GetBytes(ts);IPEndPointep=newIPEndPoint(broadcast,UDPListenerPort);s.SendTo(sendbuf,ep);catch(Exceptione)接收C#codeUdpClientlistener;intUDPListenerPort=8082;IPEndPointgroupEP=newIPEndPoint(IPAddress.Any,UDPListenerPort);trywhile(true)bytebytes=listener.Receive(refgroupEP);stringRecIP=groupEP.ToString().Substring(0,groupEP.ToString().IndexOf(":");/收到发送UPD端的IPstringRecStr=Encoding.ASCII.GetString(bytes,0,bytes.Length);/收到的UPD字符串catchC#codeTCPClientTCPClient类提供了一种使用TCP协议连接到某个端点的简化方法。它还通过NetworkStream对象展现在连接过程中读取或写入的数据。请参见下面从QuickStart文档中摘录的日期/时间客户机示例。使用C#编写usingSystem;usingSystem.Net;usingSystem.Net.Sockets;usingSystem.IO;usingSystem.Text;classClientpublicstaticvoidMain(Stringargs)TCPClienttcpc=newT来一个Remoting的:C#codeusingSystem;namespaceRemotablepublicclassRemotableType:MarshalByRefObjectprivatestring_internalString="ThisistheRemotableType."publicstringStringMethod()return_internalString;usingSystem;usingSystem.Runtime.Remoting;namespaceRemotingFirstpublicclassListenerpublicstaticvoidMain()RemotingConfiguration.Configure("Listener.exe.config");Console.WriteLine("Listeningforrequests.PressEntertoexit");Console.ReadLine();usingSystem;usingSystem.Runtime.Remoting;namespaceClientpublicclassClientpublicstaticvoidMain()RemotingConfiguration.Configure("Client.exe.config");Remotable.RemotableTyperemoteObject=newRemotable.RemotableType();Console.WriteLine(remoteObject.StringMethod();Listener.exe.config<?xmlversion="1.0"encoding="utf-8"?><configuration><system.runtime.remoting><application><service><wellknownmode="Singleton"type="Remotable.RemotableType,RemotableType"objectUri="RemotableType.rem"/></service><channels><channelref="http"port="8989"/></channels></application></system.runtime.remoting></configuration>实只要用到Socket联接,基本上就得使用Thread,是交叉使用的。C#封装的Socket用法基本上不算很复杂,只是不知道托管之后的Socket有没有其他性能或者安全上的问题。在C#里面能找到的最底层的操作也就是socket了,概念不做解释。程序模型如下:WinForm程序:启动端口侦听;监视Socket联接情况;定期关闭不活动的联接;Listener:处理Socket的Accept函数,侦听新链接,建立新Thread来处理这些联接(Connection)。Connection:处理具体的每一个联接的会话。1:WinForm如何启动一个新的线程来启动Listener:/starttheserverprivatevoidbtn_startServer_Click(objectsender,EventArgse)/this.btn_startServer.Enabled=false;Thread_createServer=newThread(newThreadStart(WaitForConnect);_createServer.Start();/waitallconnectionsprivatevoidWaitForConnect()SocketListenerlistener=newSocketListener(Convert.ToInt32(this.txt_port.Text);listener.StartListening();因为侦听联接是一个循环等待的函数,所以不可能在WinForm的线程里面直接执行,不然Winform也就是无法继续任何操作了,所以才指定一个新的线程来执行这个函数,启动侦听循环。这一个新的线程是比较简单的,基本上没有启动的参数,直接指定处理函数就可以了。2:Listener如何启动循环侦听,并且启动新的带有参数的线程来处理Socket联接会话。先看如何建立侦听:(StartListening函数)IPEndPointlocalEndPoint=newIPEndPoint(_ipAddress,_port);/CreateaTCP/IPsocket.Socketlistener=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);/Bindthesockettothelocalendpointandlistenforincomingconnections.trylistener.Bind(localEndPoint);listener.Listen(20);/20trucks/Startlisteningforconnections.while(true)/herewillbesuspendedwhilewaitingforanewconnection.Socketconnection=listener.Accept();Logger.Log("Connect",connection.RemoteEndPoint.ToString();/logit,newconnection基本步骤比较简单:建立本机的IPEndPoint对象,表示以本机为服务器,在指定端口侦听;然后绑定到一个侦听Socket上;进入while循环,等待新的联接;如果有新的联接,那么建立新的socket来对应这个联接的会话。值得注意的就是这一句联接代码:listener.Accept()。执行这一句的时候,程序就在这个地方等待,直到有新的联检请求的时候程序才会执行下一句。这是同步执行,当然也可以异步执行。新的联接Socket建立了(Accept之后),对于这些新的socket该怎么办呢?他们依然是一个循环等待,所以依然需要建立新的Thread给这些Socket去处理会话(接收/发送消息),而这个Thread就要接收参数了。Thread本身是不能接收参数的,为了让它可以接收参数,可以采用定义新类,添加参数作为属性的方法来解决。因为每一个Socket是一个Connection周期,所以我定义了这么一个类publicclassConnection。这个类至少有这样一个构造函数publicConnection(Socketsocket);之所以这么做,就是为了把Socket参数传给这个Connection对象,然后好让Listener启动这个Thread的时候,Thread可以知道他正在处理哪一个Socket。具体处理的方法:(在Listener的StartListening函数,ocketconnection=listener.Accept();之后)ConnectiongpsCn=newConnection(connection);/eachsocketwillbewaitfordata.keeptheconnection.Threadthread=newThread(newThreadStart(gpsCn.WaitForSendData);thread.Name=connection.RemoteEndPoint.ToString();thread.Start();如此一来,这个新的socket在Accept之后就在新的Thread中运行了。3:Connection的会话处理建立了新的Connection(也就是socket),远程就可以和这个socket进行会话了,无非就是send和receive。现在先看看怎么写的这个线程运行的Connection.WaitForSendData函数while(true)bytes=newbyte1024;stringdata=""/systmwillbewaitingthemsgofreceiveenvet.likeAccept();/herewillbesuspendedwhilewaitingforsocketincomemsg.intbytesRec=this._connection.Receive(bytes);_lastConnectTime=DateTime.Now;if(bytesRec=0)/closeenventLogger.Log("CloseConnection",_connection.RemoteEndPoint.ToString();break;data+=Encoding.ASCII.GetString(bytes,0,bytesRec);/.handleyourdata.可以看到这个处理的基本步骤如下:执行Receive函数,接收远程socket发送的信息;把信息从字节转换到string;处理该信息,然后进入下一个循环,继续等待socket发送新的信息。值得注意的有几个:1:Receive函数。这个函数和Listener的Accept函数类似。在这个地方等待执行,如果没有新的消息,这个函数就不会执行下一句,一直等待。2:接收的是字节流,需要转化成字符串3:判断远程关闭联接的方式4:如果对方的消息非常大,还得循环接收这个data。4:如何管理这些联接(thread)通过上边的程序,基本上可以建立一个侦听,并且处理联接会话。但是如何管理这些thread呢?不然大量产生thread可是一个灾难。管理的方法比较简单,在Listener里面我定义了一个静态的哈希表(staticpublicHashtableConnections=newHashtable();),存储Connection实例和它对应的Thread实例。而connection中也加入了一个最后联接时间的定义(privateDateTime_lastConnectTime;)。在新链接建立的时候(Listener的Accept()之后)就把Connection实例和Thread实例存到哈希表中;在Connection的Receive的时候修改最后联接时间。这样我们就可以知道该Connection在哪里,并且会话是否活跃。然后在Winform程序里头可以管理这些会话了,设置设置超时。在网络环境下,我们最感兴趣的两个命名空间是System.Net和System.Net.Sockets。System.Net命名空间通常与较高程的操作有关,例如download或upload,试用HTTP和其他协议进行Web请求等等,而System.Net.Sockets命名空间所包含的类通常与较低程的操作有关。如果要直接使用Sockets或者TCP/IP之类的协议,这个命名空间的类是非常有用的。在.Net中,System.Net.Sockets命名空间为需要严密控制网络访问的开发人员提供了WindowsSockets(Winsock)接口的托管实现。System.Net命名空间中的所有其他网络访问类都建立在该套接字Socket实现之上,如TCPClient、TCPListener和UDPClient类封装有关创建到Internet的TCP和UDP连接的详细信息;NetworkStream类则提供用于网络访问的基础数据流等,常见的许多Internet服务都可以见到Socket的踪影,如Telnet、Http、Email、Echo等,这些服务尽管通讯协议Protocol的定义不同,但是其基础的传输都是采用的Socket。其实,Socket可以象流Stream一样被视为一个数据通道,这个通道架设在应用程序端(客户端)和远程服务器端之间,而后,数据的读取(接收)和写入(发送)均针对这个通道来进行。可见,在应用程序端或者服务器端创建了Socket对象之后,就可以使用Send/SentTo方法将数据发送到连接的Socket,或者使用Receive/ReceiveFrom方法接收来自连接Socket的数据。针对Socket编程,.NET框架的Socket类是Winsock32API提供的套接字服务的托管代码版本。其中为实现网络编程提供了大量的方法,大多数情况下,Socket类方法只是将数据封送到它们的本机Win32副本中并处理任何必要的安全检查。如果你熟悉WinsockAPI函数,那么用Socket类编写网络程序会非常容易,当然,如果你不曾接触过,也不会太困难,跟随下面的解说,你会发觉使用Socket类开发windows网络应用程序原来有规可寻,它们在大多数情况下遵循大致相同的步骤。在使用之前,你需要首先创建Socket对象的实例,这可以通过Socket类的构造方法来实现:publicSocket(AddressFamilyaddressFamily,SocketTypesocketType,ProtocolTypeprotocolType);其中,addressFamily参数指定Socket使用的寻址方案,socketType参数指定Socket的类型,protocolType参数指定Socket使用的协议。下面的示例语句创建一个Socket,它可用于在基于TCP/IP的网络(如Internet)上通讯。Sockettemp=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);若要使用UDP而不是TCP,需要更改协议类型,如下面的示例所示:Sockettemp=newSocket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);一旦创建Socket,在客户端,你将可以通过Connect方法连接到指定的服务器,并通过Send/SendTo方法向远程服务器发送数据,而后可以通过Receive/ReceiveFrom从服务端接收数据;而在服务器端,你需要使用Bind方法绑定所指定的接口使Socket与一个本地终结点相联,并通过Listen方法侦听该接口上的请求,当侦听到用户端的连接时,调用Accept完成连接的操作,创建新的Socket以处理传入的连接请求。使用完Socket后,记住使用Shutdown方法禁用Socket,并使用Close方法关闭Socket。可以看出,以上许多方法包含EndPoint类型的参数,在Internet中,TCP/IP使用一个网络地址和一个服务端口号来唯一标识设备。网络地址标识网络上的特定设备;端口号标识要连接到的该设备上的特定服务。网络地址和服务端口的组合称为终结点,在.NET框架中正是由EndPoint类表示这个终结点,它提供表示网络资源或服务的抽象,用以标志网络地址等信息。.Net同时也为每个受支持的地址族定义了EndPoint的子代;对于IP地址族,该类为IPEndPoint。IPEndPoint类包含应用程序连接到主机上的服务所需的主机和端口信息,通过组合服务的主机IP地址和端口号,IPEndPoint类形成到服务的连接点。用到IPEndPoint类的时候就不可避免地涉及到计算机IP地址,System.Net命名空间中有两种类可以得到IP地址实例:IPAddress类:IPAddress类包含计算机在IP网络上的地址。其Parse方法可将IP地址字符串转换为IPAddress实例。下面的语句创建一个IPAddress实例:IPAddressmyIP=IPAddress.Parse("192.168.0.1");Dns类:向使用TCP/IPInternet服务的应用程序提供域名服务。其Resolve方法查询DNS服务器以将用户友好的域名(如"")映射到数字形式的Internet地址(如192.168.0.1)。Resolve方法返回一个IPHostEnty实例,该实例包含所请求名称的地址和别名的列表。大多数情况下,可以使用AddressList数组中返回的第一个地址。下面的代码获取一个IPAddress实例,该实例包含服务器的IP地址。IPHostEntryipHostInfo=Dns.Resol