Socket函数深入学习

来源:互联网 发布:linux怎么创建用户 编辑:程序博客网 时间:2024/06/06 19:40

1. 概述

          在网络协议中,分层思想是非常重要的,各层协议分工明确,各干各事。在现实世界中,IP网际层的实现分布在路由器和各种PC终端中,TCP传输层的实现则存在于PC终端。换句话说,路由器只实现IP协议,而终端PC的操作系统同时实现了IP和TCP层协议。为了让开发者实现各种应用程序,不同的操作系统都会提供了一组socket函数,供开发者使用。通过socket函数,开发者能够进行网络通讯,并且能够对IP/TCP层协议的某些特性进行控制。

          在所有的套接字函数中,Berkeley套接字(也称为BSD套接字)使用的最为广泛。由于专利原因,Berkeley套接字由C语言实现,只被使用在UNIX操作系统上。但其接口形式成为了事实上的网络套接字的标准,不同操作系统都有类似接口,包括Linux和Window;大多数其他的编程语言也使用了与Berkeley套接字类似的接口。

          Berkeley套接字API库提供的函数包括:
          1. socket() 创建一个新的确定类型的套接字,类型用一个整型数值标识(文件描述符),并为它分配系统资源。
          2. bind() 一般用于服务器端,将一个套接字与一个套接字地址结构相关联。
          3. listen() 用于服务器端,使一个绑定的TCP套接字进入监听状态。
          4. connect() 用于客户端,为一个套接字分配一个自由的本地端口号。 如果是TCP套接字的话,它会试图获得一个新的TCP连接。
          5. accept() 用于服务器端。 它接受一个从远端客户端发出的创建一个新的TCP连接的接入请求,创建一个新的套接字,与该连接相应的套接字地址相关联。
          6. send()recv(),或者write()read(),或者recvfrom()sendto(), 用于往/从远程套接字发送和接受数据。
          7. close() 用于系统释放分配给一个套接字的资源。 如果是TCP,连接会被中断。
          8. shutdown() 用于关闭socket连接。该函数允许只停止在某个方向上的数据传输,而一个方向上的数据传输继续进行。
          9. gethostbyname()gethostbyaddr() 用于解析主机名和地址。
          10. select() 用于修整有如下情况的套接字列表: 准备读,准备写或者是有错误。
          11. poll() 用于检查套接字的状态。 套接字可以被测试,看是否可以写入、读取或是有错误。
          12. getsockopt() 用于查询指定的套接字一个特定的套接字选项的当前值。
          13. setsockopt() 用于为指定的套接字设定一个特定的套接字选项。
          14. htons()htonl()ntohs()ntohl() 用于网络序和本机序转换。
          15. inet_addr() 用于字符串形式的IP地址和网络序形式的IP地址的转换。
          16. inet_ntoa() 用于网络序形式的IP地址和字符串形式的IP地址的转换。


2. 结构体和函数

2.1 结构体

          套接字用到的各种数据类型
          1. socket描述符。它是一个int值。在window下可能被定义为SOCKET,SOCKET也是一个int值,根据平台不同,被定义为int32或者int64。
[cpp] view plaincopy
  1. struct sockaddr {   
  2.    unsigned short sa_family; /* 地址家族, AF_xxx */   
  3.    char sa_data[14]; /*14字节协议地址*/   
  4. };   

          这个结构被为许多类型的套接字储存套接字地址信息。sa_family一般为"AF_INET",sa_data包含套接字中的目标地址和端口信息。在一个字符数组中同时存储目标地址和端口信息非常令人困惑,所以还存在一个并列的结构,struct sockaddr_in ("in" 代表 "Internet"。),其定义如下:
[cpp] view plaincopy
  1. #include <netinet/in.h>  
  2. struct sockaddr_in {   
  3.    short int sin_family; /* 通信类型 */   
  4.    unsigned short int sin_port; /* 端口 */   
  5.    struct in_addr sin_addr; /* Internet 地址 */   
  6.    unsigned char sin_zero[8]; /* 与sockaddr结构的长度相同*/   
  7. };   


          可以看到其中sin_zero 被加入到这个结构,目的是为了使sockaddr_in和 struct sockaddr 长度一致,可以使用函数 bzero() 或 memset() 来全部置零。 而 sin_family 和 struct sockaddr 中的 sa_family 一致,能够设置为 "AF_INET"。结构体 sin_port和 sin_addr 则是网络字节顺序 (Network Byte Order)。(关于网络序可见文章数据类型转换)

          struct in_addr的定义如下:
[cpp] view plaincopy
  1. #include <netinet/in.h>  
  2. struct in_addr {   
  3.    unsigned long s_addr;   
  4. };   


