530 likes | 770 Views
. TCP/IP 协议. . 协议软件接口 -socket. . 网络程序设计. Windows 网络编程 --winsock. . TCP 通信的例子程序. . UDP 通信的例子程序. . 参考书籍. 目前, Internet 主要使用 TCP/IP 协议来进行通信. 用 户. 1、 TCP/IP 协议. 应用层( FTP,HTTP,DHCP 等 ). TCP. UDP. IP. 数据链路层(网卡驱动程序). 传输介质. TCP/IP 协议结构简图. 报头长度. 总数据报文长度. 32位源 IP 地址.
E N D
TCP/IP协议 协议软件接口-socket 网络程序设计 Windows网络编程--winsock TCP通信的例子程序 UDP通信的例子程序 参考书籍
目前,Internet主要使用TCP/IP协议来进行通信 用 户 1、TCP/IP协议 应用层( FTP,HTTP,DHCP等 ) TCP UDP IP 数据链路层(网卡驱动程序) 传输介质 TCP/IP协议结构简图
报头长度 总数据报文长度 32位源IP地址 32位目的IP地址 可选项 数据 IP层协议:IP数据报文的简化图 1、TCP/IP协议 报头总长:5×4=20字节 IP地址:标识主机的网络接口---网卡
TCP层协议:TCP数据报文的简化图 16位源端口 16位目标端口 32位序数 32位应答序数 1、TCP/IP协议 标志 16位校验和 可选项 数据 端口:标识应用程序,如80—HTTP,21--FTP 标志:SYN,ACK,FIN
UDP层协议:UDP数据报文的简化图 16位源端口 16位目标端口 16位校长度 16位校验和 数据 1、TCP/IP协议
IP、TCP、UDP的关系 IP报文 IP报头 TCP报头 数 据 1、TCP/IP协议 20字节 20字节 IP报头 UDP报头 数 据 20字节 8字节 TCP主要用于有连接的通信:FTP,HTTP等 UDP主要用于无连接的通信:RTP,DNS等
操作系统 网络 应用 程序 TCP/IP API 2、协议软件接口-socket 目前,有几种可供应用程序使用TCP/IP协议 的API,最为常用的是伯克利大学为UNIX系统 定义的API,这种API现在被称为套接字(socket) 接口,并已成为事实的标准,微软也在其系统 中采用这种标准,称为Windows socket或Winsock
就象文件描述符是对文件的抽象一样,套接字 是对网络通信的抽象,是由操作系统创建并维护 的一个数据结构。 对文件操作 对套接字操作 2、协议软件接口-socket CFile f = new CFile(); f.read(); f.write(); f.close(); f.open(); ….. Socket s = creat(); s.read(); s.write(); s.close(); s.bind(); …..
套接字的数据结构 TCP/IP协议族 Family: PF_INET 服务类型 TCP:SOCK_STREAM UDP:SOCK_DGRAM Service: SOCK_STREAM Local IP: 2、协议软件接口-socket Remote IP: Local Port: Remote Port: 套接字有两中不同类型: 流套接字和数据报套接字 面向连接的TCP 无连接的UDP
Socket使用的地址结构 Struct sockaddr_in { short sin_family; //AF_INET地址族unsigned short sin_port; //使用的端口,2字节 struct in_addr sinaddr; //IP地址,4字节 char sinzero[8]; //预留,8字节 } Struct sockaddr { u_short sa_family; //地址族 char sa_data[14]; //地址 } 2、协议软件接口-socket
Socket提供的函数 socket()创建用于网络通信的描述符—套接字 bind()将本地IP地址和协议端口号绑定到套接字 2、协议软件接口-socket listen()将套接字置入监听模式并准备接受连接请求 accept()作好接受客户端连接的准备 connect()连接远程对等实体(服务器) read()接收下一个传入的数据报文 write()发送外发的一个数据报文 close()终止通信并释放套接字占用的资源
Socket编程原理:采用C/S结构 服务器 客户机 进程通信设施 监听 请求 2、协议软件接口-socket 请求 应答 应答 时 间
服务器 客户机 socket() bind() listen() socket() accept() connect() write() read() read() write() close() close() 2、协议软件接口-socket 流套接字编程时序图(TCP)
服务器 客户机 socket() socket() bind() bind() 2、协议软件接口-socket readfrom() sendto readfrom() sendto close() close() 数据报套接字编程时序图(UDP)
①winsock:针对TCP/IP编程的底层API,其部分代 码在winsock.dll,较复杂但灵活性好 ②WinInet:是比winsock更高一层的API,但它只实 用于HTTP,FTP,gopher的客户端程序,不能用 来创建服务器程序。 ③ MFC类:面向对象的API 有: CAsyncSocket,CSocket CInternetSession, CHttpConnection CGopherConnection, CInternetFile CHttpFile, CFtpFile, CGopherFileFind CInternetException等派生类 ④控件:功能强,使用简单,但通常需要图形界面 的支持,如对话框。 3、winsock基础 对winsock的封装 对winInet 的封装
3.1 Winsock版本 采用Winsock 1的应用必须有Winsock.h 包含文件 使用Winsock 2的应用需要Winsock2.h 包含文件。
3.2 使用winsock进行通信 初始化/加载 Winsock库 创建socket 使用socket进行通信 关闭socket 清除Winsock库
3.2 使用winsock进行通信 ①Winsock初始化与结束 每个Winsock 应用都必须加载Winsock DLL的相应版本。 如果调用Winsock 之前,没有加载Winsock 库,这个函数就会返回一个SOCKET_ERROR,错误信息是WSANOTINITIALISED。 加载Winsock 库是通过调用WSAStartup函数实现的。 程序结束退出前,应该释放对Winsock的使用,调用函数WSAcleanup来实现。
3.2 使用winsock进行通信 WSAStartup初始化Windows Sockets API 应用程序 WSACleanup释放所使用的Windows Sockets DLL
3.2 使用winsock进行通信 ②错误检查和控制 如果调用一个Winsock 函数,错误情况发生了,就可用WSAGetLastError函数来获得一个代码,这个代码明确地表明发生的状况。 发生错误之后调用这个函数,就会返回所发生的特定错误的完整代码。WSAGetLastError 函数返回的这些错误都已预定义常量值,根据Winsock 版本的不同,这些值的声明不在Winsock .h中,就会在Winsock2.h中。
3.2 使用winsock进行通信 • 读取当前错误值:每次发生错误时,如果要对具体问题进行处理,那么就应该调用这个函数取得错误代码。函数定义如下: • int WSAGetLastError(void ); • 错误值请自己阅读Winsock2.h。
4 TCP通信的例子 • 本节讨论TCP协议编程,包括 • TCP服务器端编程 • TCP客户端编程
server.c client.c 开发环境——提供各种函数调用和接口 4.1客户机/服务器模式
4.1客户机/服务器模式 • 客户机/服务器模式的建立基于以下两点: • 1、非对等作用; • 2、通信完全是异步的。在操作中采取的是主动请示方式: • 服务器方(先启动,并根据请示提供相应服务): • 1、打开一通信通道并告知本地主机,它愿意在某一个公认 • 地址上接收客户请求。 • 2、等待客户请求到达该端口。 • 3、接收到重复服务请求,处理该请求并发送应答信号。 • 4、返回第二步,等待另一客户请求 • 5、关闭服务器。 • 客户方: • 1、打开一通信通道,并连接到服务器所在主机的特定端口。 • 2、向服务器发送服务请求报文,等待并接收应答;继续提 • 出请求…… • 3、请求结束后关闭通信通道并终止。
4.2面向连接的套接字的系统调用时序图 s:主线程套接字,用于 监听客户端的请求 ns:子线程套接字,用 于与特定的客户端连接
4.3.1创建套接字socket() • 创建套接字:(服务器端和客户端) • SOCKET socket ( int af, int type, int protocol ); • 参数: • af:网络地址类型; type:套接字类型; protocol:指定网络协议。
4.3.2套接字的绑定bind() • 套接字的绑定:将本地地址绑定到所创建的套接字上(服务器端和客户端) • int bind ( • SOCKET s, • const struct sockaddr FAR * name, • int namelen ); • 参数: • S: 是由socket()调用返回的并且未作连接的套接字描述 • 符(套接字号)。 • Name:socket地址结构,为sockaddr结构,一般使用 • sockaddr_in结构,在使用再强制转换为sockaddr • 结构。 • Namelen:地址结构的长度。
4.3.3套接字的监听listen() • 套接字的监听:(服务器端) • int listen ( SOCKET s, int backlog ) • 参数: • s:一个已绑定但未联接的套接字。 • backlog:指定正在等待联接的最大队列长度, • 这个参数非常重要,因为服务器一般 • 可以提供多个连接。
4.3.4套接字等待连接accept( ) • 套接字等待连接:(服务器端) • SOCKET accept ( • SOCKET s, • struct sockaddr FAR * addr, • int FAR * addrlen ) • 参数: • s:处于监听模式的套接字。 • sockaddr:接收成功后返回客户端的网络地址。 • addrlen:网络地址的长度。
Winsock 2引入了一个名为WSAAccept的函数。它能根据一个条件函数的返回值,选择性地接受一个连接。这个新函数的定义如下 Winsock2的等待函数WSAAccept的函数 其中,头三个参数与accept的Winsock 1版本是相同的。lpfnCondition参数是指向一个函数的指针,那个函数是根据客户请求来调用的。该函数决定是否接受客户的连接请求。
4.3.5套接字的连接connect() • 套接字的连接:将两个套接字连结起来准备通信。(客户端) • int connect ( • SOCKET s, • const struct sockaddr FAR * name, • int namelen ) • 参数: • s:欲连结的已创建的套接字。 • name:欲连结的socket地址。 • namelen:为socket地址的结构的长度。
Winsock2的连接函数WSAConnect() 前三个参数和connect API函数的参数是完全一样的。 lpCallerData参数是指向缓冲区的指针,缓冲区内包含客户机向服务器发出的请求连接的数据。 lpCalleeData参数则指向另一个缓冲区,区内包含服务器向客户机返回的建立连接时的数据。这两个参数都是WSABUF结构,因此,若是lpCallerData,len字段应该设为buf字段中准备传输的数据长度。若是lpCalleeData,len字段则代表buf中的缓冲区长度,设为从服务器返回的数据长度。 lpQOS参数用于指定套接字s需要的服务质量,而lpGQOS则用于指定套接字组所需要的服务质量。目前,尚未提供对套接字组的支持。若lpQOS是空值,则表明没有某应用专用的Q O S。
数据传输 要在已建立连接的套接字上接收数据,可用这两个A P I函数:send和WSASend。第二个函数是Winsock 2中专有的。 在已建立了连接的套接字上接收数据也有两个函数: recv和WSARecv。后者也是Winsock 2函数
4.3.6 发送数据send() • 发送数据:(服务器端和客户端) • int send ( • SOCKET s, • const char FAR * buf, • int len, int flags ) • 参数: • s:服务器端监听的套接字。 • buf:欲发送数据缓冲区的指针。 • len:发送数据缓冲区的长度。 • flags:数据发送标记。可为0、 • MSG_DONTROUTE或MSG_OOB
Winsock2发送数据WSAsend() int WSASend ( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionROUTINE ); S: 是一个连接会话的有效句柄。 lpBuffers是指向一个或多个WSABUF结构的指针。 dwBufferCount指明准备投递的WSABUF结构数。 lpNumberOfBytesSent是指向DWORD(是WSASend调用返回的)的指 针,其中包含字节总发送数。 dwFlags参数与它在send中的意义相同。 lpOverlapped和lpCompletionROUTINE—用于重叠I/O。 WSASend函数把lpNumberOfBytesSent设为写入的字节数。成功的话,该函数就返回0,否则就返回SOCK_ERROR。
4.3.7 数据接收recv( ) • 数据接收:(客户端) • int recv ( • SOCKET s, • char FAR * buf, • int len, int flags ) • 参数: • s:准备接收数据的套接字。 • buf:准备接收数据的缓冲区。 • len:准备接收数据缓冲区的大小。 • flags:数据接收标记。: 0、MSG_PEEK、 • MSG_OOB
中断连接 一旦完成任务,就必须关掉连接,释放关联到那个套接字句柄的所有资源。要真正地释放与一个开着的套接字句柄关联的资源,执行closesocket调用即可。 closesocket可能会带来负面影响,即可能会导致数据的丢失。应该在调用closesocket函数之前,利用shutdown函数从容中断连接。
4.3.8 中断套接字连接shutdown() • 中断套接字连接:通知服务器端或客户端停止接收和发送数据。(服务器端和客户端) • int shutdown ( SOCKET s, int how ) • 参数: • s:欲中断连接的套接字。 • How:描述禁止哪些操作,取值为: • SD_RECEIVE、SD_SEND、SD_BOTH。 • #define SD_RECEIVE 0x00 • #define SD_SEND 0x01 • #define SD_BOTH 0x02
4.3.9 关闭套接字closesocket() • 关闭套接字: • 释放所占有的资源。(服务器端和客户端) • int closesocket( SOCKET s ) • 参数: • s:欲关闭的套接字。
TCP程序示例 • 服务器上: • Tcp\server.c • Tcp\client.c
5、无连接协议 • 典型的无连接协议 : UDP • 本节讨论 • 接收端(可称之为服务器) • 发送端
接收端 1、用socket或WSASocket建立套接字。 2、通过bind函数把这个套接字和准备接收数据 的接口绑定在一起。 3、和面向会话不同的是,我们不必调用listen 和accept。 4、相反,只需等待接收数据。 5、由于它是无连接的,因此始发于网络上任 何一台机器的数据报都可被接收端的套接 字接收。
5.1接收数据函数recvfrom 1、前面四个参数和recv是一样的,其中包括标志 MSG_OOB和MSG_PEEK。 2、对监听套接字的具体协议来说,from参数是一个 SOCKADDR结构,带有指向地址结构的长度的 fromlen。 3、这个API调用返回数据时,SOCKADDR结构内便填入 发送数据的那个工作站的地址。
两者的差别在于接收数据的W S A B U F结构的用法上。 利用d w B u ff e r C o u n t为W S A R e c v F r o m提供一个或多个W S A B U F缓冲。提供多个缓冲,就可用发散集合了。 读取的字节总数返回在l p N u m b e r O f B y t e s R e c v d中。 l p F l a g s参数可以是代表无选项的0、MSG_OOB、MSG_PEEK或MSG_PARTIAL。这些标志还可以累加起来。如果在调用这个函数时,指定MSG_PARTIAL,提供者就知道返回数据,即使只收到了部分消息。调用返回之后,如果只收到部分消息,就会设置MSG_PARTIAL标志。再次返回之后,WSARecvFrom就会把lpFrom参数(它是一个指向SOCKADDR结构的指针)设为发送端的地址。 lpFromLen指向SOCKADDR结构的长度,另外,在这个函数中,它还是一个指针,指向DWORD。最后两个参数, lpOverlapped和lpCompletionROUTINE,用于重叠I / O
发送端 要在一个无连接的套接字上发送数据,有两种选择。 第一种,也是最简单的一种,便是建立一个套接字,然后调用sendto或WSASendTo
5.2 发送数据 sendto函数 b u f是发送数据的缓冲, l e n指明发送多少字节。 其余参数和r e c v f r o m的参数一样。 t o参数是一个指向SOCKADDR结构的指针,带有接收数据的那个工作站的目标地址。