2023年7月15日发(作者:)
C#Socket编程详解(三)Socket编程三、Socket编程1、UDP通信1.1采⽤Socket实现UDP1.1.1简介Socket实现UDP的基本步骤如下:(1)创建⼀个Socket对象Socket mySocket = new Socket(etwork,, );AddressFamily 寻址类型etwork代表IPV4。SocketType 套接字类型表⽰使⽤数据报协议。ProtocolType 协议类型表⽰使⽤UDP协议但需要注意的是套接字类型与协议类型并不是可以随便组合。如下表SocketTypeStream(使⽤字节流)Dgram(使⽤数据报)RawRawProtocolTypeTcpUdpIcmpRaw描述⾯向连接⾯向⽆连接⽹际消息控制基础传输协议
(2)将创建的套接字对象与本地IPEndPoint进⾏绑定。完成上述步骤后,那么创建的套接字就能够在本地IPEndPoint上接收流⼊的UDP数据包,或者将流出的UDP数据包通过本地IPEndPoint发送到⽹络中其他任意设备。(EndPointlocalEP)//绑定本地IP注意:1、绑定的⽬的主要是绑定端⼝,接收⽅指定在本机的哪个端⼝接收数据。2、并不是所有机器都需要bind: (1)A机仅发送数据,B机仅⽤来接收A发送的数据。如下图所⽰:此时A不⽤绑定本机IP(也可以绑定),B必须绑定本机IP,否则报错。因为A只需要发送,不需要接收,从⽽就不需要监听本机的特定端⼝来接收UDP数据报。B机必须要绑定本机IP。因为A在SendTo时会向指定的端⼝发送UDP数据报,B机需要监听本机相应端⼝,才能收到A发送过来的数据。(2)B发消息A接收,A接收到消息后发送消息B接收。如下图所⽰:此时A必须绑定,B不需要绑定(也可以绑定)。A需要在程序启动时,⾸先监听特定端⼝等待接收信息,因此需要绑定。B⾸先发送数据,不需要在指定的UDP端⼝等待流⼊的数据。B在发送时socket会随机指定B机的端⼝进⾏发送。A收到数据,同时会获取B端的发送IP与端⼝。即ReceiveFrom(byte[] data , ref EndPoint Remote)中的Remote就是B端socket发送数据时使⽤的IP与端⼝信息。A再发送数据时会⽤收到的B机的端⼝进⾏发送。即SendTo(byte[] data , EndPointRemote),Remote就是上⾯ReceiveFrom⽅法获取的Remote。此时B在接收时eFrom监听的IP与端⼝就是发送数据时使⽤的IP与端⼝。总结:通常,接收⽅需要绑定端⼝,发送⽅发送数据时指定的端⼝(要发送到接收⽅的哪个端⼝)应与接收⽅监听的端⼝⼀致。绑定的⽬的是为了监听本机的接收数据的端⼝。发送⽅发送数据的时候会明确告知往接收⽅的哪个端⼝发送。(3)发送数据使⽤UDP进⾏通信时,不需要连接。因为异地的主机之间没有建⽴连接,所以UDP不能使⽤标准的Send()和Receive()套接字⽅法,⽽是使⽤两个其他的⽅法:SendTo(byte[] data , EndPoint Remote)和ReceiveFrom(byte[] data , ref EndPoint Remote)。 SendTo()⽅法指定要发送的数据,和⽬标机器的IPEndPoint。该⽅法有多种不同的使⽤⽅法,可以根据具体的应⽤进⾏选择,但是⾄少要指定数据包和⽬标机器。如下:SendTo(byte[] data , EndPointRemote)<1>⼴播发送:在发送前应对socket进⾏设置,如下ketOption(,ast,1);⼴播消息的⽬的IP地址是⼀种特殊IP地址,称为⼴播地址。⼴播地址由IP地址⽹络前缀加上全1主机后缀组成,如:192.168.1.255是192.169.1.0这个⽹络的⼴播地址;130.168.255.255是130.168.0.0这个⽹络的⼴播地址。向全部为1的IP地址(255.255.255.255)发送消息的话,那么理论上全世界所有的联⽹的计算机都能收得到了。但实际上不是这样的,⼀般路由器上设置抛弃这样的包,只在本地⽹内⼴播,所以效果和向本地⽹的⼴播地址发送消息是⼀样的。通常EndPoint Remote = new IPEndPoint(ast,9050)其中端⼝号要确定好。<2>⼀对⼀发送 ⼀对⼀发送⽆需设置socket EndPoint Remote需要制定接收机的IP与接收端⼝。(4)接收数据ReceiveFrom()⽅法同SendTo()⽅法类似,但是使⽤EndPoint对象声明的⽅式不⼀样。利⽤ref修饰。接收⽅接收到数据后Remote为发送⽅的IP与端⼝信息。如果发送⽅没有绑定,则发送⽅在发送的时候,操作系统会随机指定⼀个端⼝,接收⽅的Remote就是发送⽅随机确定的IP与端⼝。ReceiveFrom()⽅法是⼀个阻塞⽅法,会⼀直等待,直到接收到数据才会往下执⾏程序。int ReceiveFrom(byte[] data ,ref EndPointRemote)int返回参数返回的是接收到的byte[]长度。例如发送⽅发送了⼀个长度为1024的byte[],接收⽅接收到了1024长度的byte[],则返回1024。参数data的长度不能⼩于发送⽅发送的⼀包数据的长度。例如发送⽅发送了1024长度的byte[]。则data的长度应⼤于等于1024,否则由于长度不够会报错。UDP应⽤不是严格意义上的真正的服务器和客户机,⽽是平等的关系,即没有主与次的关系。通常,我们都是将⼴播发送⽅当做服务端,⼴播接收⽅当做客户端。1.1.2程序⽰例1.1.2.1实现⼴播功能服务端发送⼴播、客户端接收⼴播。服务端代码:static void Main(string[] args){ sock = newSocket(etwork, ,);//如果是⼴播通常这样设定发送地址,⼀定要与接收端绑定的端⼝⼀致! iep1 = newIPEndPoint(ast, 9050); stringhostname = tName(); data =es(hostname);//设置socket,否则程序报错 ketOption(,ast,1); Thread t = newThread(BroadcastMessage); (); y();
}private static void BroadcastMessage(){ while (true){ //向iep1发送⼴播数据 (data, iep1); (2000); }
}
客户端代码:Socket sock = new Socket(etwork, , );//获取本机所有IP进⾏监听//表⽰本机ip,换⾔之,如果服务器绑定此地址,则表⽰侦听//本机所有ip对应的那个端⼝(本机可能有多个ip或只有⼀个ip)IPEndPoint iep =new IPEndPoint(, 9050);//绑定本机端⼝,⽤于接收UDP数据包(iep);//iep可以是任意IP,因为有多⽹卡情况的存在,在ReceiveFrom后会被赋值为发送端的地址EndPoint ep = (EndPoint)iep;ine("Ready to receive…");byte[] data = new byte[1024];//是⼀个阻塞⽅法,会⼀直等待数据,直到接收到数据才会往下执⾏//返回的地址ep是路由器地址,因为是由路由器转发的信息。int recv = eFrom(data, ref ep);string stringData = ing(data, 0, recv);
ine("received: {0} from: {1}",stringData,ng());();
y();1.1.2.2实现⼀对⼀通信服务器端代码:int recv;byte[] data = new byte[1024];//得到本机IP,设置TCP端⼝号
IPEndPoint ip = new IPEndPoint(, 8001);Socket newsock = new Socket(etwork,, );//绑定⽹络地址,这⾥之所以绑定,是因为要侦听客户端发送来的数据(ip);ine("This is a Server, host name is{0}", tName());//等待客户机连接ine("Waiting for a client");//得到客户机IPEndPoint Remote = new IPEndPoint(, 0);//获得的Remote是客户端地址recv = eFrom(data, ref Remote);ine("Message received from {0}:", ng());ine(ing(data, 0,recv));//客户机连接成功后,发送信息string welcome = "你好 ! ";//字符串与字节数组相互转换data = es(welcome);//发送信息(data, , ,Remote);();
客户端代码:byte[] data = new byte[1024];string input, stringData;ine("This is a Client, host name is{0}", tName());//设置为服务端IP与端⼝IPEndPoint ip = new IPEndPoint(("127.0.0.1"),8001);//定义⽹络类型,数据连接类型和⽹络协议UDPSocket server = new Socket(etwork,, );string welcome = "你好! ";data = es(welcome);//向服务端发送数据,此时客户端通过本机哪个IP与端⼝发送数据是随机的,//该信息被存放在socket中。//服务端接收到信息会得到客户端的发送地址,服务端利⽤这个地址向客户端//发送消息,客户端直接利⽤这个socket就可以接收数据,从⽽⽆需bind。(data, , , ip);EndPoint Remote = new IPEndPoint(, 0);data = new byte[1024];//获得的Remote是服务端地址int recv = eFrom(data, ref Remote);ine("Message received from {0}:", ng());ine(ing(data, 0,recv));ine("Stopping Client.");();
注意:该例⼦是服务端接收到客户端的信息后,直接利⽤receiveFrom获取的客户端IP向客户端发送消息。因此,客户端利⽤发送时的IP与端⼝进⾏信息的接收,⽆需bind。 如果服务端与客户端指定了各⾃的接收端⼝,那么双⽅都需要绑定各⾃的端⼝进⾏监听。举例:服务端(IP:172.1.1.1监听端⼝:8001)IPEndPoint Local = new IPEndPoint(, 8001);IPEndPoint Remote = new IPEndPoint(IPAddress. Parse("127.1.1.2"),8002);(Local);//向客户端绑定的地址发送信息(data,Remote);客户端(IP:172.1.1.2 监听端⼝:8002)IPEndPoint Local = new IPEndPoint(, 8001);IPEndPoint Remote = new IPEndPoint(IPAddress. Parse("127.1.1.2"),8002);(Local);//向服务端绑定的地址发送信息(data,Remote);
⼀旦socket绑定了IP与端⼝,那么会通过该端⼝收发数据。如果想发送与接收绑定不同的地址,则需要在⼀个程序中声明两个socket,绑定不同的地址,⼀个socket⽤于收,⼀个socket⽤于发。1.1.2.3实现⼀对多通信(组播)IP组播通信需要⼀个特殊的组播地址,IP组播地址是⼀组D类IP地址,范围从224.0.0.0 到239.255.255.255。其中还有很多地址是为特殊的⽬的保留的。224.0.0.0到224.0.0.255的地址最好不要⽤,因为他们⼤多是为了特殊的⽬的保持的(⽐如IGMP协议)。 IGMP是IP组播的基础。在IP协议出现以后为了加⼊对组播的⽀持,IGMP产⽣了。IGMP所做的实际上就是告诉路由器,在这个路由器所在的⼦⽹内有⼈对发送到某⼀个组播组的数据感兴趣,这样当这个组播组的数据到达后⾯,路由器就不会抛弃它,⽽是把他转送给所有感兴趣的客户。假如不同⼦⽹内的A和B要进⾏组播通信,那么位于AB之间的所有路由器必须都要⽀持IGMP协议,否则AB之间不能进⾏通信。
1 使⽤Socket实现任意源组播 利⽤C#实现UDP组播的基本步骤为: (1)建⽴socket; (2)socket和端⼝绑定; (3)加⼊⼀个组播组; (4)通过sendto / recvfrom进⾏数据的收发; (5)关闭socket。 下⾯是简单的⽰例:(1) 发送⽰例:byte[] data = new byte[1024];string hostname = tName();
string welcome = hostname+ ":你好 ! ";data = es(welcome);//声明组播组IPIPAddress ip = ("226.1.1.2");Socket s = new Socket(etwork,, );//对socket进⾏设置ketOption(,astTimeToLive, 1);//确定组播地址,注意端⼝号要与接收端统⼀IPEndPoint ipep = new IPEndPoint(ip, 5000);while (true){ (2000); (data,, , ipep);}();
(2)接收⽰例:byte[] data = new byte[1024];int recv = 0;Socket s = new Socket(etwork,, );//绑定本机地址,注意端⼝号要与发送端多播端⼝号⼀致IPEndPoint ipep = new IPEndPoint(, 5000);(ipep);//加⼊到多播组ketOption(,bership, newMulticastOption(("226.1.1.2"), ));//定义任意地址⽤于返回发送⽅的地址IPEndPoint sender = new IPEndPoint(, 0);EndPoint Remote = (EndPoint)sender;while (true){//放回的是路由器的地址,⽽不是发送机器的地址,这是因为是由路由器转发的//信息 recv =eFrom(data, ref Remote); ine(ing(data, 0, recv));}();y();
1.2采⽤UdpClient实现UDP1.2.1简介(1)创建对象绑定指定的端⼝进⾏数据的接收与发送:UdpClient udpClient= new UdpClient(11000);使⽤随机的端⼝进⾏数据的接收与发送:UdpClient udpClient= new UdpClient(0);使⽤随机端⼝进⾏发送,每次发出去的信息端⼝号码都是不同的,这样接收⽅要把信息发回来,就没办法了。绑定指定终结点,进⾏数据的发送:UdpClient udpClient= new UdpClient(IPEndPoint);
(2)发送数据发送的时候创建⼀个结点,来指定接收⽅的地址Send(byte[],Int32,IpEndPoint)Send(byte[],Int32,String,Int32)Send(byte[],Int32)在使⽤第三个send⽅法时由于没有指定远端地址,需要在之前使⽤Connect⽅法来指定远端地址。
(3)接收信息byte[] Receive(IpEndPoint)该⽅法是⼀个阻塞⽅法,如果没有收到数据,会⼀直在该⽅法上等待。1.2.2程序⽰例1.1.2.1实现⼴播功能发送端:其实UDP⼴播就是向255.255.255.255发送数据,接收端只需绑定UDP⼴播的端⼝号即可。发送端,发送的地址,255.255.255.255:Port,即,ast:Port。
UdpClient UDPsend = new UdpClient(newIPEndPoint(, 0));//其实ast 就是 255.255.255.255IPEndPoint endpoint = newIPEndPoint(ast, 8080);byte[] buf = es("This is UDPbroadcast");(buf, , endpoint);
接收端:接收端仅需绑定发送端发送时指定的端⼝,便可以接收⼴播信息IPEndPoint LocalIp = new IPEndPoint(, 8080);//任意地址,⽤于Receive中的ref参数,返回发送端的地址。IPEndPoint Remote = new IPEndPoint(, 0);UdpClient udpClient = new UdpClient(LocalIp);//Receive⽅法返回byte数组。byte[] d = e(ref Remote);string s = ing(d, 0, );
1.1.2.2实现⼀对⼀通信发送端://定义接收端的地址IPEndPoint targetIp = newIPEndPoint(("192.168.2.119"), 8001);UdpClient udpClient= new UdpClient(0);byte[] data = es(“Hello”);//发送信息,第⼆个参数为发送的字节数(data, , targetIp);
接收端://定义本地地址,⽤于不断监听该地址收到的信息。该地址与发送端的发送地址//⼀致。IPEndPoint LocalIp = new IPEndPoint(,8001);//任意地址,⽤于Receive中的ref参数,返回发送端的地址。IPEndPoint Remote = new IPEndPoint(, 0);UdpClient udpClient = new UdpClient(LocalIp);//Receive⽅法返回byte数组。byte[] d = e(ref Remote);string s = ing(d, 0, );
1.1.2.3实现⼀对多通信(组播)IP组播通信需要⼀个特殊的组播地址,IP组播地址是⼀组D类IP地址,范围从224.0.0.0 到239.255.255.255。其中还有很多地址是为特殊的⽬的保留的。224.0.0.0到224.0.0.255的地址最好不要⽤,因为他们⼤多是为了特殊的⽬的保持的(⽐如IGMP协议)。因此如果设定的IP不符合D类IP规则,则程序报错。发送端://定义组播地址,注意⼀定要是D类⼴播IPEndPoint targetIp = new IPEndPoint(("226.1.1.2"),8001);UdpClient udpClient= new UdpClient(0);byte[] data = es(“Hello”);//发送信息,第⼆个参数为发送的字节数(data, , targetIp);
接收端://定义本地地址,注意端⼝⼀定与发送端发送时指定的端⼝⼀致IPEndPoint LocalIp = new IPEndPoint(,8001);//任意地址,⽤于Receive中的ref参数,返回发送端的地址。IPEndPoint Remote = new IPEndPoint(, 0);UdpClient udpClient = new UdpClient(LocalIp);//加⼊组播,与发送端定义的组播地址⼀致,必须符合D类IP地址,否则报错lticastGroup(("226.1.1.2"));//Receive⽅法返回byte数组。byte[] d = e(ref Remote);string s = ing(d, 0, );//离开该多播组lticastGroup(("226.1.1.2"));2、TCP通信2.1采⽤Socket实现TCP2.1.1同步通信2.1.1.1简介服务端:服务端的主要职责是处理各个客户端发送来的数据,因此在客户端的Socket编程中需要使⽤两个线程来循环处理客户端的请求,⼀个线程⽤于监听客户端的连接情况,⼀个线程⽤于监听客户端的消息发送。基本流程· 创建套接字· 绑定套接字的IP和端⼝号——Bind(EndPoint)· 将套接字处于监听状态等待客户端的连接请求——Listen(int) 监听端⼝,其中parameters表⽰最⼤监听数。· 阻塞⽅法,当请求到来后,接受请求并返回本次会话的套接字——Accept()接受客户端链接,并返回⼀个新的链接,⽤于处理同客户端的通信问题。· 使⽤返回的套接字和客户端通信——Send()/Receive() Send(byte[]) 简单发送数据。 Send(byte[],SocketFlag) 使⽤指定的SocketFlag发送数据。 Send(byte[],int, SocketFlag) 使⽤指定的SocketFlag发送指定长度数据。 Send(byte[], int, int,SocketFlag) 使⽤指定的SocketFlag,将指定字节数的数据发送到已连接的socket(从指定偏移量开始)。Receive(byte[]) 简单接受数据。 Receive(byte[],SocketFlag) 使⽤指定的SocketFlag接受数据。 Receive(byte[], int, SocketFlag) 使⽤指定的SocketFlag接受指定长度数据。 Receive (byte[], int,int, SocketFlag) 使⽤指定的SocketFlag,从绑定的套接字接收指定字节数的数据,并存到指定偏移量位置的缓冲区。· 返回,再次等待新的连接请求· 关闭套接字
客户端:客户端相对于服务端来说任务要轻许多,因为客户端仅仅需要和服务端通信即可,可是因为在和服务器通信的过程中,需要时刻保持连接通畅,因此同样需要两个线程来分别处理连接情况的监听和消息发送的监听。基本流程· 创建套接字保证与服务器的端⼝⼀致· 向服务器发出连接请求——Connect(EndPoint)连接远程服务器,如果没有相应的远程等待连接,直接返回错误。· 和服务器端进⾏通信——Send()/Receive()· 关闭套接字2.1.1.2程序⽰例2.1.1.2.1简单的单次通信服务端接收客户端信息并向客户端返回信息服务端:static void Main(){ try{ IPEndPoint ipe = new IPEndPoint(,8001); Socket s = newSocket(etwork, ,);//创建⼀个Socket类 (ipe);//绑定 (0);//开始监听 Socket temp = ();//等待客户端连接 byte[] recvBytes = new byte[1024]; int bytes = e(recvBytes);//从客户端接受信息,返回接收的数据长度 string recvStr = ing(recvBytes,0, bytes); ine("Server GetMessage:{0}", recvStr); string sendStr = "Ok!Client SendMessage Sucessful!"; byte[] bs = es(sendStr); (bs);//向客户端发送数据 (); (); } catch (ArgumentNullException e){ ine("ArgumentNullException:{0}", e);} catch (SocketException e){ ine("SocketException: {0}", e);} ine("Press Enter to Exit"); ne();}客户端:static void Main(){ try{ IPEndPoint ipe =new IPEndPoint (“192.168.2.119”),8001);//服务器地址 Socket c = new Socket(etwork,,);//创建⼀个Socket t(ipe);//连接到服务器 byte[] bs =es("hello!This is a socket test"); (bs);//向服务端发送信息 byte[] recvBytes= new byte[1024]; int bytes= e(recvBytes);//从服务器端接受返回信息 string recvStr +=ing(recvBytes, 0, bytes); ine("Client Get Message:{0}", recvStr);//显⽰服务器返回信息 (); } catch(ArgumentNullException e){ ine("ArgumentNullException:{0}", e); } catch(SocketException e){ ine("SocketException:{0}", e); } ine("Press Enter to Exit"); ne();}
2.1.1.2.1多次通信public class TCPServer
{
private byte[]result = new byte[1024];
// 最⼤的监听数量
privateint maxClientCount;
// IP地址
privatestring ip;
// 端⼝号
privateint port;
// 客户端列表
privateList
// IP终端
privateIPEndPoint ipEndPoint;
// 服务端Socket
privateSocket ServerSocket; // 当前客户端Socket
privateSocket ClientSocket;
public TCPServer(int port, int count)
{
=ng();
= port;
entCount=count;
Sockets = newList
oint = newIPEndPoint(, port);
//初始化服务端Socket
Socket = newSocket(etwork,,);
//端⼝绑定
(oint);
//设置监听数⽬
(maxClientCount);
}
// 定义⼀个Start⽅法将构造函数中的⽅法分离出来
public void Start()
{
//创建服务端线程,实现客户端连接请求的循环监听
var ServerThread = new Thread(ClientConnect);
//服务端线程开启
();
}
// 监听客户端链接
private void ListenClientConnect()
{
//设置循环标志位
while(true)
{ //获取连接到服务端的客户端
Socket = ();
//将获取到的客户端添加到客户端列表
(Socket);
//向客户端发送⼀条消息
ssage(("客户端{0}已成功连接到服务器",EndPoint));
//创建客户端消息线程,实现客户端消息的循环监听
var ReveiveThread = newThread(eClient);
//注意到ReceiveClient⽅法传⼊了⼀个参数
//实际上这个参数就是此时连接到服务器的客户端
//即ClientSocket
(Socket);
}
}
// 接收客户端消息的⽅法
private void ReceiveClient(objectobj)
{
Socket_ClientSocket = (Socket)obj;
while(true)
{
try
{
//获取数据长度
int receiveLength = _e(result);
//获取客户端消息
string clientMessage =ing(result, 0, receiveLength);
//服务端负责将客户端的消息分发给各个客户端
ssage(("客户端{0}发来消息:{1}",_EndPoint,clientMessage)); }
catch (Exception e)
{
//从客户端列表中移除该客户端
(_ClientSocket);
//向其它客户端告知该客户端下线 ssage(("服务器发来消息:客户端{0}从服务器断开,断开原因{1}",_EndPoint,e));
//断开连接
_wn();
_();
break;
}
}
}
// 向所有的客户端群发消息
public void SendMessage(string msg)
{
//确保消息⾮空以及客户端列表⾮空
if (msg == || <= 0) return;
//向每⼀个客户端发送消息
foreach (Socket s in Sockets)
{
(es(msg));
}
}
// 向指定的客户端发送消息
public void SendMessage(string ip,intport,string msg)
{
//构造出⼀个终端地址
IPEndPoint_IPEndPoint = new IPEndPoint((ip), port);
//遍历所有客户端
foreach (Socket s in ClientSockets)
{
if (_IPEndPoint ==(IPEndPoint)EndPoint)
{
(es(msg));
}
} }
}
2.1.2异步通信2.1.1.1简介(1)BeginAccept(AsynscCallBack,object)开始⼀个异步的接受⼀个连接尝试。object:通常传⼊的是服务Socket对象实例。AsynscCallBack:回调函数,该回调函数中必须包含EndAccept⽅法。应⽤程序调⽤BegineAccept⽅法后,系统会使⽤单独的线程执⾏指定的回调⽅法并在EndAccept上⼀直处于阻塞状态,直⾄监测到挂起的链接。EndAccept会返回新的socket对象。供你来同远程主机数据交互。调⽤BeginAccept当希望原始线程阻塞的时候,请调⽤e⽅法。当需要原始线程继续执⾏时请在回调⽅法中使⽤ManualResetEvent的set⽅法。(2)Socket EndAccept(IAsyncResult)重点是返回⼀个Socket与客户端进⾏通信。IAsyncResult作⽤不⼤。(3)BeginConnect(EndPoint,AsyncCallBack, Object)EndPoint:指定想要连接的服务端地址。AsynscCallBack:回调函数,该回调函数中必须包含EndConnect⽅法。object:通常传⼊的是客户端Socket对象实例。(4)void EndConnect(IAsyncResult)⽆返回值,在为连接成功前⼀直在该⽅法上等待。(5)BeginReceive(byte[] buffer,int offset, int size, SocketFlags, AsyncCallbac, object)buffer:⽤于存放接收信息。offset:指定buffer的其实存储位置。size:指定接收的信息byte长度。SocketFlags:socket标志位。AsynscCallBack:回调函数,该回调函数中必须包含EndReceive⽅法。object:通常是⼀个存放了第⼀个参数buffer以及与远端进⾏通信的Socket对象的数据结构的对象实例。(6)int EndReceieve(IAsyncResult)返回收到的信息的byte数组长度。IAsyncResult作⽤不⼤。(7)BeginSend(byte[],SocketFlag, AsyncCallBack, Object)byte[]:需要发送的数据。AsyncCallBack:回调函数,该回调函数中必须包含EndSend⽅法。object:通常为与远端进⾏通信的Socket实例。(8)int EndSend(IAsyncResult)返回发送的信息的byte数组长度。IAsyncResult作⽤不⼤。 服务端:基本流程· 创建套接字· 绑定套接字的IP和端⼝号——Bind()· 使套接字处于监听状态等待客户端的连接请求——Listen()· 当请求到来后,使⽤BeginAccept()和EndAccept()⽅法接受请求,返回新的套接字· 使⽤BeginSend()/EndSend和BeginReceive()/EndReceive()两组⽅法与客户端进⾏收发通信· 关闭套接字
客户端:基本流程· 创建套接字并保证与服务器的端⼝⼀致· 使⽤BeginConnect()和EndConnect()这组⽅法向服务端发送连接请求· 使⽤BeginSend()/EndSend和BeginReceive()/EndReceive()两组⽅法与服务端进⾏收发通信· 关闭套接字
2.1.1.2程序⽰例2.1.1.2.1服务端仅⽤来不断接收客户端信息classStateObject{ public Socket workSocket = null; public const int BufferSize = 1024; public byte[] buffer = new byte[BufferSize];}
classAsyncServer{ private IPEndPoint ipEndPoint; private Socket serverSocket; private Socket clientSocket; private List
public AsyncServer() { clients = new List
public void ListenThread() { while (true) { (); ccept(newAsyncCallback(AcceptCallback), serverSocket); e(); } }
public void AcceptCallback(IAsyncResult ar) { Socket temp =((Socket)tate).EndAccept(ar); StateObject state = new StateObject(); cket = temp; eceive(, 0, Size,0, new AsyncCallback(ReceiveCallback), state); (); }
public void ReceiveCallback(IAsyncResult ar) { StateObject state =(StateObject)tate; Socket handler = cket; int i = eive(ar); if (i > 0) { string message =ing(, 0, i); ine(message); } }}
2.1.1.2.2服务端与客户端双向通信 (该代码引⽤⾃他⼈)异步通信:客户端Client:预定义结构体,⽤于异步委托之间的传递。⽤户根据⾃⼰需要定制即可public class StateObject{ public Socket workSocket= null; public const int BufferSize= 256; public byte[] buffer= new byte[BufferSize]; public StringBuilder sb = new StringBuilder();}正⽂:public class AsynchronousClient{ private const int port =11000; private static ManualResetEvent connectDone= new ManualResetEvent(false); private static ManualResetEvent sendDone= new ManualResetEvent(false); private static ManualResetEvent receiveDone= new ManualResetEvent(false); private static String response= ;
private static void StartClient(){ try{ IPHostEntry ipHostInfo= e(""); IPAddress ipAddress= sList[0]; IPEndPoint remoteEP= new IPEndPoint(ipAddress,port); Socket client= new Socket(etwork, , );
onnect(remoteEP,new AsyncCallback(ConnectCallback), client); e(); Send(client, "This is atest
Receive(client); e(); ine("Responsereceived : {0}", response); wn(); ();} catch (Exception e){ ine(ng());} }
private static void ConnectCallback(IAsyncResult ar) { try{ Socket client = (Socket)tate; nect(ar); ine("Socketconnected to {0}", ng()); (); } catch (Exception e){ ine(ng());} }
private static void Receive(Socket client) { try{ StateObject state= new StateObject(); cket= client; eceive(,0, Size, 0, new AsyncCallback(ReceiveCallback),state); } catch (Exception e){ ine(ng());} }
private static void ReceiveCallback(IAsyncResult ar) { try{ StateObject state = (StateObject)tate; Socket client =cket; int bytesRead= eive(ar); if (bytesRead> 0){ (ing(,0, bytesRead)); eceive(,0, Size, 0, new AsyncCallback(ReceiveCallback),state); } else{ if (> 1) { response= ng(); } (); } } catch (Exception e){ ine(ng());} }
private static void Send(Socket client, String data) { byte[] byteData= es(data); end(byteData,0, , 0, new AsyncCallback(SendCallback),client); }
private static void SendCallback(IAsyncResult ar) { try{ Socket client = (Socket)tate; int bytesSent= d(ar); ine("Sent {0}bytes to server.", bytesSent); ();} catch (Exception e){ ine(ng());} }
public static int Main(String[] args) { StartClient(); return 0; }}服务器端Server:预定义结构体,⽤于异步委托之间的传递。同客户端的⼀致。不再赘述正⽂:public class AsynchronousSocketListener{ public static ManualResetEvent allDone= new ManualResetEvent(false); public AsynchronousSocketListener(){} public static void StartListening() { byte[] bytes = new Byte[1024]; IPHostEntry ipHostInfo= e("127.0.0.1"); IPAddress ipAddress= sList[0]; IPEndPoint localEndPoint= new IPEndPoint(ipAddress,11000); Socket listener= new Socket(etwork,,); try{ (localEndPoint); (100); while (true){ (); ine("Waitingfor "); ccept(new AsyncCallback(AcceptCallback),listener); e(); } } catch (Exception e){ ine(ng());} ine("nPressENTER "); (); } public static void AcceptCallback(IAsyncResult ar) { Socket listener =(Socket)tate; Socket handler =ept(ar); StateObject state= new StateObject(); cket= handler; eceive(,0, Size, 0,newAsyncCallback(ReadCallback),state); (); } public static void ReadCallback(IAsyncResult ar) { String content= ; StateObject state = (StateObject)tate; Socket handler =cket; int bytesRead= eive(ar); if (bytesRead> 0) { (ing(,0, bytesRead)); content= ng(); if (f("
2.2采⽤TCPListener、TCPClient实现TCP2.2.1同步通信2.2.1.1简介TCPListener介绍1、构造函数(1)TCPListener(IPEndPoint)(2)TCPListener(Int32)(3)TCPListener(IPAddress,Int32)这三种构造函数都是指定服务器本⾝监听的地址与端⼝。2、开启监听(1)Start()(2)Start(Int32)设置了最⼤挂起连接数,即最⼤的可以等待在Accept上的请求。3、接受客户端连接(1)Socket AcceptSocket () 阻塞⽅法。返回Socket与客户端进⾏通信(2)TcpClient AcceptTcpClient () 阻塞⽅法。返回TcpClient与客户端进⾏通信4、关闭监听器void Stop()TCPClient介绍1、构造函数(1)TCPClient()当使⽤这种不带任何参数的构造函数时,将使⽤本机默认的ip地址并将使⽤默认的通信端⼝号0。这样情况下,如果本机不⽌⼀个ip地址,将⽆法选择使⽤。(2)TCPClient(IPEndPoint)IPEndPoint⽤于指定在建⽴远程主机连接时所使⽤的本地⽹络接⼝(IP 地址)和端⼝号。使⽤上述两种构造函数,在与⽬标主机进⾏连接时,需要使⽤Connect⽅法进⾏连接。(3)TCPClient(String,Int32)连接到指定主机的,参数指定的是⽬标主句的地址,因此使⽤该⽅法后⾃动连接⽬标主机,⽆需Connect⽅法。2、连接到⽬标主句(1)void Connect(IPEndPoint)使⽤指定的远程⽹络终结点将客户端连接到远程 TCP 主机。(2)Connect(IPAddress, int)使⽤指定的 IP 地址和端⼝号将客户端连接到 TCP 主机。(3)Connect(string, int)将客户端连接到指定主机上的指定端⼝3、返回⽤于发送或接收数据的NetworkStreamNetworkStream GetStream ()4、使⽤NetworkStream进⾏数据的接收与发送(1)int Read (byte[] buffer, int offset, int size)buffer:⽤于存储从 NetworkStream 读取的数据。offset:buffer 中开始将数据存储到的位置。size:要从 NetworkStream 中读取的字节数。返回值:从 NetworkStream 中读取的字节数。(2)void Write (byte[] buffer, int offset, int size)buffer:存放要写⼊ NetworkStream 的数据。offset:buffer 中开始写⼊数据的位置。size:要写⼊ NetworkStream 的字节数。
2.2.1.2程序⽰例服务端:class Server{ private TcpListener tcpListener; private Thread listenThread; public Server() { tener = newTcpListener(, 3000); Thread = new Thread(newThreadStart(ListenForClients)); (); }
private voidListenForClients(){ (); while (true) { try{ TcpClient client =TcpClient(); Thread clientThread = new Thread(newParameterizedThreadStart(HandleClientComm)); (client);}catch(Exception e){break;}}();}
private voidHandleClientComm(object client){ TcpClient tcpClient = (TcpClient)client; NetworkStream clientStream =eam(); byte[] message = new byte[4096]; int bytesRead; while (true) { bytesRead = 0; try { bytesRead =(message, 0, 4096); } catch { break; } if (bytesRead == 0) { break; } strings = ing(message, 0, bytesRead); ine(s); byte[] SendMessage = es("收到信息"); (SendMessage,0, ); ();}(); ();}}
客户端:TcpClientclient = new TcpClient();IPEndPointserverEndPoint = new IPEndPoint(("127.0.0.1"), 3000);t(serverEndPoint);NetworkStreamclientStream = eam();byte[] buffer= es("Hello Server!");(buffer,0 , );();
byte[] message= new byte[4096];int bytesRead= (message, 0, 4096);string s =ing(message, 0, bytesRead);ine(s);();();
发布者:admin,转转请注明出处:http://www.yc00.com/web/1689407053a243015.html
评论列表(0条)