2.2 字节转换:

[cpp] view plaincopy
  1. htons()--"Host to Network Short"  
  2. htonl()--"Host to Network Long"  
  3. ntohs()--"Network to Host Short"  
  4. ntohl()--"Network to Host Long"  

          其中 short (两个字节)和 long (四个字节)。这些函数对于变量类型 unsigned 也是适用对。假设想将 short 从本机字节顺序转换为网络字节顺序。可以用 "h" 表示 "本机 (host)",接着是 "to",然后用 "n" 表示 "网络 (network)",最后用 "s" 表示 "short": h-to-n-s, 或者 htons() ("Host to Network Short")。


2.3 IP 地址和字符串转化:

          inet_addr()和inet_ntoa() 用于字符串和int值之间的字符串转换。使用如下:
[cpp] view plaincopy
  1. sockaddr_in ina;  
  2. ina.sin_addr.s_addr = inet_addr("192.168.1.1");  
  3.   
  4. char *a1;  
  5. a1 = inet_ntoa(ina.sin_addr); /* 这是192.168.1.1*/  
  6. printf("address 1: %s/n",a1);  


2.4 函数:

2.4.1 socket()函数

[cpp] view plaincopy
  1. #include <sys/types.h>;   
  2. #include <sys/socket.h>;   
  3. int socket(int domain, int type, int protocol);   

          参数 domain: 通常被设置成 "AF_INET"。
          参数 type: 告诉内核是 SOCK_STREAM 类型还是 SOCK_DGRAM 类型。
          参数 protocol :通常被设置为 "0"。
          如果需要更多的信息,可以看 socket() 的 man帮助。
          返回值: socket() 只是返回你以后在系统调用种可能用到的 socket 描述符,在错误的时候返回-1。全局变量 errno 中将储存返回的错误值。

2.4.2 connect()函数

[cpp] view plaincopy
  1. #include <sys/types.h>;   
  2. #include <sys/socket.h>;  
  3. int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);   

          参数 sockfd :是系统调用 socket() 返回的套接字文件描述符。
          参数 serv_addr :是 保存着目的地端口和 IP 地址的数据结构 struct sockaddr。
          参数 addrlen: 设置 为 sizeof(struct sockaddr)。
          connect() 函数只用于客户端,使socket变成主动socket (active socket)

2.4.3 bind()函数

[cpp] view plaincopy
  1. #include <sys/types.h>;  
  2. #include <sys/socket.h>;  
  3. int bind(int sockfd, struct sockaddr *my_addr, int addrlen);   

          参数 sockfd :是调用 socket 返回的文件描述符。
          参数 my_addr :是指向数据结构 struct sockaddr 的指针,它保存地址(即端口和 IP 地址) 信息。如果IP地址信息为INADDR_ANY, 表示不关心本地地址信息。在存在多个IP地址的情况下,所有的IP都会进行被绑定。
          参数 addrlen :设置为 sizeof(struct sockaddr)。
          返回值:bind() 在错误的时候依然是返回-1,并且设置全局错误变量errno。 


          使用bind()函数需要注意以下一些问题:
          1. bind() 函数只用于服务器端,使socket变成被动socket (passive socket)
          2. 不要采用小于 1024的端口号。所有小于1024的端口号都被系统保留!可以选择1024 到65535之间的端口。见<《计算机网络》 读书笔记(四) 运输层> "1.1.3 运输层端口"。
          3. 按照下面的写法可以让系统自动处理端口和地址。
[cpp] view plaincopy
  1. my_addr.sin_port = htons(0); /* 随机选择一个没有使用的端口 */   
  2. my_addr.sin_addr.s_addr = htonl(INADDR_ANY);/* 使用自己的IP地址 */   

2.4.4 listen()函数

[cpp] view plaincopy
  1. int listen(int sockfd, int backlog);   

          参数 sockfd :是调用 socket() 返回的套接字文件描述符。

          参数backlog: 是在进入队列中允许的连接数目。 进入的连接是在队列中一直等待直到你接受连接。它们的数目限制于队列的允许。 大多数系统的允许数目是20。
          返回值:和别的函数一样,在发生错误的时候返回-1,并设置全局错误变量 errno。
          listen() 函数只用于服务器端

2.4.5 accept()函数

          调用 accept() 将返回一个新的套接字文件描述符。新的套接字可以用于发送 (send()) 和接收 ( recv()) 数据。
