第3章基本TCP套接口编程_第1页
第3章基本TCP套接口编程_第2页
第3章基本TCP套接口编程_第3页
第3章基本TCP套接口编程_第4页
第3章基本TCP套接口编程_第5页
已阅读5页,还剩33页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Tcp套接字编程知识点: 使用TCP套接字编程的基本步骤 实现TCP套接字编程的基本函数 服务器的3种异常情况TCP套接字编程(cont.)#include int socket(int family, int type, int protocol) 返回:非负套接字(sockfd)成功;-1出错。功能:指定协议类型功能:指定协议类型参数说明:参数说明:family:协议族;type:套接字类型; protocol:一般为0,除原始套接字外。 family typeAF_INET IPv4协议SOCK_STREAM 字节流套接口AF_INET6IPv6协议SOCK_DGRAM 数据报套接口AF

2、_LOCALunix域协议SOCK_RAW 原始套接口AF_ROUTE 路由套接口AF_KEY 密钥套接口基本套接字函数socket AF_INET AF_INET6 AF_LOCAL AF_ROUTE AF_KEY SOCK_STREAM TCP TCP Yes SOCK_DGRAM UDP UDP Yes SOCK_RAW IPv4 IPv6 Yes Yes Protocol:指明此socket请求所使用的协议,可以使用如下相关符号常数来表示。IPPROTO_TCP:表示TCP协议IPPROTO_UDP:表示UDP协议调用Socket()函数的过程socket 函数指定了协议族函数指定了协

3、议族(IPv4、IPv6或或unix)和套接口类型和套接口类型(字节流、数据报或原字节流、数据报或原始套接口始套接口)。但并没有指定本地协议地址或远程协议地址。但并没有指定本地协议地址或远程协议地址。理解理解socket socket使用 Unix 文件描述符 (file descriptor) 和其他程序通讯的方式。 Unix 程序在执行任何形式的 I/O 的时候,程序是在读或者写一个文件描述符。一个文件描述符只是一个和打开的文件相关联的整数。这个文件可能是一个网络连接,FIFO,管道,终端,磁盘上的文件或者什么其他的东西。Unix 中所有的东西是文件!因此,与 Internet 上别的程序

4、通讯的时候,要通过文件描述符。 利用系统调用 socket()得到网络通讯的文件描述符。他返回套接口描述符 (socket descriptor),然后再通过他来调用 send() 和 recv()。那么为什么不用一般的调用那么为什么不用一般的调用 read() 和和 write() 来通过套接口通讯?来通过套接口通讯? 简单的答案是:可以使用一般的函数! 详细的答案是:使用 send() 和 recv() 让你更好的控制数据传输。#include #include int bind(int sockfd, const struct sockaddr *addr, socklen_len ad

5、drlen) 返回:0成功;-1出错并置errno功能:给套接口分配一个本地协议地址。功能:给套接口分配一个本地协议地址。参数说明:参数说明: sockfd是套接字函数返回的套接字描述符;addr参数是指向特定协议的地址结构的指针,指定用于通信的本地协议地址,这是通用地址(网际协议)。 addrlen是该套接字地址结构的长度。 该函数指明套接字将使用本地的哪一个协议端口进行数据传送(IP地址和端口号)。 一般而言,服务器调用此函数,而客户则很少调用它。 基本套接字函数bind 绑定地址时,可以指定地址和端口号,也可以指定其中之一,甚至一个也不指定。通配地址:INADDR_ANY,其值一般为0,

6、它通知内核选择IP地址。 若指定端口号为0,调用函数bind时,内核选择一个临时端口(在实际中,端口号都要指定);但若指定一个通配IP地址,则直到套接字已连接(TCP)或数据报已在套接字上发出(UDP),内核才选择一个本地IP地址。 表中给出了调用bind()函数时,IP地址和端口号的取值及其对应的结果。IP地址地址 端口端口结果结果通配地址 0内核选择IP地址和端口号通配地址 非0内核选择IP地址,进程指定端口本地IP 0进程指定IP地址,内核选择端口本地IP 非0进程指定IP地址和端口号struct sockaddr_in addr;int port = 1234;int opt = SO

