《Winsock编程接口》PPT课件.ppt

上传人:tia****nde 文档编号:12708654 上传时间:2020-05-14 格式:PPT 页数:94 大小:1.36MB
返回 下载 相关 举报
《Winsock编程接口》PPT课件.ppt_第1页
第1页 / 共94页
《Winsock编程接口》PPT课件.ppt_第2页
第2页 / 共94页
《Winsock编程接口》PPT课件.ppt_第3页
第3页 / 共94页
点击查看更多>>
资源描述
,第二章Winsock编程接口,Windows套接字简介字节顺序和Winsock的寻址方式WinsockAPI基本函数数据报套接字编程流式套接字编程,1.Windows套接字简介,Winsock来源,Sockets本来是Unix操作系统上流行的一种网络编程接口(API)。API:ApplicationProgrammingInterface发展过程:1983年在Berkeley大学4.2BSDUNIX中首先使用,因此被称为BerkeleySocketAPI(只支持TCP/IP协议)。后来在4.3BSDUNIX中增加了对OSI网络协议的支持。应用平台:UNIX、LINUX产生Winsock:WinsockAPI在1991年根据4.3BSDUNIX的BerkeleySocket制定的。,Winsock规范,Winsock规范:一套开放的、支持多种协议的Windows下的网络编程接口。主要支持TCP/IP协议。Winsock规范主要包括Windows提供的API函数:符合BerkeleySocket风格的库函数;针对Windows的一组扩展库函数。发展过程:1991年的1.0版到1997年的2.2版(主要扩充了对其它协议的支持)。应用平台:Windows系列,Winsock实现库两个版本:Winsock1和Winsock2。现在大都使用Winsock2,Winsock2库的最新版本是2.2。,Winsock实现库,采用Winsock1的应用必须包含winsock.h头文件。使用Winsock2的应用必须包含winsock2.h头文件。,不同平台对应不同的库版本。,套接字基本概念,套接字可以看成是两个网络应用程序进行通信时,各自通信连接中的一个端点。,在编写网络应用程序时,应先申请套接字,以后两台机器上的程序通过该套接字进行通信。,在网络程序编程中,通过将IP、端口与一个套接字绑定,并指明通信协议,从而来实现应用程序与TCPIP协议的交互。,套接字通信模型,客户端,服务器,基于TCP/IP的网络,中间环节,套接字可以看成是两个网络应用程序进行通信时,各自通信连接中的一个端点。如下图:,套接字中的信息通过驱动程序送入网卡,然后经网络发送到远端服务器,网卡将接收到的信息通过驱动程序送入套接字,进程C,进程S,套接字(Socket)分类,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW),打电话,在TCP/IP协议组中,使用TCP协议来实现流式套接字。,发信,在TCP/IP协议组中,使用UDP协议来实现数据报套接字。,该套接字允许对底层协议(如IP或ICMP)进行直接访问,常用来测试新安装的网络设备,以及对流经网卡的数据进行捕获分析。,它定义了一种无连接、不可靠的双向数据传输服务。,它定义了一种可靠的、面向连接的双向数据传输服务。,2.字节顺序和Winsock的寻址方式,字节顺序含义:占内存多于一个字节类型的数据在内存中的存储顺序。通常有如下两种存储顺序:1Littleendian:将低序字节存储在起始地址(低序优先)2Bigendian:将高序字节存储在起始地址(高序优先),内存地址1000100110021003,低序优先存储顺序:0 x780 x560 x340 x12,高序优先存储顺序:0 x120 x340 x560 x78,然而处于主机上的数据却因不同的CPU上运行的操作系统的不同,字节顺序也不同,参见下表。,通常根据所处的位置不同,低序优先字节顺序和高序优先字节顺序我们又分别称为主机字节顺序和网络字节顺序。,处理器操作系统字节排序HP-PANT低序优先HP-PAUNIX高序优先Intelx86全部低序优先Motorola680 x()全部高序优先,因此,我们常说的主机字节顺序,就是低序优先,但主机上的字节顺序却不尽都是低序优先。,网络字节顺序是TCP/IP中规定的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。,不同字节顺序转换函数主机字节顺序hhost;网络字节顺序nnetworku_shorthtons(u_shorthostshort);把一个u_short型的主机字节顺序存储的数转换为网络字节顺序存储字节数转换u_longhtonl(u_longhostlong);把一个u_long型的主机字节顺序存储的数转换为网络字节顺序存储字节数转换u_shortntohs(u_shortnetshort);把一个u_short型的网络字节顺序存储的数转换为主机字节顺序存储字节数转换u_longntohl(u_longnetlong);把一个u_long型的网络字节顺序存储的数转换为主机字节顺序存储字节数转换,Winsock寻址Winsock要兼容多个协议,所以必须使用通用的寻址方式。TCP/IP(IP地址,端口)来指定一个地址,其它协议不一定如此。sockaddr结构,structsockaddru_shortsa_family;/2个字节共16位charsa_data14;/14个字节;/共16个字节,sa_family:AF_UNIXUnixinternalprotocolsAF_INETInternetprotocols常用本课程使用AF_NSXeroxNSprotocolsAF_IMPLINKIMPlinklayer其中,AF_代表“addressfamily”sa_data在不同的地址家族中存放的数据不同。,sockaddr_in结构(Winsock定义的sockaddr结构的TCP/IP版本)本质和sockaddr是相同的结构(大小一致)sockaddr_in更容易操作。,structsockaddr_inshortsin_family;/Addressfamily2字节u_shortsin_port;/16-bitportnumber2字节structin_addrsin_addr;/32-bit(IP)地址4字节charsin_zero8;/unused8字节;/共16个字节该结构与sockaddr兼容,供用户填入参数,sockaddr_in结构成员说明。,sin_family:必须设置为AF_INET,它告诉Winsock程序使用的是IP地址家族,sin_port:指定TCP或UDP通信服务的端口。0-1023:公共服务使用。如80为HTTP服务的端口1024-65535:可以由普通用户使用。,structin_addrsin_addr:IP地址。,sin_zero:为了和sockaddr保持一致。,in_addr结构在in_addr中采用共用体类型。,structin_addrunion/共用体structu_chars_b1,s_b2,s_b3,s_b4;S_un_b;/1+1+1+1=4字节structu_shorts_w1,s_w2;S_un_w;/2+2=4字节u_longS_addr;/4字节S_un;structin_addrsin_addr;/sockaddr_in结构中的IP地址,共用体:几个不同的变量共占一段内存的结构称为“共用体”。共用体变量所占的内存长度等于最长的成员的长度。这里共用体的三个变量长度一致。,点分十进制记法,11000000101010000000000100001100,192.168.1.12,0 xC0,0 xA8,0 x01,0 x0C,IP地址设置,对地址进行设置:注意要把多字节数转换成网络字节顺序假设有IP地址“192.168.1.12”4字节数据十进制表示对于structin_addrsin_addr可以有如下赋值方法:第一种方法(每个数都是单字节,不用转换)sin_addr.S_un.S_un_b.s_b1=192;sin_addr.S_un.S_un_b.s_b2=168;sin_addr.S_un.S_un_b.s_b3=1;sin_addr.S_un.S_un_b.s_b4=12;,第二种方法(转换成网络字节顺序即高位在前)192168112对应的16进制0 xC00 xA80 x010 x0C1921682字节16进制0 xC0A81122字节16进制0 x010Csin_addr.S_un.S_un_w.s_w1=htons(12*16*16*16+10*16+8);sin_addr.S_un.S_un_w.s_w2=htons(1*16*16+12);,第三种方法(转换成网络字节顺序即高位在前)192.168.1.124字节16进制0 xC0A8010Csin_addr.S_un.S_addr=htonl(12*pow(16,7)+10*pow(16,5)+8*pow(16,4)+1*pow(16,2)+12);,IP地址转换函数(点分十进制IP地址网络字节顺序IP地址),char*inet_ntoa(structin_addrin);把一个(Ipv4)32位网络字节顺序IP地址转换成”aa.bb.cc.dd”格式的字符串unsignedlonginet_addr(constchar*cp);把一个”aa.bb.cc.dd”格式的IP地址字符串转换为(Ipv4)32位网络字节顺序IP,因此可以有更简便的方法,也是最常用的方法sin_addr.S_un.S_addr=inet_addr(192.168.1.12);,structsockaddr_inshortsin_family;/Addressfamily2字节u_shortsin_port;/16-bitportnumber2字节structin_addrsin_addr;/32-bit(IP)地址4字节charsin_zero8;/unused8字节;/共16个字节,下面初始化一个sockaddr_in结构,sockaddr_inaddrSrv;addrSrv.sin_family=AF_INET;/把16位主机字节转换为网络字节addrSrv.sin_port=htons(6789);/把点分制转换为32位网络字节addrSrv.sin_addr.S_un.S_addr=inet_addr(127.0.0.1);,总结,套接字的基本概念套接字的通信模型Winsock的寻址方式主机字节顺序(低位在前)网络字节顺序(高位在前)两种顺序间的转换IP地址设置,3.WinsockAPI基本函数,WinsockAPI环境准备#include/包含了绝大部分socket函数和相关结构类型的声明#pragmacomment(lib,WS2_32)/链接到WS2_32.lib,打开WinsockWSAStartup()加载可用的Winsock实现库函数形式intWSAStartup(WORDwVersionRequested,/传入参数LPWSADATAlpWSAData/传出参数);/typedefunsignedshortWORD;/2个字节16位功能说明:检查系统中是否有可用的Winsock实现库,并加载该实现库。网络应用程序开始必须调用此函数。,参数说明:,WORDwVersionRequested含义:用来指定想要加载的Winsock库的版本。表示一个字(2个字节)。高位字节指定副版本,低位字节制定主版本。目前的Win32平台而言,Winsock2库的最新版本是2.2(Win95为Winsock1.1),参数设定:假设加载Winsock2.2版。指定此参数的值为0 x0202;wVersionRequested=0 x0202或使用宏MAKEWORD(x,y):通过宏MAKEWORD来指定参数的值。x是高位字节,y是低位字节MAKEWORD(2,2)指定版本为Winsock2.2版,typedefstructWSADataWORDwVersion;/调用者希望使用的版本号WORDwHighVersion;/库文件支持的最高版本charszDescriptionWSADESCRIPTION_LEN+1;/Winsock库描述字符串如Winsock2.0库charszSystemStatusWSASYS_STATUS_LEN+1;/系统状态字符串如:RunningunsignedshortiMaxSockets;/Winsock2库已废弃参数unsignedshortiMaxUdpDg;/Winsock2库已废弃参数char*lpVendorInfo;/Winsock2库已废弃参数WSADATA,*LPWSADATA;,LPWSADATAlpWSAData一个指向WSADATA结构的指针,用来返回加载库的详细信息。,WSADATA*lpWSAData;等价于LPWSADATAlpWSAData;,返回0则正确,否则返回错误。错误代码可以通过WSAGetLastError()来获取。下面只举几个例子说明。更多信息可以MSDN获取,也可到winsock.h或winsock2.h中查找。如:WSASYSNOTREADY:10091加载的Winsock库不存在WSAEFAULT:10014lpWSAData参数是无效的指针,返回值:,终止使用WinsockWSACleanup()函数形式:intWSACleanup();功能说明:该函数终止应用程序对Winsock库的使用,释放加载的Winsock库。每一个WSAStartup的调用必须对应一个WSACleanup的调用。返回值:调用成功返回0;否则SOCKET_ERROR被返回,具体错误可以通过函数WSAGetLastError()来获取。SOCKET_ERROR是系统宏定义值为-1。,#include/标准输入输出系统文件#include/winsock声明文件#pragmacomment(lib,WS2_32)/链接到WS2_32.libvoidmain()/通过载入Winsock库,来使用Winsock的相关函数WSADATAwsaData;/用来返回Winsock库的详细信息WORDversion=MAKEWORD(2,2);/wVersionRequested=0 x0202;intret=WSAStartup(version,加载释放举例:,WSADATA,/*进行版本号匹配的检查*/if(LOBYTE(wsaData.wVersion)!=2|/取低位字节主版本HIBYTE(wsaData.wVersion)!=2)/取高位字节幅版本printf(Winsock库版本不匹配n);WSACleanup();/释放库return;printf(“wVersion=%dn”,wsaData.wVersion);/希望使用版本printf(“wHighVersion=%dn”,wsaData.wHighVersion);/最高版本printf(“szDescription=%sn”,wsaData.szDescription);/库描述printf(“szSystemStatus=%sn”,wsaData.szSystemStatus);/系统状态WSACleanup();/释放库,wVersion=514wHighVersion=514szDescription=WinSock2.0szSystemStatus=Running,514说明版本号是2.20 x0202:十进制21616+2=514WinSock2.0指WinSock2库,WSADATA,创建套接字socket(),函数形式:SOCKETsocket(intaf,/说明套接字使用的协议地址族inttype,/描述套接字的协议类型intprotocol/说明套接字使用的特定协议字段);,为应用程序创建套接字,返回值SOCKET:系统提供的类型定义typedefu_intSOCKET;调用成功:返回新创建的套接字描述符(一个编号)。调用失败:返回INVALID_SOCKET(系统宏定义值0)。可以调用WSAGetLastError()来获取具体错误的代码。创建套接字后系统会为其分配相应的系统缓冲区。,举例说明:流式和数据报套接字中协议字段指定0时,取缺省的协议SOCKETsockSrv=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);SOCKETsockSrv=socket(AF_INET,SOCK_STREAM,0);/等同上一行SOCKETsockSrv=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);SOCKETsockSrv=socket(AF_INET,SOCK_DGRAM,0);/等同上一行SOCKETsockSrv=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);SOCKETsockSrv=socket(AF_INET,SOCK_RAW,IPPROTO_IP);,关闭套接字closesocket(),函数形式:intclosesocket(SOCKETs);,关闭一个不再使用的套接字。,调用成功:返回0;调用失败:返回SOCKET_ERROR(系统宏定义,值为-1)具体错误代码:调用WSAGetLastError()来获取。,绑定套接字到指定的Socket地址bind(),函数形式:intbind(SOCKETs,conststructsockaddr*name,/Socket地址intnamelen/指定地址参数(name)的长度);,把一个套接字与一个Socket地址联系起来。,调用成功返回0,否则将返回SOCKET_ERROR,SOCKETsListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);sockaddr_inaddrSrv;/地址名字/进行绑定bind(监听套接字,地址,地址长度);if(SOCKET_ERROR=bind(sListen,(SOCKADDR*),sockaddr_inaddrSrv;/地址名字addrSrv.sin_family=AF_INET;/协议地址族intPORT=4000;addrSrv.sin_port=htons(PORT);addrSrv.sin_addr.S_un.S_addr=inet_addr(127.0.0.1);,监听连接listen(),函数形式:intlisten(SOCKETs,intbacklog/指定正在等待连接的最大队列长度);,设置套接字进入监听连接请求的状态,以准备接收客户发出的连接请求。,调用成功返回0,否则将返回SOCKET_ERROR,/进入监听状态if(SOCKET_ERROR=listen(sListen,5)/5表示等待连接队列的长度printf(监听失败:%dn,WSAGetLastError();return;,请求连接connect(),函数形式:intconnect(SOCKETs,conststructsockaddr*name,intnamelen);,在客户端当一个套接字建立好之后,就要调用connect()函数,向服务器端提出连接请求。,调用成功返回0,否则将返回SOCKET_ERROR,使用该函数请求建立连接时,将激活建立连接的三次握手,用来建立一条与服务器的TCP连接。,/connect(客户端套接字,服务端地址,服务端地址长度);if(SOCKET_ERROR=connect(sockClient,(sockaddr*),接受连接accept(),函数形式:SOCKETaccept(SOCKETs,structsockaddr*addr,/存放发出连接请求的客户机的地址int*addrlen/指出客户机套接字地址的长度);,服务器端调用accept()函数来接受来自客户端的连接请求。,调用失败:返回INVALID_SOCKET;调用成功:返回一个新建套接字。新建套接字客户机套接字原来的监听套接字,仍然处于监听连接状态。,该函数只适用于TCP服务器。缺省是阻塞函数,若队列中无连接请求,则阻塞等待,直到有连接请求,才继续执行。,SOCKETSockConn;/声明和客户端连接的套接字SOCKADDR_INaddrClient;/来自客户端的地址intaddrlen=sizeof(addrClient);/初始化客户地址长度参数SockConn=accept(sListen,(SOCKADDR*),在已建立连接的套接字上发送数据send(),函数形式:intsend(SOCKETs,constchar*buf,/用户发送缓冲区指针intlen,/待发送缓冲区的字节数intflags/一般设置为0表示正常发送数据);,在已经建立连接的套接字上发送数据。,调用成功:返回发送的字节数。调用失败:返回SOCKET_ERROR,缺省是阻塞函数,在执行send时,若套接字的系统缓冲区没有可用空间,则程序一直阻塞等待,直到有可用的空间为止。,#defineBUFFERLEN1024/数据缓冲区大小charsendBufBUFFERLEN;/用户发送缓冲区sprintf(sendBuf,“欢迎访问我的服务!”);/strlen(buffer)+1把字符串的结束符0一块发送intsendlen=send(SockConn,sendBuf,strlen(sendBuf)+1,0);,在已建立连接的套接字上接收数据recv(),函数形式:intrecv(SOCKETs,char*buf,/用户接收缓冲区指针intlen,/接收缓冲区的字节数intflags/一般设置为0表示正常接收数据);,对于已经建立连接的套接字,recv可以从该套接字接收数据。,调用成功:返回接收的字节数。调用失败:返回SOCKET_ERROR,缺省是阻塞函数,在执行recv时,若套接字的系统缓冲区没有数据可取,则程序一直阻塞等待,直到接收到对方数据为止。,#defineBUFFERLEN1024/数据缓冲区大小charrecvBufBUFFERLEN;/用户接收缓冲区intrecvlen=recv(sockClient,recvBuf,sizeof(recvBuf),0);,在无连接的套接字上发送数据sendto(),函数形式:intsendto(SOCKETs,constchar*buf,/用户发送缓冲区指针intlen,/待发送缓冲区的字节数intflags,/一般设置为0conststructsockaddr*to,/发送数据的目的地址inttolen/目的地址长度);,对于无连接的套接字,使用sendto()从套接字上发送数据。,调用成功:返回发送的字节数。调用失败:返回SOCKET_ERROR,缺省是阻塞函数,在执行sendto时,如果套接字的系统缓冲区没有可用空间,则程序一直阻塞等待,直到有可用的空间为止。,#defineBUFFERLEN1024/数据缓冲区大小charsendBufBUFFERLEN;/用户发送缓冲区sprintf(sendBuf,我是张三!);sendlen=sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(sockaddr*),在无连接的套接字上接收数据recvfrom(),函数形式:intrecvfrom(SOCKETs,char*buf,/用户接收缓冲区指针intlen,/接收缓冲区的字节数intflags,/一般设置为0structsockaddr*from,/指明接收数据的来源地址int*fromlen/来源地址长度);,对于无连接的套接字,使用recvfrom()从套接字上接收一个数据报并保存发送源地址。,调用成功:返回接收的字节数。调用失败:返回SOCKET_ERROR,缺省是阻塞函数,在执行recvfrom时,若套接字的系统缓冲区没有数据可取,则程序一直阻塞等待,直到接收到对方数据为止。,#defineBUFFERLEN1024/数据缓冲区大小charrecvBufBUFFERLEN;/用户接收缓冲区SOCKADDR_INaddrClient;/来自客户的地址intaddrlen=sizeof(addrClient);/初始化客户地址长度参数/从客户端接收recvlen=recvfrom(sockSrv,recvBuf,sizeof(recvBuf),0,(sockaddr*),4.数据报套接字编程,用户数据报协议UDP-特点,UDP是无连接的,即发送数据之前不需要建立连接(当然发送数据结束时也没有连接可释放),因此减少了开销和发送数据之前的时延。UDP使用尽最大努力交付,即不保证可靠交付,同时也不使用拥塞控制,因此主机不需要维持具有许多参数的、复杂的连接状态表。UDP用户数据报只有8个字节的首部开销,比TCP的20个字节的首部要短。,由于UDP没有拥塞控制,因此网络出现的拥塞不会使源主机的发送速率降低。这对某些实时应用是很重要的。UDP是面向报文的。这就是说,UDP对应用程序交下来的报文不再划分为若干个分组来发送,也不把收到的若干个报文合并后再交付给应用程序。应用程序交给UDP一个报文,UDP就发送这个报文;而UDP收到一个报文,就把它交付给应用程序。应用程序必须选择合适大小的报文。,用户数据报协议UDP-特点,伪首部,源端口,目的端口,长度,检验和,数据,首部,UDP长度,源IP地址,目的IP地址,0,17,IP数据报,字节,4,4,1,1,2,12,2,2,2,2,字节,发送在前,数据,首部,UDP用户数据报,UDP用户数据报的首部格式,伪首部,源端口,目的端口,长度,检验和,数据,首部,UDP长度,源IP地址,目的IP地址,0,17,IP数据报,字节,4,4,1,1,2,12,2,2,2,2,字节,发送在前,数据,首部,UDP用户数据报,在计算检验和时,临时把“伪首部”和UDP用户数据报连接在一起。伪首部仅仅是为了计算检验和。,数据报套接字,在TCP/IP协议组中,使用UDP协议来实现数据报套接字。,它定义了一种无连接、不可靠的双向数据传输服务。,TCP/IP协议架构,TCP,UDP,传输层,应用层,网络层,网络接口层,IP地址定义了某个主机。端口号标识了在此特定主机上的多个进程中的一个。,UDP,UDP,Internet,数据报套接字通信,bind(),recvfrom(),sendto(),closesocket(),socket(),sendto(),recefrom(),closesocket(),socket(),服务器,客户端,创建一个新的套接字,创建一个新的套接字,将一个本地地址关联到一个套接字上,阻塞等待接收客户数据,发送数据,发送数据,接收数据,关闭套接字,关闭套接字,数据报套接字工作流程(服务器先启动),使用socket()函数创建套接字。使用bind()函数将创建的套接字与本地地址绑定。收发数据(sendto()函数recvfrom()函数)使用closesocket()关闭套接字,客户端工作流程:使用socket()函数创建套接字。收发数据(sendto()函数recefrom()函数)使用closesocket()关闭套接字,服务器端工作流程:,数据报套接字工作流程总结,/UdpServer.cpp#include/标准输入输出系统文件#include/winsock声明文件#pragmacomment(lib,WS2_32)/链接到WS2_32.lib#definePORT6000/此服务器监听的端口号#defineBUFFERLEN1024/数据缓冲区大小voidmain()/通过载入Winsock库,来使用Winsock的相关函数WSADATAwsaData;/用来返回Winsock库的详细信息WORDversion=MAKEWORD(2,2);intret=WSAStartup(version,服务器程序:,数据报套接字程序的编写,WSADATA,SOCKETsockSrv;/服务器端套接字/socket(协议地址族,套接字类型,协议);sockSrv=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);if(INVALID_SOCKET=sockSrv)printf(创建套接字失败:%dn,WSAGetLastError();return;sockaddr_inaddrSrv;/设置服务器端地址addrSrv.sin_family=AF_INET;/Internet协议地址组addrSrv.sin_port=htons(PORT);/把16位端口转换为网络字节addrSrv.sin_addr.S_un.S_addr=inet_addr(“192.168.0.1);/指定IP/绑定套接字到服务器端地址/bind(服务器端套接字,服务器端地址,服务器端地址长度);if(SOCKET_ERROR=bind(sockSrv,(sockaddr*),sockaddr_in,SOCKADDR_INaddrClient;/声明来自客户的地址,以备填充intaddrlen=sizeof(addrClient);/初始化客户地址长度参数charsendBufBUFFERLEN;/用户发送缓冲区charrecvBufBUFFERLEN;/用户接收缓冲区intsendlen;intrecvlen;/发送接收数据长度while(true)/进入循环/从客户端接收/recvfrom(服务器端套接字,接收缓冲区,缓冲区大小,接收标志,接收数据的来源地址,来源地址长度)recvlen=recvfrom(sockSrv,recvBuf,sizeof(recvBuf),0,(sockaddr*),/向客户端发送sprintf(sendBuf,“欢迎IP%s;端口%d访问我的服务!”,inet_ntoa(addrClient.sin_addr),ntohs(addrClient.sin_port);/sendto(服务器端套接字,发送缓冲区,发送数据长度,发送标志,发送数据的目的地址,目的地址长度);/strlen(sendBuf)+1指把结束符0一块发送sendlen=sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(sockaddr*)/释放Winsock库,/UdpClient.cpp#include/标准输入输出系统文件#include/winsock声明文件#pragmacomment(lib,WS2_32)/链接到WS2_32.lib#definePORT6000/此服务器监听的端口号#defineBUFFERLEN1024/数据缓冲区大小voidmain()/通过载入Winsock库,来使用Winsock的相关函数WSADATAwsaData;/用来返回Winsock库的详细信息WORDversion=MAKEWORD(2,2);intret=WSAStartup(version,客户端程序:,SOCKETsockClient;/声明客户端套接字/socket(协议地址族,套接字类型,协议);sockClient=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);if(INVALID_SOCKET=sockClient)printf(创建套接字失败:%dn,WSAGetLastError();return;/填写远程地址信息sockaddr_inaddrSrv;/声明服务器端地址addrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(PORT);/把16位端口号转换为网络字节addrSrv.sin_addr.S_un.S_addr=inet_addr(192.168.0.1);intaddrlen=sizeof(addrSrv);/初始化服务器端地址长度参数,charsendBufBUFFERLEN;/用户发送缓冲区charrecvBufBUFFERLEN;/用户接收缓冲区intsendlen;/发送数据长度intrecvlen;/接收数据长度/向服务器发送数据sprintf(sendBuf,Iamaclient!);/sendto(客户端套接字,发送缓冲区,发送数据长度,发送标志,发送数据的目的地址,目的地址长度);sendlen=sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(sockaddr*),/从服务器读取数据/recvfrom(客户端套接字,接收缓冲区,缓冲区大小,接收标志,接收数据的来源地址,来源地址长度)recvlen=recvfrom(sockClient,recvBuf,sizeof(recvBuf),0,(sockaddr*)/释放Winsock库,UDP服务器正在运行.,服务器端程序运行结果:,发送到服务器的数据:Iamaclient!;字节数:15接收到服务器端的数据:欢迎IP127.0.0.1;端口1066访问我的服务!;字节数:42,客户端程序运行结果:,发送到服务器的数据:Iamaclient!;字节数:15接收到服务器端的数据:欢迎IP127.0.0.1;端口1067访问我的服务!;字节数:42,客户端的IP:127.0.0.1,端口:1066接收到客户端的数据:Iamaclient!;字节数:15发送到客户端的数据:欢迎IP127.0.0.1;端口1066访问我的服务!;字节数:42,客户端的IP:127.0.0.1,端口:1067接收到客户端的数据:Iamaclient!;字节数:15发送到客户端的数据:欢迎IP127.0.0.1;端口1067访问我的服务!;字节数:42,5.流式套接字编程,TCP报文段的首部格式,TCP报文段分为首部和数据两部分。TCP的全部功能都体现在它首部中各字段的作用。TCP报文段首部的前20个字节是固定的,后面有4N字节是根据需要而增加的选项(N必须是整数)。因此TCP首部的最小长度是20字节。,TCP首部,20字节的固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,32位,SYN,RST,PSH,ACK,URG,位08162431,填充,TCP数据部分,TCP首部,TCP报文段,IP数据部分,IP首部,发送在前,TCP首部,20字节固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,SYN,RST,PSH,ACK,URG,位08162431,填充,源端口和目的端口字段各占2字节。端口是运输层与应用层的服务接口。,TCP首部,20字节固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,SYN,RST,PSH,ACK,URG,位08162431,填充,序号字段占4字节。TCP连接中传送的数据流中的每一个字节都编上一个序号。序号字段的值则指的是本报文段所发送的数据的第一个字节的序号。,TCP首部,20字节固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,SYN,RST,PSH,ACK,URG,位08162431,填充,确认号字段占4字节,是期望收到对方的下一个报文段的数据的第一个字节的序号。,TCP首部,20字节固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,SYN,RST,PSH,ACK,URG,位08162431,填充,数据偏移占4位,它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远(TCP报文段首部的长度)。“数据偏移”的单位不是字节而是32位字(4字节为计算单位)。,TCP首部,20字节固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,SYN,RST,PSH,ACK,URG,位08162431,填充,保留字段占6位,保留为今后使用,但目前应置为0。,TCP首部,20字节固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,SYN,RST,PSH,ACK,URG,位08162431,填充,紧急位URG当URG1时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据)。,TCP首部,20字节固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,SYN,RST,PSH,ACK,URG,位08162431,填充,确认位ACK只有当ACK1时确认号字段才有效。当ACK0时,确认号无效。,TCP首部,20字节固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,SYN,RST,PSH,ACK,URG,位08162431,填充,推送位PSH(PuSH)接收TCP收到PSH=1的报文段,就尽快地交付给接收应用进程,而不再等到整个缓存都填满了后再向上交付。,TCP首部,20字节固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,SYN,RST,PSH,ACK,URG,位08162431,填充,复位位RST(ReSeT)当RST1时,表明TCP连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。,TCP首部,20字节固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,SYN,RST,PSH,ACK,URG,位08162431,填充,同步位SYN当SYN=1时,表示这是一个连接请求或连接接受报文。,TCP首部,20字节固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,SYN,RST,PSH,ACK,URG,位08162431,填充,终止位FIN(FINal)用来释放连接。当FIN1时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。,TCP首部,20字节固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,SYN,RST,PSH,ACK,URG,位08162431,填充,窗口字段占2字节。窗口字段用来控制对方发送的数据量,单位为字节。TCP连接的一端根据设置的缓存空间大小确定自己的接收窗口大小,然后通知对方以确定对方的发送窗口的上限。,TCP首部,20字节固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,SYN,RST,PSH,ACK,URG,位08162431,填充,检验和占2字节。检验和字段检验的范围包括首部和数据这两部分。在计算检验和时,要在TCP报文段的前面加上12字节的伪首部。,TCP首部,20字节固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,SYN,RST,PSH,ACK,URG,位08162431,填充,紧急指针字段占16位。紧急指针指出在本报文段中的紧急数据的下一个字节的序号。紧急数据的长度为一个字节。,TCP首部,20字节固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,SYN,RST,PSH,ACK,URG,位08162431,填充,选项字段长度可变。TCP只规定了一种选项,即最大报文段长度MSS(MaximumSegmentSize)。MSS告诉对方TCP:“我的缓存所能接收的报文段的数据字段的最大长度是MSS个字节。”,TCP首部,20字节固定首部,目的端口,数据偏移,检验和,选项(长度可变),源端口,序号,紧急指针,窗口,确认号,保留,FIN,SYN,RST,PSH,ACK,URG,位08162431,填充,填充字段这是为了使整个首部长度是4字节的整数倍。,TCP可靠通信的具体实现很复杂,TCP的报文段的长度是不确定的。TCP够提供全双工通信,可以在发送自己的数据报文段的同时,捎带地把确认信息附上。为了提高通信传输效率,发送数据报文段的一方,可以连续发送多个数据报文段,而不需要在收到一个确认后才发送下一个报文段。要使用特定的算法来估算比较合适的重传时间。,TCP的数据编号与确认,TCP协议是面向字节的。TCP将所要传送的报文看成是字节组成的数据流,并使每一个字节对应于一个序号。在连接建立时,双方要商定初始序号。TCP每次发送的报文段的首部中的序号字段数值表示该报文段中的数据部分的第一个字节的序号。TCP的确认是对接收到的数据的最高序号表示确认。接收方返回的确认号是已收到的数据的最高序号加1。因此确认号表示接收方期望下次收到的数据中的第一个数据字节的序号。,TCP的运输连接管理,运输连接有三个阶段,即:连接建立、数据传送和连接释放。运输连接的管理就是使运输连接的建立和释放都能正常地进行。TCP的连接和建立都是采用客户服务器方式。主动发起连接建立的应用进程叫做客户(client)。被动等待连接建立的应用进程叫做服务器(server)。,用三次握手建立TCP连接,主机B,被动打开,主动打开,B发送确认,A发送确认,主机A,连接请求,连接建立状态,三次握手或三次联络,防止已失效的连接请求报文段又传送到B,因而产生错误。A发出连接请求,但因未收到确认而再重传一次。后来收到了确认,建立了连接。数据传输完毕后释放了连接。A共发送了两个连接请求报文段,其中的第二个到达了B。A发出的第一个连接请求报文段以后又传送到B。B误认为是A又发出一次新的连接请求。于是就向A发出确认报文段,同意建立连接。A不会理睬B的确认。但B却以为运输连接就这样建立了,并一直等待A发来数据。B的许多资源就这样白白浪费了。,TCP的连接释放过程,应用进程释放连接A不再发送报文,主机B,主机A,发送确认,确认,至此,整个连接已经全部释放。,半关闭状态,关闭状态,从A到B的连接就释放了,连接处于半关闭状态。相当于A向B说:“我已经没有数据要发送了。但你如果还发送数据,我仍接收。”,在TCP/IP协议组中,使用?协议实现流式套接字。,流式套接字定义了一种可靠的、面向连接的双向数据传输服务,实现了无差错、无重复的顺序数据传输。,TCP/IP协议架构,TCP,UDP,传输层,应用层,网络层,网络接口层,TCP,流式套接字工作原理,电话服务系统与面向连接的流式套接字的工作原理非常类似,114服务台,Internet,创建一个监听套接字sListen,将一个本地地址关联到监听套接字上,宣布愿意接受连接;给出队列大小,发送/接收数据,释放连接,阻塞,直到有人企图连接上来,创建一个新的套接字,主动尝试建立一个连接,发送/接收数据,释放连接,socket()监听套接字sListen,bind(),listen(),accept(),socket(),send()/recv(),closesocket(sNew),connect(),send()/recv(),closesocket(),服务器,客户端,建立连接,从accept返回已连接套接字sNew,closesocket(sListen),连接成功准备通信,关闭监听套接字,服务结束,工作流程模型图,服务器,IP+端口,客户3,客户2,2.bind(),3.listen(),1.socket(),1.socket(),2.connect(),4.accept(),3,2,1,1,工作流程动态演示,客户1,使用socket()函数创建套接字(监听套接字)使用bind()函数将创建的套接字与本地地址绑定使用listen()函数使服务器套接字做好接收连接请求的准备使用accept()函数接受来自客户端由connect()发出的请求连接建立后,使用accept()函数返回的已连接套接字与客户端的套接字进行收发数据(send()函数recv()函数)使用closesoc
展开阅读全文
相关资源
相关搜索

当前位置:首页 > 图纸专区 > 课件教案


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!