[cpp] view plaincopy
  1. #include <sys/socket.h>;  
  2. int accept(int sockfd, void *addr, int *addrlen);   

          参数 sockfd :相当简单,是和 listen() 中一样的套接字描述符。
          参数 addr: 是个指向局部的数据结构 sockaddr_in 的指针。这是一个传出参数,可以用于测定那个地址在那个端口呼叫,这用于机器存在多个IP地址的情况。
          参数 addrlen :是个局部的整形变量,设置为 sizeof(struct sockaddr_in)。 
          返回值:同样,在错误时返回-1,并设置全局错误变量 errno。 

2.4.6 send()函数

[cpp] view plaincopy
  1. #include <sys/socket.h>;  
  2. int send(int sockfd, const void *msg, int len, int flags);   

          参数 sockfd :是你想发送数据的套接字描述符(或者是调用 socket() 或者是 accept() 返回的。
          参数 msg :是指向你想发送的数据的指针。
          参数 len :是数据的长度。 
          参数 flags :用于操作数据发送时TCP层的一些特性,如发送外带数据,通常设置为 0。
          返回值:send() 返回实际发送的数据的字节数--它可能小于要求发送的数目。 注意,有时候你告诉它要发送一堆数据可是它不能处理成功。它只是发送它可能发送的数据,然后希望你能够发送其它的数据。
          如果 send() 返回的数据和 len 不匹配,你就应该发送其它的数据。
          它在错误的时候返回-1,并设置 errno。

2.4.7 recv()函数

[cpp] view plaincopy
  1. #include <sys/socket.h>;  
  2. int recv(int sockfd, void *buf, int len, unsigned int flags);    

          参数 sockfd :是要读的套接字描述符。
          参数 buf :是要读的信息的缓冲。
          参数 len: 是缓 冲的最大长度。
          参数flags :用于控制读取行为的一些属性,如读取外带数据或者查询buf而不读取数据等,通常设置为0。
          返回值:recv() 返回实际读入缓冲的数据的字节数。或者在错误的时候返回-1, 同时设置 errno。

2.4.8 sendto()函数

[cpp] view plaincopy
  1. #include <sys/socket.h>;  
  2. int sendto(int sockfd, const void *msg, int len, unsigned int flags,  const struct sockaddr *to, int tolen);    

          sendto用于无连接数据报套接字,也就是UDP协议数据发送。
          参数 sockfd :是你想发送数据的套接字描述符(或者是调用 socket() 或者是 accept() 返回的。
          参数 msg: 是指向你想发送的数据的指针。
          参数 len: 是数据的长度。 
          参数 flags: 通常设置为0,UDP协议中是不存在外带数据的。
          参数 to :是个指向数据结构 struct sockaddr 的指针,包含了目的地的 IP 地址和端口信息。
          参数 tolen: 可以简单地设置为 sizeof(struct sockaddr)。 
          返回值:和函数 send() 类似,sendto() 返回实际发送的字节数(它也可能小于 你想要发送的字节数),或者在错误的时候返回 -1。

2.4.9 recvfrom()函数

          recvfrom用于无连接数据报套接字,也就是UDP协议数据接受。
          参数 sockfd: 是要读的套接字描述符。
          参数 buf :是要读的信息的缓冲。
          参数 len: 是缓 冲的最大长度。
          参数 flags :用于控制读取行为的一些属性,通常设置为0,同样由于UDP协议不支持外带数据,flags也无法设置为读取外带数据。

          参数 from: 是一个指向局部数据结构 struct sockaddr 的指针,它的内容是源机器的 IP 地址和端口信息。           

          参数 fromlen: 是个int 型的局部指针,它的初始值为 sizeof(struct sockaddr)。函数调用返回后,fromlen 保存着实际储存在 from 中的地址的长度。

          返回值:recvfrom() 返回收到的字节长度,或者在发生错误后返回 -1。

          send() 和 recv() 也可以用于UDP数据传输,只要在创建socket时指定协议类型为SOCK_DGRAM。

2.4.10 close()函数

[cpp] view plaincopy
  1. void close(sockfd);  
          参数 sockfd :是要关闭的套接字描述符
          close用于优雅的关闭socket连接,在TCP下它将按照标准TCP四次握手执行。它可以防止对套接字进行更多的数据读写,任何在另一端读写套接字的企图都将返回错误信息。

2.4.11 shutdown()函数

[cpp] view plaincopy
  1. int shutdown(int sockfd, int how);   

          它允许你将一定方向上的通讯或者双向的通讯(就象close()一 样)关闭,你可以使用:
          参数 sockfd: 是想要关闭的套接字文件描述符。
          参数 how :的值是下面的其中之 一:
   0 – 不允许接受
   1 – 不允许发送
         2 – 不允许发送和接受(和 close() 一样)
         shutdown() 成功时返回 0,失败时返回 -1(同时设置 errno。) 如果在无连接的数据报套接字中使用shutdown(),那么只不过是让 send() 和 recv() 不能使用。

2.4.12 getpeername()函数

[cpp] view plaincopy
  1. #include <sys/socket.h>;  
  2. int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);  

          函数 getpeername() 告诉在连接的流式套接字上谁在另外一边。一旦获得它们的地址,就可以使用 inet_ntoa() 或者 gethostbyaddr() 来打印或者获得更多的信息。
          参数 sockfd :是连接的流式套接字的描述符。
          参数 addr :是一个指向结构 struct sockaddr (或者是 struct sockaddr_in) 的指针,它保存着连接的另一边的 信息。
          参数 addrlen :是一个 int 型的指针,它初始化为 sizeof(struct sockaddr)。
          返回值:函数在错误的时候返回 -1,设置相应的 errno。

2.4.13 gethostname()函数

[cpp] view plaincopy
  1. #include <unistd.h>;  
  2. int gethostname(char *hostname, size_t size);  
[cpp] view plaincopy
  1.   

          它返回程序所运行的机器的主机名字。然后你可以使用 gethostbyname() 以获得机器的 IP 地址。

          参数 hostname: 是一个字符数组指针,它将在函数返回时保存主机名。

          参数 size:是hostname 数组的字节长度。

          返回值:函数调用成功时返回 0,失败时返回 -1,并设置 errno。

2.4.14 gethostbyname()函数

[cpp] view plaincopy
  1. #include <netdb.h>;  
  2. struct hostent *gethostbyname(const char *name);   

          它主要的功能是:给它一个容易记忆的某站点的地址,它转换出IP地址。
          返回值:它返回一个指向 struct hostent 的指针。这个数据结构如下:
[cpp] view plaincopy
  1. struct hostent {  
  2.    char *h_name;        //地址的正式名称  
  3.    char **h_aliases;        //空字节-地址的预备名称的指针。  
  4.    int h_addrtype;      //地址类型; 通常是AF_INET。  
  5.    int h_length;        //地址的比特长度  
  6.    char **h_addr_list;  //零字节-主机网络地址指针。网络字节顺序  
  7. };  
  8. #define h_addr h_addr_list[0]   //h_addr_list中的第一地址  

          gethostbyname() 成功时返回一个指向结构体 hostent 的指针,或者 是个空 (NULL) 指针。和以前不同,不设置errno,而用h_errno 设置错误信息。获取错误信息需要使用 herror()函数。

2.4.15 gethostbyaddr()函数

[cpp] view plaincopy
  1. #include <netdb.h>;  
  2. struct hostent gethostbyaddr(const char* addr, int len, int type);  

          参数 addr :指向网络字节顺序地址的指针。
          参数 len: 地址的长度,在AF_INET类型地址中为4。
          参数 type: 地址类型,应为AF_INET。
          返回值:它返回一个指向 struct hostent 的指针。hostent定义同上。
          gethostbyaddr() 成功时返回一个指向结构体 hostent 的指针,或者 是个空 (NULL) 指针。和以前不同,不设置errno,而用h_errno 设置错误信息。获取错误信息需要使用 herror()函数。

2.4.16 select()函数

          select() 函数可以同时监视多个套接字。它可以告诉你哪个套接字准备读,哪个又准备写,哪个套接字又发生了例外 (exception)。
[cpp] view plaincopy
  1. #include <sys/time.h>;  
  2. #include <sys/types.h>;  
  3. #include <unistd.h>;  
  4. int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);  

          参数 numfds :应该等于最高的文件描述符的值加1。
          参数 readfds:为可读文件集
          参数 writefds:为可写文件集
          参数 exceptfds:为异常文件集
          参数 timeout:为超时时间,数据结构 struct timeval 如下: 