7、_REUSEADDR;setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt); bzero(&server,sizeof(server);addr.sin_family = AF_INET;addr.sin_addr.s_addr = htonl(INADDR_ANY);addr.sin_port = htons(port);if (bind(fd, (struct sockaddr *)&addr, sizeof(addr) = -1)/* 错误处理 */bind函数的用法#include int list

8、en(int sockfd, int backlog) 返回:0成功;-1出错并置errno值;功能:将未连接主动套接口的转换为被动套接口,指示内核接受功能:将未连接主动套接口的转换为被动套接口,指示内核接受对该套接口的连接请求对该套接口的连接请求.函数listen仅被服务器调用,它完成两件事情: 函数listen将未连接的套接字转化成被动套接字,指示内核应接受指向此套接字的连接请求; 函数的第二个参数规定了内核为此套接字排队的最大连接个数;基本套接字函数listen三路握手过程客户服务器Connect调用在未完成队列建立条目SYN JSYN K, ack J+1ack K+1该条目从未完成队

9、列移至已完成队列,accept阻塞Connect返回TCP三路握手和监听套接口的两个队列对于给定的监听套接字,内核要维护两个队列: 未完成连接队列:为每个请求建立连接的SYN分节开设一个条目,服务器正等待完成TCP三次握手,当前的套接字处在SYNRCVD状态; 已完成连接队列:为每个已完成TCP三次握手的客户端开设一个条目。当前的套接字状态为ESTABLISHED。 两个队列之和不超过backlog;三路握手完成两队列之和不能超过backlog已完成连接队列(ESTABLISHED状态)未完成连接队列(SYN_RCVD状态)新到达的SYN分节服务器TCPacceptTCP为监听套接口维护的两个

10、队列为监听套接口维护的两个队列另外几点说明:另外几点说明: 不同的实现对backlog有不同的解释,如源自Berkeley的实现将backlog增加一个模糊因子,把它乘以1.5,再作为两个队列之和; 不要把backlog定义为0,因为有些实现允许1个连接排队,而有些实现不允许有连接排队; 当一个客户SYN到达时,若两个队列都是满的,tcp就忽略此分节,且不发送RST。这是因为,这种情况是暂时的,客户tcp将重发SYN,期望不久的将来就能在队列中找到空闲条目。#include #include int connect(int sockfd, const struct sockaddr *addr

11、, socklen_t addrlen); 返回:0成功;-1出错;功能:建立与功能:建立与TCP服务器的连接服务器的连接参数说明:参数说明:/sockfd 是系统调用 socket() 返回的套接口文件描述符/serv_addr 是保存着目的地端口和 IP 地址的数据结构 struct sockaddr/addrlen 设置为 sizeof(struct sockaddr) 函数connect激发TCP的三路握手过程;仅在成功或出错返回;基本套接字函数connect错误有以下几种情况错误有以下几种情况: 如果客户没有收到SYN分节的响应(总共75秒,这之间需要可能需要重发若干次SYN),则返

12、回ETIMEDOUT。 如果对客户的SYN的响应是RST,则表明该服务器主机在指定的端口上没有进程在等待与之相连。函数返回错误ECONNREFUSED; 如果客户发出的SYN在中间路由器上引发一个目的地不可达ICMP错误,客户上的内核保存此消息,并按第一种情况,连续发送SYN,直到规定时间,返回保存的消息(即ICMP错误)作为EHOSTUNREACH或ENETUNREACH错误返回给进程。#include int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen); 返回:非负描述字(connfd)OK;-1出错;功

13、能功能:在已完成队列头返回下一个已完成的连接。在已完成队列头返回下一个已完成的连接。 accept函数由TCP服务器调用;从已完成连接队列头返回下一个已完成连接;如果该队列空,则进程进入睡眠状态。 函数返回的套接字为已连接套接字,应与监听套接字区分开来 该函数最多:一个既可能是新套接字也可能是错误指示的整数,一个客户进程的协议地址(由cliaddr所指),以及该地址的大小(这后两个参数是值结果参数);也就是说,服务器可以通过参数cliaddr来得到请求连接并获得成功的客户的地址和端口号;基本套接字函数accept几点说明:几点说明: 调用成功时返回: 1. cliaddr: 客户进程的协议地址

14、和地址大小 2. 新套接口描述字(已连接套接口描述字) , 监听套接口描述字 listening socket descriptor一个给定的服务器常常是只生成一个监听套接口, 且一直存在,直到该服务器关闭。 已连接套接口描述字connected socket descriptor内核为每个被接受的客户连接创建了一个已连接套接口。当服务器完成某客户的服务时,关闭已连接套接口。 1024以下的端口:超级用户使用#include int close(int sockfd); 返回:0OK;-1出错; close函数缺省功能是将套接字做上“已关闭”标记,并立即返回到进程。这个套接字不能再为该进程所用

15、(即不能再接收和发送数据,不能作为函数read和write的参数)。 正常情况下,close将引发向TCP的四分节终止序列,但在终止前将发送已排队的数据; 如果套接字描述符访问计数在调用close后大于0(在多个进程共享同一个套接字的情况下),则不会引发TCP终止序列(即不会发送FIN分节);基本套接字函数close#include int shutdown(int sockfd, int howto); 返回:0OK;-1出错,并置相应的errno的值; 该函数立即发送FIN分节(无论其访问计数是否大于0)。shutdown根据参数howto关闭指定方向的数据传输; SHUT_RD:关闭连接

16、的读这一半,不再接收套接字中的数据且现留在接收缓冲区的数据作废; SHUT_WR :关闭连接的写这一半(半关闭),当留在套接字发送缓冲区中的数据都被发送,后跟tcp连接终止序列,不管访问计数是否大于0;此后将不能在执行对套接字的任何写操作; SHUT_RDWR:连接的读、写都关闭,这等效于调用shutdown两次,一次调用是用SHUT_RD,第二次用SHUT_WR。基本套接字函数-shutdown数据数据FIN数据和FIN的确认数据数据FIN数据和FIN的确认writewriteshutdownRead返回大于0Read返回大于0Read返回0writewritecloseRead返回大于0R

17、ead返回0Read返回大于0客户服务器调用shutdown关闭一半TCP连接#include int write(int fd, char *buf, int len); 返回:大于0读写字节大小;-1出错; 调用函数write,有如下几种情况: 套接字发送缓冲区有足够空间,返回发送的字节数; tcp协议接收到RST数据,返回1,同时errno为ECONNRESET; ; 进程阻塞过程中接收到信号,返回1,同时errno为EINTR。write(connfd,buff,strlen(buff);基本套接字函数write#include int read(int fd, char *buf,

18、int len);返回:大于0读写字节大小;-1出错;调用函数read时,有如下几种情况:套接字接收缓冲区接收数据,返回接收到的字节数;tcp协议收到FIN数据,返回0;tcp协议收到RST数据,返回1,同时errno为ECONNRESET;进程阻塞过程中接收到信号,返回1,同时errno为EINTR。read(connfd,buff,strlen(buff);基本套接字函数read#include #include ssize_t send (int fd, const void *msg, size_t len, int flags); 返回:非0发送成功的数据长度;-1出错; flags

19、 是传输控制标志,其值定义如下: 0:常规操作,如同write()函数 MSG_OOB,发送带外数据(TCP紧急数据)。 MSG_DONTROUTE:忽略底层协议的路由设置,只能将数据发送给与发送机处在同一个网络中的机器上。 MSG_DONTWAIT:打开非阻塞模式(了解就OK),执行I/O操作,再关闭非阻塞模式。数据传输函数send#include #include ssize_t recv(int fd, void *buf ,size_t len, int flags); 返回:大于0表示成功接收的数据长度;0: 对方已关闭,-1:出错。 flags是传输控制标志,其值定义如下: 0:常

20、规操作,如同read()函数; MSG_PEEK:只查看数据而不读出数据,后续读操作仍然能读出所查看的该数据; MSG_OOB:忽略常规数据,而只读带外数据; MSG_WAITALL:recv函数只有在将接收缓冲区填满(也就是len的值)后才返回。 MSG_DONTWAIT:将单个I/O操作设为非阻塞模式,执行I/O操作,然后关闭非阻塞标志。数据传输函数recv 功能:派生新进程 create new process 定义:#include pid_t fork (void); 在子进程中返回0,在父进程中返回子进程的进程ID 出错时返回 1,调用一次返回两次fork 函数 fork的典型应用

21、(多线程): 1一个进程可为自己创建一个拷贝。当一个拷贝处理一个操作时,其他的拷贝可以执行其他的任务。这是非常典型的网络服务器。 2一个进程想执行其他的程序,由于创建新进程的唯一方法是调用fork,进程首先调用fork来生成一个拷贝,然后其中一个拷贝(通常为子进程)调用exec 来代替自己去执行新程序。 socket() -得到文件描述符! bind() -我们在哪个端口? connect() -Hello! listen() -有人给我打电话吗? accept() -Thank you for calling port 3490. send() 和 recv() -Talk to me, b

22、aby! sendto() 和 recvfrom() -Talk to me, DGRAM-style close() 和 shutdown() -滚开! getpeername() -你是谁? gethostname() -我是谁? DNS -你说“白宫”,我说 198.137.240.100简而言之简而言之实现TCP套接字基本步骤分为服务器端和客户端两部分:服务器端 创建套接字; 绑定套接字; 设置套接字为监听模式,进入被动接受连接状态; 接受请求,建立连接 读写数据终止连接TCP套接字编程客户端步骤 创建套接字 与远程服务器建立连接 读/写数据;终止连接TCP套接字编程(cont.)in

23、t main(void)int sockfd,connect_sock;if(sockfd=socket(AF_INET,SOCK_STREAM,0)=-1) perror(“create socket failed.”);exit(-1);/* bind sockfd to some address */* listen */loop if(connect_sock=accept(sockfd,NULL,NULL)=-1) perror(“Accept error.”); exit(-1); /* read and process request */close(connect_sock);

24、 close(sockfd);TCP服务器模板/* include some header files */int main(void)int sockfd;if(sockfd=socket(AF_INET,SOCK_STREAM,0)=-1)perror(“Create socket failed.”);exit(-1);/* connect to server */* send requst and receive response */close(sockfd);TCP客户模板 采用客户/服务器模式,完成下列功能: 客户根据用户提供的IP地址,连接相应的服务器; 服务器等待客户的连接,一旦连接成功,则显示客户的IP地址,并发欢迎信息给客户; 客户接收服务器发送的信息并显示;TCP套接字例程 下面我们考虑在以下三种异常情况发生后,tcp客户服务器程序的

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论