【教学课件】第5章网络软件开发技术-编程篇.ppt

上传人:小飞机 文档编号:5659037 上传时间:2023-08-06 格式:PPT 页数:71 大小:296KB
返回 下载 相关 举报
【教学课件】第5章网络软件开发技术-编程篇.ppt_第1页
第1页 / 共71页
【教学课件】第5章网络软件开发技术-编程篇.ppt_第2页
第2页 / 共71页
【教学课件】第5章网络软件开发技术-编程篇.ppt_第3页
第3页 / 共71页
【教学课件】第5章网络软件开发技术-编程篇.ppt_第4页
第4页 / 共71页
【教学课件】第5章网络软件开发技术-编程篇.ppt_第5页
第5页 / 共71页
点击查看更多>>
资源描述

《【教学课件】第5章网络软件开发技术-编程篇.ppt》由会员分享,可在线阅读,更多相关《【教学课件】第5章网络软件开发技术-编程篇.ppt(71页珍藏版)》请在三一办公上搜索。

1、8/6/2023,1,第5章 网络软件开发技术 编程篇,TCP socket&UDP socket西安交通大学计算机教学实验中心,软件开发技术基础,8/6/2023,2,Socket 简介,Socket是TCP/IP协议族提供的应用编程接口。应用层的应用系统通过调用Socket的接口来利用传输层提供的各种服务,包括可靠的流协议TCP和不可靠的数据报协议UDP。,应用程序,传输层协议,Socket,8/6/2023,3,Socket 简介,1982-Berkeley Software Distributions 操作系统引入了sockets 作为本地进程之间通信的接口1986-Berkeley

2、扩展了socket 接口使之支持UNIX 下的TCP/IP 通信现在很多应用(FTP,Telnet,etc)都依赖这一接口,8/6/2023,4,Socket 简介,Socket是一个编程接口是一种特殊的文件描述符(everything in Unix is a file)并不仅限于TCP/IP通信协议面向连接(Transmission Control Protocol-TCP/IP)无连接(User Datagram Protocol-UDP 和 Inter-network Packet Exchange-IPX),8/6/2023,5,WinSock,从 Berkeley Sockets(

3、Unix)移植包括了许多对windows环境的扩展支持开放的网络编程接口API 开放多个厂商提供 winsock源码和二进制兼容性最初的Winsock版本是1.1版,在它的基础上,微软又进一步提供了Winsock2接口。Winsock2支持多种底层的网络协议,如TCP/IP、ATM、IPX等,8/6/2023,6,WinSock.dll,FTP WinSock.dll,TCP/IP,IPX,AppleTalk,NetBIOS,Remote Access Service(RAS),FTP TCP/IP,Modem,Network Drivers,LAN,Application,Windows S

4、ocket,协议 和应用,Phone Line,8/6/2023,7,Berkeley socket 和 WinSock的不同,Berkeley Socket 是一个 int 数据类型,WinSock Socket 则是 SOCKET 数据类型WinSock 中以SOCKET_ERROR 代表出错,Berkeley Socket 以-1 代表出错WinSock 应用必须首先调用 WSAStartup()初始化,并在结束前调用 WSACleanup()释放资源,8/6/2023,8,void main(void)/*The following two lines needed for Windo

5、ws socket*/WORD wVersionRequested=MAKEWORD(2,2);/*WSA 函数的参数*/WSADATA wsaData;/*WSA 函数的参数*/,8/6/2023,9,Winsock DLL,在装入Winsock DLL 之前调用任何 Winsock 函数都会返回 SOCKET-ERROR 错误.调用 WSAStartup装入Winsock DLL int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);wVersionRequested Winsock DLL versionX(high ord

6、er):sub-versionY(low order):main versionMAKEWORD(X,Y)宏定义将两个字节组装成一个WORD,8/6/2023,10,在调用“closesocket”函数之后程序结束之前,释放socket资源,/*This stuff cleans-up winsock*/WSACleanup();,typedef struct WSAData WORD wVersion;WORD wHighVersion;char szDescriptionWSADESCRIPTION_LEN+1;char szSystemStatusWSASYS_STATUS_LEN+1;