[cpp] view plaincopy
  1. struct timeval {   
  2.    int tv_sec; /* seconds */   
  3.    int tv_usec; /* microseconds */   
  4. };   


          当函数 select() 返回的时候,readfds 的值修改为反映你选择的哪个文件描述符可以读。可以用下面讲到的宏 FD_ISSET() 来测试。 
          对这些集合进行操作系统定义了一些宏,每个集合类型都是 fd_set。
          FD_ZERO(fd_set *set) – 清除一个文件描述符集合 
          FD_SET(int fd, fd_set *set) - 添加fd到集合 
          FD_CLR(int fd, fd_set *set) – 从集合中移去fd 
          FD_ISSET(int fd, fd_set *set) – 测试fd是否在集合中 


2.4.17 poll()函数

[cpp] view plaincopy
  1. #include <poll.h>  
  2. int poll(struct pollfd fds[], nfds_t nfds, int timeout);  

          参数 fds:是一个struct pollfd结构类型的数组,用于存放需要检测其状态的Socket描述符;每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便;特别是对于socket连接比较多的情况下,在一定程度上可以提高处理的效率;这一点与select()函数不同,调用select()函数之后,select()函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中;因此,select()函数适合于只检测一个socket描述符的情况,而poll()函数适合于大量socket描述符的情况; 

         struct pollfd结构定义如下:

