2/10/20201计算机网络编程技术第3章基于TCP协议的程序设计2/10/20202本章的主要内容回顾TCP协议的基本特点了解阻塞模式和非阻塞模式同步套接字编程技术异步套接字编程技术2/10/202033.1TCP协议介绍(1)TCP协议提供服务的特点:①面向连接的传输;②端到端的通信;③高可靠性,确保传输数据的正确性,不出现丢失或乱序;④全双工方式传输;⑤采用字节流方式,即以字节为单位传输字节序列;⑥紧急数据传送功能。2/10/20204(2)TCP数据包格式TCP头部数据源端口号目的端口号序号头部长度校验和(16位)窗口大小选项及填充确认号保留URGACKPSHRSTSYNFIN紧急指针0151631注意标志位的作用注意选项的作用,如何使用选项部分?2/10/20205(3)TCP协议的通信特点3次握手;4次挥手;回顾拥塞控制2/10/20206(4)TCP的熟知端口端口号服务进程描述20FTP文件传输协议(数据连接)21FTP文件传输协议(控制连接)23Telnet虚拟终端网络25SMPT简单邮件传输协议53DNS域名服务器80HTTP超文本传输协议111RPC远程过程调用2/10/202073.2阻塞模式/非阻塞模式同步异步阻塞模式:读、写、连接、接收非阻塞模式2/10/202083.2.1阻塞模式的效率提升方法(1)超时控制方法套接字选项设置:SetSockOption(),GetSockOption()应用示例:SocketsocketTimeout=newSocket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);IPEndPointmyHost=newIPEndPoint(IPAddress.Any,8080);socketTimeout.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.ReceiveTimeout,3000);定时器2/10/20209(2)套接字多路复用方法C#使用Socket类提供的Select方法。应用示例:ArrayListsocketList=newArrayList(5);socketList.Add(sock1);socketList.Add(sock2);Socket.Select(socketList,null,null,1000);Byte[]buffer=newtype[1024];for(inti=0;isocketList.Length-1;i++){socketList[i].Receive(buffer);Console.WriteLine(System.Text.Encoding.ASCII.GetString(buffer);}这样,Select方法将监视sock1和sock2两个套接字上接收的数据。2/10/202010(3)调用异步选择函数AsyncSelect()在VC++编程环境中使用;对于winsock2.0,采用:WSAAsyncSelect()函数2/10/2020113.2.2非阻塞模式的应用(1)非阻塞套接字Socketsock=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);sock.Blocking=false;(2)异步套接字采用异步回调AsyncCallback,委托为应用程序提供完成异步操作。2/10/2020123.3同步套接字编程技术常规的网络聊天程序,相互发送文字信息。服务器监听,网络接收数据,数据发送;客户机发出连接请求,数据发送与接收。2/10/202013服务器“开始监听”的主要代码this.btnStartListen.Enabled=false;IPAddressip=IPAddress.Parse(this.textBoxIP.Text);IPEndPointserver=newIPEndPoint(ip,Int32.Parse(this.textBoxPort.Text));socket=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);socket.Bind(server);//监听客户端连接socket.Listen(10);newSocket=socket.Accept();//显示客户IP和端口号this.lbState.Items.Add(与客户+newSocket.RemoteEndPoint.ToString()+建立连接);//创建一个线程接收客户信息thread=newThread(newThreadStart(AcceptMessage));thread.Start();2/10/202014服务器“数据接收”的主要代码NetworkStreamnetStream=newNetworkStream(newSocket);byte[]datasize=newbyte[4];netStream.Read(datasize,0,4);intsize=System.BitConverter.ToInt32(datasize,0);Byte[]message=newbyte[size];intdataleft=size;intstart=0;while(dataleft0){intrecv=netStream.Read(message,start,dataleft);start+=recv;dataleft-=recv;}接收数据的方法有2种:使用Socket类的Receive方法使用NetworkStream类的Read方法。Read方法的返回值是一个整数,表明实际从TCP缓冲区中读取的字节数,可能少于远端发来的数据量。2/10/202015服务器“数据发送”的主要代码发送数据也有2种方法:使用Socket类的Send方法,应用示例:byte[]bytes=newbyte[2048];stringmessage=测试数据发送;bytes=System.Text.Encoding.Unicode.GetBytes(message);newSocket.Send(bytes,bytes.Length,SocketFlags.None);使用NetworkStream类的Write方法,能够保证将用户数据全部发送到TCP缓冲区中,自动完成。不需要用户管理,简化了编程工作。stringstr=this.rtbSend.Rtf;inti=str.Length;byte[]datasize=newbyte[4];datasize=System.BitConverter.GetBytes(i);//将位整数值转换为字节数组byte[]sendbytes=System.Text.Encoding.Unicode.GetBytes(str);NetworkStreamnetStream=newNetworkStream(newSocket);netStream.Write(datasize,0,4);netStream.Write(sendbytes,0,sendbytes.Length);netStream.Flush();this.rtbSend.Rtf=;2/10/202016客户机“连接请求”的主要代码IPAddressip=IPAddress.Parse(this.tbIP.Text);IPEndPointserver=newIPEndPoint(ip,Int32.Parse(this.tbPort.Text));socket=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);try{socket.Connect(server);}catch{MessageBox.Show(与服务器连接失败!);return;}this.btnRequest.Enabled=false;this.lbState.Items.Add(与服务器连接成功);Threadthread=newThread(newThreadStart(AcceptMessage));thread.Start();2/10/2020173.4异步套接字编程技术配套的方法功能BeginAccept(),EndAccept()服务器接收一个连接请求BeginConnect(),EndConnect()客户端连接到服务器BeginReceive(),EndReceive()接收数据BeginReceiveFrom(),EndReceiveFrom()从指定的主机上接收数据BeginSend(),EndSend()发送数据BeginSendTo(),EndSendTo()将数据发送到指定的主机2/10/202018(1)客户机发出连接请求客户端使用BeginConnect方法发出连接请求给服务器:Socketsocket=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);IPEndPointiep=newIPEndPoint(IPAddress.Parse(127.0.0.1),8000);socket.BeginConnect(iep,newAsyncCallback(ConnectServer),socket);privatevoidConnectServer(IAsyncResultar){clientSocket=(Socket)ar.AsyncState;clientSocket.EndConnect(ar);clientSocket.BeginReceive(data,0,dataSize,SocketFlags.None,newAsyncCallback(ReceiveData),clientSocket);}2/10/202019(2)服务器接收连接请求privateSocketserverSocket,newSocket;IPHostEntrymyHost=newIPHostEntry();myHost=Dns.GetHostByName(NetHost);//主机名称NetHostIPAddressmyIP=IPAddress.Parse(myHost.AddressList[0].ToString());//选取第1个地址IPEndPointiep=newIPEndPoint(myIP,8000);serverSocket=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);serverSocket.Bind(iep);serverSocket.Listen(5);//监听队列为5//开始异步接收连接请求serverSocket.BeginAccept(newAsyncCallback(AcceptConnection),serverSocket);2/10/202020(3)服务器发送和接收数据一旦服务器接收到一个客户机连接请求,AsyncCallback委托将自动调用AcceptConnection方法。privatevoidAcceptConnection(IAsyncResultar){SocketmyServer=(Socket)ar.AsyncState;//异步接收传入的连接,并创建新的Socket来处理远程主机通信newSocket=myServer.EndAccept(ar);byte[]message=System.Text.En