7、unsigned short iMaxSockets;unsigned short iMaxUdpDg;char FAR*lpVendorInfo;WSADATA,*LPWSADATA;,8/6/2023,11,SOCKET socket_id=socket(AF_INET,SOCK_STREAM,0);,8/6/2023,12,Basic Socket Calls(socket),/Berkeley 形式int socket(int family int type,int protocol);/WinSock 形式SOCKET socket(int family,int type,int p

8、rotocol);,8/6/2023,13,socket(续),SOCKET socket(int family,int type,int protocol);family 是地址族AF_INET/internet 协议AF_UNIX/unix internal协议AF_NS/Xerox NS协议AF_IMPLINK/Interface Message协议typeSOCK_STREAM/流式 socketSOCK_DGRAM/数据报 socketSOCK_RAW/raw socketprotocol 参数通常置为0,8/6/2023,14,3.TCP Sockets 编程,创建一个被动模式(s

9、erver,服务器)的 socket.建立应用层的连接Client/Server 交互在发送和接收数据之前client 必须调用 connect 连接服务器server 必须调用 accept 接收client的连接发送和接收数据.关闭连接.,8/6/2023,15,TCP Sockets 编程基本流程,socket(),bind(),listen(),accept(),send(),recv(),close(),send(),socket(),recv(),close(),connect(),recv(),建立连接,数据请求,数据响应,断连指示,Client,Server,8/6/2023,

10、16,socket(),bind(),listen(),accept(),close(),调用socket创建一个套接字,并在传输层实体中分配表空间,返回一个文件描述符,用于以后调用中使用。,调用bind将某地址赋予,使得远程应用程序能访问本地应用程序。,调用listen分配数据空间,以便存储多个用户的连接建立请求。,调用accept将本地应用程序阻塞起来,等待接收客户端发来的连接请求。,释放连接:使用close原语单独释放连接。,服务器端,8/6/2023,17,socket(),close(),connect(),调用socket创建一个套接字,并在传输层实体中分配表空间,返回一个文件描述