[cpp] view plaincopy
  1. truct pollfd {  
  2.          int fd; /*文件描述符*/  
  3.          short events; /* 等待的需要测试事件 */   
  4.          short revents; /* 实际发生了的事件,也就是返回结果 */  
  5. };  

          event和revents可为下列选项:

          POLLIN                            普通或优先级带数据可读
          POLLRDNORM              普通数据可读
          POLLRDBAND               优先级带数据可读
          POLLPRI                         高优先级数据可读
          POLLOUT                        普通数据可写
          POLLWRNORM              普通数据可写
          POLLWRBAND               优先级带数据可写
          POLLERR                        发生错误
          POLLHUP                        发生挂起
          POLLNVAL                       描述字不是一个打开的文件

          参数 nfds:nfds_t类型的参数,用于标记数组fds中的结构体元素的总数量;

          参数 timeout:是poll函数调用阻塞的时间,单位:毫秒;
          返回值:
          >0:数组fds中准备好读、写或出错状态的那些socket描述符的总数量;
          ==0:数组fds中没有任何socket描述符准备好读、写,或出错;此时poll超时,超时时间是timeout毫秒;换句话说,如果所检测的socket描述符上没有任何事件发生的话,那么poll()函数会阻塞timeout所指定的毫秒时间长度之后返回,如果timeout==0,那么poll() 函数立即返回而不阻塞,如果timeout==INFTIM,那么poll() 函数会一直阻塞下去,直到所检测的socket描述符上的感兴趣的事件发生是才返回,如果感兴趣的事件永远不发生,那么poll()就会永远阻塞下去;
          -1: poll函数调用失败,同时会自动设置全局变量errno;

          poll()函数与select()十分相似,当返回正值时,代表满足响应事件的文件描述符的个数,如果返回0则代表在规定时间内没有事件发生。如发现返回为负则应该立即查看 errno,因为这代表有错误发生。如果没有事件发生,revents会被清空,所以你不必多此一举。

1. 概述

             在《网络编程之 socket函数 (一)》 中所讲到的所有socket函数并不涉及设置socket属性,而只是定义了客户端和服务器端的socket流程。服务器端和客户端的使用流程如下:
            客户端:
                 socket()->connect()->send()/recv()->close()

            服务器端:
                 socket()->bind()->listen()->accept()->close()
                                                                      |
                                                                      |  ->send()/recv()->close()
                                                                      |
                                                                      |  ->select()->send()/recv()->close()


            下面介绍的一些函数则涉及socket的属性设置。事实上socket的不同属性对socket的行为影响巨大。


2. 函数介绍

            控制套接字属性的函数包括:
            1. getsockopt(): 用于查询指定的套接字一个特定的套接字选项的当前值。
            2. setsockopt(): 用于为指定的套接字设定一个特定的套接字选项。
            3. fcntl(): 向打开的文件fd发送命令,更改其属性。在linux下所有的一切都被认为是文件,socket也不例外。可以认为fcntl()是ioctl()的一个子集。
            4. octl(): 此函数像个杂货铺,对设备的控制通常都通过这个函数来实行,具体对设备的操作方式取决于设备驱动程序的编写。fcntl()和ioctl()的区别如同fread()和read()函数的关系。
            5. ioctlsocket(): 此函数只能用于windows平台,相当于linux下套接口函数ioctl()的一个子集。

2.1 setsockopt()

[cpp] view plaincopy
  1. #include <sys/types.h>  
  2. #include <sys/socket.h>  
  3. int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);  
  4. int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);  

            参数sock: 将要被设置或者获取选项的套接字。
            参数level: 选项所在的协议层。level指定控制套接字的层次.可以取三种值: 
                SOL_SOCKET: 通用套接字选项. 
                IPPROTO_IP:  IP选项. 
                IPPROTO_TCP: TCP选项. 
            参数optname:需要访问的选项名。
            参数optval:对于getsockopt(),指向返回选项值的缓冲。对于setsockopt(),指向包含新选项值的缓冲。
            参数optlen:对于getsockopt(),作为入口参数时,选项值的最大长度。作为出口参数时,选项值的实际长度。对于setsockopt(),现选项的长度。


            SOL_SOCKET通用套接字选项,可以认为是应用socket应用层的选项,包括以下各选项:
======================================================================== 
选项名称        说明                  数据类型 
======================================================================== 
SO_ACCEPTCONN                 套接口正在用listen()监听                                     int
SO_BROADCAST                    允许发送广播数据                                                 int 
SO_DEBUG                              允许调试                                                                int 
SO_DONTROUTE                    不查找路由,禁止选径                                         int 
SO_ERROR                              获得套接字错误                                                    int 
SO_KEEPALIVE                       保持连接                                                                int 
SO_LINGER                              延迟关闭连接                                                       struct linger 
SO_OOBINLINE       带外数据放入正常数据流                                     int 
SO_RCVBUF                             接收缓冲区大小                                                   int 
SO_SNDBUF                             发送缓冲区大小                                                   int 
(SO_RCVLOWAT)                    接收缓冲区下限                                                    int 
(SO_SNDLOWAT)                   发送缓冲区下限                                                    int 
(SO_RCVTIMEO)                     接收超时                                                                struct timeval 
(SO_SNDTIMEO)                     发送超时                                                                struct timeval 
SO_REUSERADDR                 允许重用本地地址和端口                                      int 
SO_TYPE                                  获得套接字类型                                                    int 
SO_BSDCOMPAT                   与BSD系统兼容                                                    int 

======================================================================== 

           注:上述括号中的选项表示window下不支持的但linux支持之选项



            IPPROTO_IP选项,可以认为是操作系统为应用程序开的接口,用于控制底层IP层的一些特性,包括以下各列:
======================================================================== 
选项名称        说明                  数据类型 
======================================================================== 
IP_HDRINCL                        在数据包中包含IP首部                                          int 
(IP_OPTINOS)                     IP首部选项                                                            int 
IP_TOS                                  服务类型 
IP_TTL                                   生存时间                                                               int 
======================================================================== 

           注:上述括号中的选项表示window下不支持的但linux支持之选项



            IPPROTO_TCP选项,可以认为是操作系统为应用程序开的接口,用于控制底层TCP层的一些特性,包括以下各列:
======================================================================== 
选项名称        说明                  数据类型 
======================================================================== 
(TCP_MAXSEG)                  TCP最大数据段的大小                                        int 
TCP_NODELAY                  不使用Nagle算法                                                 int 

======================================================================== 

           注:上述括号中的选项表示window下不支持的但linux支持之选项


            返回值(Linux):
            成功执行时,返回0。失败返回-1,errno被设为以下的某个值   
               EBADF:sock不是有效的文件描述词
               EFAULT:optval指向的内存并非有效的进程空间
               EINVAL:在调用setsockopt()时,optlen无效
               ENOPROTOOPT:指定的协议层不能识别选项
               ENOTSOCK:sock描述的不是套接字

            返回值(Windows):
               WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。
               WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。
               WSAEFAULT:optlen参数非法。
               WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。
               WSAENOPROTOOPT:未知或不支持选项。其中,SOCK_STREAM类型的套接口不支持SO_BROADCAST选项,SOCK_DGRAM类型的套接口不支持SO_ACCEPTCONN、SO_DONTLINGER 、SO_KEEPALIVE、SO_LINGER和SO_OOBINLINE选项。
              WSAENOTSOCK:描述字不是一个套接口。

            注意:
            当设置TCP套接口接收缓冲区的大小时,函数调用顺序是很重要的,因为TCP的窗口规模选项是在建立连接时用SYN与对方互换得到的。对于客户,SO_RCVBUF选项必须在connect之前设置;对于服务器,SO_RCVBUF选项必须在listen前设置。


2.2 fcntl()

[cpp] view plaincopy
  1. #include <unistd.h>  
  2. #include <fcntl.h>  
  3. int fcntl(int fd, int cmd);  
  4. int fcntl(int fd, int cmd, long arg);  
  5. int fcntl(int fd,int cmd, struct flock *lock);  

            参数fd: 是被参数cmd操作的描述符.

            参数cmd: 对描述符操作的命令码。有如下选项:

==============================================================

            FD_DUPFD                                                                     复制文件描述符
            F_GETFD或者F_SETFD                                                获得/设置文件描述符
            F_GETFL或者F_SETFL                                                获得/设置文件状态值
            F_GETOWNF_SETOWN                                          获得/设置异步I/O所有权
            F_GETLK或者F_SETLK或者F_SETLKW                  获得/设置文件记录锁

            F_GETLEASE或者F_SETLEASE                                获得/设置文件租约