11、符,用于以后调用中使用。,调用connect阻塞应用程序,传输层实体开始建立连接,当连接建立完成时,取消阻塞。,释放连接:使用close原语单独释放连接。,客户端,8/6/2023,18,send(),send(),recv(),recv(),数据请求,数据响应,双方使用send和receive完成数据的全双工发送,数据传输,8/6/2023,19,int status=bind(socket_id,(struct sockaddr_in*)my_addr,sizeof(my_addr);,8/6/2023,20,Basic Socket Calls(bind),/Berkeley 形式int

12、 bind(int sockfd,struct sockaddr*addr,int addrLen);/WinSock 形式int bind(SOCKET sockfd,struct sockaddr*addr,int addrLen);,8/6/2023,21,bind(续),int bind(SOCKET sockfd,struct sockaddr*addr,int addrLen);sockfd 由socket()调用返回addr 是指向 sockaddr_in 结构的指针,包含server IP 地址和端口号struct sockaddr_inshort sin_family/add

13、ress familyu_short sin_port/port numberstruct in_addr sin_addr/IP address(32-bits)addrLen-sizeof(struct sockaddr_in),8/6/2023,22,struct sockaddr_in my_addr;/*My(client)Internet address*/,8/6/2023,23,地址结构,通用地址结构struct sockaddr u_short sa_family;/地址族,AF_xxx char sa_data14;/14字节协议地址;Internet协议地址结构struc

14、t sockaddr_in short sin_family;/地址族,AF_INET,2 bytes u_short sin_port;/端口,2 bytes struct in_addr sin_addr;/IPV4地址,4 bytes char sin_zero8;/8 bytes unused;IPv4地址结构struct in_addr/internet address u_long s_addr;/socket address;,8/6/2023,24,unsigned long inet_addr(char*address);address是以NULL结尾的点分IPv4字符串。该

15、函数返回32位的地址,如果cp字符串包含的不是合法的IP地址,则函数返回。例:in_addr addr;addr.s_addr=inet_addr(202.117.50.26);char*inet_ntoa(struct in_addr address)address是IPv4地址结构,函数返回一指向包含点分IP地址的静态存储区字符指针,如果错误则函数返回NULL,地址转换函数(inet_addr()和 inet_ntoa(),8/6/2023,25,gethostname(),得到本机的名称int gethostname(char*hostname,int bufferLength)host

16、name是一个字符数组,bufferLength是该数组的长度。当调用成功,函数返回0并且将本机的名称赋值给hostname;当调用失败,则返回SOCKET_ERROR.,8/6/2023,26,从域名解析得到IP地址(gethostbyname),gethostbyname():给定主机名,(例如),得到主机IP地址.struct hostent*getbyhostname(char*hostname)char*h_name;/official name of hostchar*h_aliases;/alias listshort h_addrtype;/address family(e.g

17、.,AF_INET)short h_length;/length of address(4 for AF_INET)char*h_addr_list;/list of addresses(null pointer terminated),8/6/2023,27,下面的代码完成对的域名解析,得到其IP地址:hostent*phostent;/指向hostent结构的指针in_addr in;/IPV4地址结构if(phostent=gethostbyname()=NULL)printf(gethostbyname()错误:%d,WSAGetLastError();else/拷贝4字节的IP地址到

18、IPV4地址结构 memcpy(,域名解析示例,8/6/2023,28,字节序,不同的计算机系统采用不同的字节序存储数据,同样一个两字节的16位整数(0X0304),在内存中存储的方式就不同:一种方式是将低字节存储在起始地址,称为“Little-Endian”字节序,Intel、AMD等采用的是这种方式;另一种是将高字节存储在起始地址,称为“Big-Endian”字节序,由Macintosh、Motorola等所采用,8/6/2023,29,字节序转换函数,把给定系统所采用的字节序称为主机字节序。为了避免不同类别主机之间在数据交换时由于对于字节序解释的不同而导致的差错,引入了网络字节序,即网络

19、传输所采用的字节序。规定网络字节序使用“Big-Endian”方式。主机到网络u_long htonl(u_long hostlong);u_short htons(u_short short);网络到主机u_long ntohl(u_long hostlong);u_short ntohs(u_short short);,8/6/2023,30,例子:创建 SOCKADDR_IN,下面的代码演示了如何利用上面描述的inet_addr 和htons 函数来创建sockaddr_in结构。sockaddr_in internetAddr;int port=6666;internetAddr.si

20、n_family=AF_INET;/将点分的IP地址转换为4字节的整数并赋值给s_addr域internetAddr.sin_addr.s_addr=inet_addr(202.117.50.26);/port变量以主机字节序存储,因此将它转换为网络字节序并赋值给sin_portinternetAddr.sin_port=htons(port);,8/6/2023,31,int status=listen(socket_id,3);,8/6/2023,32,Basic Socket Calls(listen),/Berkeley formint listen(int sockfd,int ba

21、cklog);/WinSock formint listen(SOCKET sockfd,int backlog);,8/6/2023,33,listen(续),int listen(SOCKET sockfd,int backlog);sockfd监听连接的Socket1=backlog=5指定了正在等待连接的最大队列长度,它的作用在于处理可能同时出现的几个连接请求。例如,假定backlog参数为2,如果三个客户机同时发出请求,那么头两个会被放在等待处理的队列中,以便服务器程序依次为它们提供服务,而第三个连接的客户则会收到WSAECONNREFUSED错误。,8/6/2023,34,SOCK

22、ET child_sock=accept(socket_id,(struct sockaddr_in*)client_addr,sizeof(client_addr);,8/6/2023,35,Basic Socket Calls(accept),/Berkeley formint accept(int s struct sockaddr*addr,int*pointerToAddrLen);/WinSock formSOCKET accept(SOCKET s,struct sockaddr*addr,int*pointerToAddrLen);,8/6/2023,36,accept(续),

23、SOCKET accept(SOCKET s,struct sockaddr*addr,int*pointerToAddrLen);s是接受客户连接的Socketaddr用于接收外来连接的地址信息,如果暂时不关心该地址信息,则可以置为NULLpointerToAddrLen是addr结构的长度返回一个新的已连接的Socket,使用这个Socket可以和客户进行通信,而原来的监听Socket仍然可以接受其他客户的连接.,8/6/2023,37,Accept()调用实例,SOCKET s;/监听SocketSOCKET clientSocket;/客户Socketsockaddr_in addr;

24、/服务器的绑定地址sockaddr_in clientAddr;/连接的客户地址IN_ADDR clientIn;/客户IP地址int nClientAddrLen;/客户地址结构长度/创建流Sockets=socket(AF_INET,SOCK_STREAM,0);if(s!=INVALID_SOCKET)/填充地址信息addr.sin_family=AF_INET;addr.sin_port=htons(2000);addr.sin_addr.s_addr=htonl(INADDR_ANY);/绑定Socketif(bind(s,(sockaddr*)&addr,sizeof(addr)!

25、=SOCKET_ERROR)/监听连接if(listen(s,3)!=SOCKET_ERROR),8/6/2023,38,Accept()调用实例,/设置客户地址结构长度nClientAddrLen=sizeof(clientAddr);/接受连接clientSocket=accept(s,(sockaddr*)/.,8/6/2023,39,int status=connect(socket_id,(struct sockaddr_in*)addr,sizeof(addr);,8/6/2023,40,Basic Socket Calls(connect),/Berkeley formint c

26、onnect(int s struct sockaddr*addr,int sizeOfAddr);/WinSock formint connect(SOCKET s,struct sockaddr*addr,int sizeOfAddr);,8/6/2023,41,connect(续),int connect(SOCKET sockfd,struct sockaddr*addr,int addrLen);sockfd 由socket()调用返回addr 是指向 sockaddr_in 结构的指针,包含server IP 地址和端口号addrLen-sizeof(struct sockaddr

27、_in),8/6/2023,42,客户同服务器建立连接示例,SOCKET s;sockaddr_in serverAddr;s=socket(AF_INET,SOCK_STREAM,0);serverAddr.sin_family=AF_INET;serverAddr.sin_port=htons(2000);serverAddr.sin_addr.s_addr=inet_addr(202.117.50.26);if(connect(s,(sockaddr*)else/.,8/6/2023,43,int status=recv(socket_id,in_buffer,MAX_BUFFER_SI

28、ZE,0);,Example:char in_buffer MAX_BUFFER,On success,the number of bytes received,8/6/2023,44,Basic Socket Calls(recv),/Berkeley formint recv(int s char*bytesToReceive,int nBytes,int flags);/WinSock form int recv(SOCKET s,char*bytesToReceive,int nBytes,int flags);,8/6/2023,45,recv(续),int recv(SOCKET

29、s,char*bytesToReceive,int nBytes,int flags);s是已经连接的SocketbytesToReceive指向用于接收数据的缓冲区的指针nBytes是缓冲区的字节数flags 0,MSG_DONTROUTE,or MSG_OOB当调用成功,recv()返回被接收的字节数;如果返回0,则说明连接已经被关闭;当调用失败,返回SOCKET_ERROR。注意:recv()并不保证接收到所有请求的数据。它实际接收的字节数由返回值指示。也许需要循环调用recv()来得到需要的结果。,8/6/2023,46,recv()的典型用法,#define BUFSIZE 1024

30、SOCKET s;char bufBUFSIZE;/缓冲区int bytesRecv;/创建socket并连接服务器/.bytesRecv=recv(s,buf,BUFSIZE,0);if(bytesRecv=SOCKET_ERROR)printf(recv()调用错误:%d,WSAGetLastError();else if(bytesRecv=0)printf(对方已经关闭连接.);closesocket(s);else/处理缓冲区的数据,8/6/2023,47,#define BUFSIZE 4096char bufBUFSIZE;int left=BUFSIZE;char*p=buf;

31、/这里假设s是已经连接的流式Socket.while(left 0)ret=recv(s,p,left,0);if(ret=SOCKET_ERROR)/错误处理 left-=ret;p+=ret;,8/6/2023,48,int status=send(socket_id,out_buffer,MAX_BUFFER_SIZE,0);,On success,the number of bytes actually sent,8/6/2023,49,Basic Socket Calls(send),/Berkeley formint send(int s const char*bytesToSen

32、d,int nBytes,int flags);/WinSock form int send(SOCKET s,const char*bytesToSend,int nBytes,int flags);,8/6/2023,50,send(续),int send(SOCKET s,const char*bytesToSend,int nBytes,int flags);s是已经连接的SocketbytesToSend指向待发送数据缓冲区的指针nBytes是待发送数据的字节数flags e.g.,MSG_OOB注意:send()并不保证发送所有请求的数据。它实际发送的字节数由返回值指示。也许需要循

33、环调用send()来得到需要的结果。,8/6/2023,51,Example,#define BUFSIZE 4096char bufBUFSIZE;int left=BUFSIZE;char*p=buf;/给buf填充4096字节的待发数据./这里假设s是已经连接的流式Socket.while(left 0)ret=send(s,p,left,0);if(ret=SOCKET_ERROR)/错误处理 left-=ret;p+=ret;,8/6/2023,52,int status=closesocket(socket_id);,8/6/2023,53,Example,使用流式Socket的简

34、单应用,它包括客户和服务器两个程序。当客户同服务器建立连接后,客户会向服务器发送一个“来自客户的消息”这样的字符串,服务器以发送回字符串“欢迎连接服务器”作为响应。然后,双方程序都结束运行。,8/6/2023,54,UDP Sockets 编程,数据报服务(UDP):不可靠的传送创建 UDP socketsClientServer发送数据.接收数据.,8/6/2023,55,无连接协议的Socket 调用,client/server UDP 通信,Server,Socket(),Bind(),Recvfrom(),Blocks until data received,Process reque

35、st,Sendto(),Client,Socket(),Bind(),Sendto(),Blocks until data received,Process reply,recvfrom(),8/6/2023,56,无连接协议的Socket 调用,注意 recvfrom()仅仅监听 socket 地址,Server,Socket(),Bind(),Recvfrom(),Blocks until data received,Process request,Sendto(),Client,Socket(),Bind(),Sendto(),Blocks until data received,Pro

36、cess reply,recvfrom(),8/6/2023,57,无连接协议的Socket 调用,所以任何发送到该socket的数据包都会得到响应,Server,Socket(),Bind(),Recvfrom(),Blocks until data received,Process request,Sendto(),Client,Socket(),Bind(),Sendto(),Blocks until data received,Process reply,recvfrom(),8/6/2023,58,无连接协议的Socket 调用,对于server,这是需要的,但是client如何呢?

37、,Server,Socket(),Bind(),Recvfrom(),Blocks until data received,Process request,Sendto(),Client,Socket(),Bind(),Sendto(),Blocks until data received,Process reply,recvfrom(),8/6/2023,59,无连接协议的Socket 调用,如果一个“伪造”的应答到达client的 socket,它就如同server发送的一样被处理,Server,Socket(),Bind(),Recvfrom(),Blocks until data re

38、ceived,Process request,Sendto(),Client,Socket(),Bind(),Sendto(),Blocks until data received,Process reply,recvfrom(),8/6/2023,60,无连接协议的Socket 调用,换句话说,client 等待数据,但是并不检测谁发送的数据,Server,Socket(),Bind(),Recvfrom(),Blocks until data received,Process request,Sendto(),Client,Socket(),Bind(),Sendto(),Blocks u

39、ntil data received,Process reply,recvfrom(),close(),close(),8/6/2023,61,创建 UDP socket,SOCKET socket(int family,int type,int protocol);SOCKET sock;sock=socket(AF_INET,SOCK_DGRAM,0);,8/6/2023,62,服务器绑定地址,SOCKET s;sockaddr_in addr;s=socket(AF_INET,SOCK_DGRAM,0);addr.sin_family=AF_INET;addr.sin_port=hton

40、s(2000);addr.sin_addr.s_addr=htonl(INADDR_ANY);if(bind(s,(sockaddr*)&addr,sizeof(addr)=SOCKET_ERROR)/错误处理,Fill in addr,8/6/2023,63,Basic Socket Calls(sendto),/Berkeley formint sendto(int s,const char*bytesToSend,int nBytes,int flags,struct sockaddr*to,int sizeOfSockaddr);/WinSock form int sendto(SOC

41、KET s,const char*bytesToSend,int nBytes,int flags,struct sockaddr*to,int sizeOfSockaddr);,8/6/2023,64,sendto(续),int sendto(SOCKET s,const char*bytesToSend,int nBytes,int flags struct sockaddr*to,int sizeOfSockaddr);to是指向接收者地址的指针sizeOfSockaddr is sizeof(struct sockaddr_in)当调用成功,sendto()返回被成功发送的字节数;当调

42、用失败,返回SOCKET_ERROR需要说明两点:第一,由于UDP是不可靠的,因此sendto()的成功返回并不代表数据被成功发送到了目标地址,甚至不能保证数据已经从本机发出;第二,sendto()一次发送的数据大小是有上限的,至少要小于IP包的大小(约64K),8/6/2023,65,sendto()的典型用法,#define BUFSIZE 1024SOCKET s;sockaddr_in addr;char bufBUFSIZE;int bufLen;int bytesSent;s=socket(AF_INET,SOCK_DGRAM,0);if(s=INVALID_SOCKET)/错误处

43、理.else/填充接收者的地址addr.sin_family=AF_INET;addr.sin_port=htons(2000);addr.sin_addr.s_addr=inet_addr(202.117.50.26);/填充待发数据到缓冲区strcpy(buf,Hello,World!);bufLen=strlen(buf);/发送数据报bytesSent=sendto(s,buf,bufLen,0,(sockaddr*),8/6/2023,66,Basic Socket Calls(recvfrom),/Berkeley formint recvfrom(int s char*recei

44、vedData,int nBytes,int flags,struct sockaddr*from,int sizeOfSockaddr);/WinSock form int recvfrom(SOCKET s,char*receivedBytes,int nBytes,int flags,struct sockaddr*from,int sizeOfSockaddr);,8/6/2023,67,recvfrom(续),int recvfrom(SOCKET s,char*receivedData,int nBytes,int flags struct sockaddr*from,int si

45、zeOfSockaddr);from是指向数据发送者地址的指针sizeOfSockaddr is sizeof(struct sockaddr_in)当调用成功,recvfrom()返回被接收的字节数;当调用失败,返回SOCKET_ERROR。这里要说明,如果返回0,并不说明有错误发生,而是读取了一个纯UDP报文头。如果接收到的数据报比提供的接收缓冲区要大,那么会出现WSAEMSGSIZE错误。,8/6/2023,68,recvfrom()的典型用法,#define BUFSIZE 1024SOCKET s;sockaddr_in addr;/本Socket地址结构char bufBUFSIZ

46、E;/接收缓冲区int bytesRecv;sockaddr_in addrFrom;/发送方地址结构int addrFromLen=sizeof(addrFrom);/发送方地址结构长度in_addr inFrom;/发送方IP地址s=socket(AF_INET,SOCK_DGRAM,0);if(s=INVALID_SOCKET)/错误处理.,8/6/2023,69,else/填充本地地址结构以便从中读取数据addr.sin_family=AF_INET;addr.sin_port=htons(2000);addr.sin_addr.s_addr=htonl(INADDR_ANY);/绑定socketif(bind(s,(sockaddr*),8/6/2023,70,Example,使用的是无连接的数据报socket完成和的示例同样的功能。,8/6/2023,71,实例 从Web服务器下载文件,简单HTTP请求的格式,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号