===============================================================


            参数arg: 参数cmd对应的命令参数,有些命令存在此arg,有些不存在。

            1. F_GETFL和F_SETFL的标志如下面的描述:
                O_NONBLOCK        非阻塞I/O;如果read(2)调用没有可读取的数据,或者如果write(2)操作将阻塞,read或write调用返回-1和EAGAIN错误
                O_APPEND              强制每次写(write)操作都添加在文件大的末尾,相当于open(2)的O_APPEND标志
                O_DIRECT               最小化或去掉reading和writing的缓存影响.系统将企图避免缓存你的读或写的数据.如果不能够避免缓存,那么它将最小化已经被缓存了的数据造 成                                                       的影响.如果这个标志用的不够好,将大大的降低性能
                O_ASYNC                当I/O可用的时候,允许SIGIO信号发送到进程组,例如:当有数据可以读的时候

             注意:
             在修改文件描述符标志或文件状态标志时必须谨慎,先要取得现在的标志值,然后按照希望修改它,最后设置新标志值。不能只是执行F_SETFD或F_SETFL命令,这样会关闭以前设置的标志位。

              2. F_GETLK/F_SETLK/F_SETLKW 命令及标志如下面的描述:
                   F_GETLK          通过第三个参数arg(一个指向flock的结构体)取得第一个阻塞lock description指向的的锁.取得的信息将覆盖传到fcntl()的flock结构的信息.如果没有发现能够阻止本次锁(flock)生成的锁,这 个结构将不被改变,除非锁的类型被设置成F_UNLCK.   
                   F_SETLK          按照指向结构体flock的指针的第三个参数arg所描述的锁的信息设置或者清除一个文件segment锁.F_SETLK被用来实现共享(或读)锁 (F_RDLCK)或独 占(写)锁(F_WRLCK),同样可以去掉这两种锁(F_UNLCK).如果共享锁或独占锁不能被设置,fcntl()将立即返 回EAGAIN.
                   F_SETLKW          除了共享锁或独占锁被其他的锁阻塞这种情况外,这个命令和F_SETLK是一样的.如果共享锁或独占锁被其他的锁阻塞,进程将等待直到这个请求能够   完成. 当fcntl()正在等待文件的某个区域的时候捕捉到一个信号,如果这个信号没有被指定SA_RESTART,fcntl将被中断.

               当一个共享锁被set到一个文件的某段的时候,其他的进程可以set 共享锁到这个段或这个段的一部分.共享所阻止任何其他进程set独占锁到这段保护区域的任何部分.如果文件描述符没有以读的访问方式打开的话,共享锁的设置请求会失败.
               独占锁阻止任何其他的进程在这段保护区域任何位置设置共享锁或独占锁.如果文件描述符不是以写的访问方式打开的话,独占锁的请求会失败。

              参数flock的指针:
              flcok结构定义如下:
[cpp] view plaincopy
  1. struct flcok   
  2. {   
  3.        short int l_type; /* 锁定的状态*/  
  4.        //这三个参数用于分段对文件加锁,若对整个文件加锁,则:l_whence=SEEK_SET,l_start=0,l_len=0;  
  5.        short int l_whence;/*决定l_start位置*/   
  6.        off_t l_start; /*锁定区域的开头位置*/   
  7.        off_t l_len; /*锁定区域的大小*/  
  8.        pid_t l_pid; /*锁定动作的进程*/   
  9. };  

              其中l_type 有三种状态: 
                F_RDLCK 建立一个供读取用的锁定 
                F_WRLCK 建立一个供写入用的锁定 
                F_UNLCK 删除之前建立的锁定

              l_whence 也有三种方式: 
                SEEK_SET 以文件开头为锁定的起始位置。 
                SEEK_CUR 以目前文件读写位置为锁定的起始位置 
                SEEK_END 以文件结尾为锁定的起始位置。 

              返回值:
              与命令有关。如果出错,所有命令都返回-1,如果成功则返回某个其他值。
              下列三个命令有特定返回值:F_DUPFD,F_GETFD,F_GETFL以及F_GETOWN。
              第一个返回新的文件描述符,
              第二个返回相应标志,
              最后一个返回一个正的进程ID或负的进程组ID。

               fcntl是控制文件属性的专用命令,和socket相关的选项,用的比较多的可能就是设置阻塞或者非阻塞socket了。如下:
[cpp] view plaincopy
  1. fcntl(fd, F_SETFL, O_NONBLOCK)   

2.3 ioctl()

[cpp] view plaincopy
  1. #include<sys/ioctl.h>  
  2. int ioctl(int handle, int cmd, char* argp);  

              参数handle: 是被参数cmd操作的描述符.
              参数cmd: 对描述符操作的命令码。有如下选项:
              参数argp: 参数cmd对应的命令参数,有些命令存在argp,有些不存在

              cmd和argp的关系如下:
============================================================================================
类别Request 说明数据类型
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
套接口                    SIOCATMARK                       是否位于带外标记                                                int
                                SIOCSPGRP                         设置套接口的进程ID 或进程组ID                        int
                                SIOCGPGRP                         获取套接口的进程ID 或进程组ID                        int
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
文件                        FIONBIO                                 设置/ 清除非阻塞I/O 标志                                     int
                                FIOASYNC                             设置/ 清除信号驱动异步I/O 标志                         int
                                FIONREAD                             获取接收缓存区中的字节数                                int
                                FIOSETOWN                         设置文件的进程ID 或进程组ID                            int
                                FIOGETOWN                         获取文件的进程ID 或进程组ID                           int
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
接口                        SIOCGIFCONF                       获取所有接口的清单                                           struct ifconf
                                SIOCSIFADDR                       设置接口地址                                                       struct ifreq
                                SIOCGIFADDR                       获取接口地址                                                       struct ifreq
                                SIOCSIFFLAGS                      设置接口标志                                                       struct ifreq
                                SIOCGIFFLAGS                      获取接口标志                                                       struct ifreq
                                SIOCSIFDSTADDR               设置点到点地址                                                   struct ifreq
                                SIOCGIFDSTADDR               获取点到点地址                                                   struct ifreq
                                SIOCGIFBRDADDR              获取广播地址                                                       struct ifreq
                                SIOCSIFBRDADDR              设置广播地址                                                       struct ifreq
                                SIOCGIFNETMASK               获取子网掩码                                                       struct ifreq
                                SIOCSIFNETMASK               设置子网掩码                                                       struct ifreq
                                SIOCGIFMETRIC                   获取接口的测度                                                   struct ifreq
                                SIOCSIFMETRIC                   设置接口的测度                                                   struct ifreq
                                SIOCGIFMTU                         获取接口MTU                                                       struct ifreq
                                SIOCxxx                                  (还有很多取决于系统的实现)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
ARP                        SIOCSARP                           创建/修改ARP 表项                                               struct arpreq         
                                SIOCGARP                           获取ARP 表项                                                       struct arpreq
                                SIOCDARP                           删除ARP 表项                                                       struct arpreq
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
路由                        SIOCADDRT                         增加路径                                                               struct rtentry
                                SIOCDELRT                         删除路径                                                               struct rtentry
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
流                              I_xxx
-----------------------------------------------------------------------------------------------------------------------------------------------------------------

2.4 ioctlsocket()

[cpp] view plaincopy
  1. #include <winsock.h>  
  2. int PASCAL FAR ioctlsocket( SOCKET s, long cmd, u_long FAR* argp);  

            参数s:一个标识套接口的描述字。
            参数cmd:对套接口s的操作命令。有如下选择:
                FIONBIO: 允许或禁止套接口s的非阻塞模式。argp指向一个无符号长整型,如允许非阻塞模式则非零,如禁止非阻塞模式则为零。当创建一个套接口时,它就处于阻 塞模式(也就是说非阻塞模式被禁止)。这与BSD套接口是一致的。WSAAsynSelect()函数将套接口自动设置为非阻塞模式。如果已对一个套接口进行了WSAAsynSelect() 操作,则任何用ioctlsocket()来把套接口]重新设置成阻塞模式的试图将以WSAEINVAL失败。为了把套接口重新设置成阻塞模式,应用程序必须首先用WSAAsynSelect()调用(IEvent参数置为0)来禁止WSAAsynSelect()。
            FIONREAD:确定套接口s自动读入的数据量。argp指向一个无符号长整型,其中存有ioctlsocket()的返回值。如果s是SOCKET_STREAM类型,则FIONREAD返回在一次recv()中所接收的所有数据量。这通常与套接口中排队的数据总量相同。如果S是SOCK_DGRAM 型,则FIONREAD返回套接口上排队的第一个数据报大小。

            SIOCATMARK:确实是否所有的带外数据都已被读入。这个命令仅适用于SOCK_STREAM类型的套接口,且该套接口已被设置为可以在线接收带外数据 (SO_OOBINLINE)。如无带外数据等待读入,则该操作返回TRUE真。否则的话返回FALSE假,下一个recv()或recvfrom()操作将检索“标记”前一些或所有数据。应用程序可用   SIOCATMARK操作来确定是否有数据剩下。如果在“紧急”(带外)数据[前有常规数据,则按序接收这些数据(请注意,recv()和recvfrom() 操作不会在一次调用中混淆常规数据与带外数]据)。argp指向一个BOOL型数,ioctlsocket()在其中存入返回值。

              参数argp:指向cmd命令所带参数的指针。

0 0
原创粉丝点击