首页 > 代码库 > 详解基本TCP套接字函数

详解基本TCP套接字函数

以下讲解基本TCP套接字函数。
1、socket 函数
   指定期望的通信协议类型。
   
  1. #include <sys/types.h> /* See NOTES */
  2.  #include <sys/socket.h>
  3.  int socket(int domain, int type, int protocol);
  4.                                        返回:若成功则为非负描述符,出错则为-1。
 
参数说明: 
 domain:   指明协议族,也称为协议域,是一个常值。
           AF_INET           IPv4 协议
           AF_INET6           IPv6 协议
           AF_LOCAL/AF_UNIX       Unix协议域
           AF_ROUTE               路由套接字
           AF_KEY                 密匙套接字
     
 type:    指明套接字的类型。
           SOCK_STREAM           字节流套接字
           SOCK_DGRAM            数据报套接字
           SOCK_SEQPACKET        有序分组套接字
           SOCK_RAW              原始套接字
     
 protocol: 指明协议类型。一般为0,以选择给定的domain和type组合的系统默认值。
           IPPROTO_TCP           TCP传输协议
           IPPROTO_UDP           UDP传输协议
           IPPROTO_SCTP          SCTP传输协议
函数描述:
    socket 函数在成功时返回一个小的非负整数值,与文件描述符类似,我们称它为套接字
    描述符,简称 sockfd。为了得到这个套接字描述符,我们只是指定了协议族(IPv4、IPv6
    或Unix)和套接字类型(字节流、数据报或原始套接字)。我们并没有指定本地跟远程的
    协议地址。
     
2、connect 函数 
   TCP 客户用 connect 函数来与 TCP 服务器建立连接。 
   
  1. #include <sys/socket.h> 
  2.  int connect( int sockfd, const struct sockaddr *servaddr, socklen_t addrlen );
  3.                                                  返回:若成功则为0,出错则为-1。
     
参数说明:
    sockfd:            由 socket 函数返回的套接字描述符。     
    servaddr、addrlen:指向一个套接字地址结构的指针和该结构的大小。套接字地址结构
                       必须含有服务器的IP地址和端口号。
函数描述:
    客户在调用 connect 函数前并不一定得调用 bind 函数,如果需要的话,内核会确定源
    IP地址,并选择一个临时端口作为源端口。所以在客户进程中的套接字一般只需指明客户
    所要连接的服务器的IP跟端口号。
 
    如果是 TCP 套接字,调用 connect 函数将激发 TCP 的三路握手。而且仅在连接成功或
    出错时才返回。其中出错的情况有如下几种:
    1-> TCP 客户没有收到 SYN 分节的响应。
    2-> TCP 服务器对客户的 SYN 分节的响应是 RST 。
    3-> 客户发出的 SYN 分节在某个路由器器上发生了错误。
 
    若 connect 调用失败则该套接字不再可用,必须关闭,我们不能对这样的套接字再次执行
    connect 函数。 
     
3、bind 函数     
   将一个本地协议地址赋予一个套接字。对于网际网协议,协议地址是32位的IPv4地址和128
   位的IPv6地址与16位的TCP或UDP端口号的组合。bind 函数主要用于服务器端,用来指定本地
   主机的哪个网络接口(IP,可以是INADDR_ANY,表示本地主机的任一网络接口)可以接受客户
   端的请求,和指定端口号(即开启的等待客户来连接的进程)。
 
  1. #include <sys/socket.h> 
  2.  int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
  3.                                                   返回:若成功则为0,出错则为-1。
参数说明:
    sockfd:          socket 函数返回的套接字描述符。
    myaddr、addrlen:指向一个套接字地址结构的指针和该结构的大小。
 
函数描述:
    对于 TCP ,调用 bind 函数可以指定一个端口号,或指定一个IP地址,也可以两者都指定,还
    可以两者都不指定。
 
    服务器在启动时捆绑它们众所周知的端口号(如何捆绑?)。如果一个TCP客户或服务器未曾调用
    bind捆绑一个端口,当调用 connect 或 listen 时,内核就要为相应的套接字选择一个临时端口。
    让内核来选择临时端口对于TCP客户来说是正常的,除非应用需要一个预留端口。然而对于TCP服务
    器来说却极为罕见,因为服务器是通过它们的众所周知的端口号来被大家认识的。
 
    进程可以把一个特定的IP捆绑到它的套接字上,不过这个IP地址必须属于其所在主机的网络接口之
    一(对于TCP服务器)。对于TCP客户,这就为在该套接字上发送的IP数据报指派了源IP地址(服务器
    源地址)。对于TCP服务器,这就限定该套接字只接收那些目的地为这个IP地址的客户连接。TCP套接
    字通常不把IP地址捆绑到它的套接字上。当连接套接字时,内核将根据所用外出网络接口来选择源IP
    地址,而所用外出端口则取决于到达服务器所需的路径。如果TCP服务器没有把IP地址捆绑到它的套接
    字上,内核就会把发送的SYN的目的IP地址作为服务器的源IP地址(即服务器IP等于INADDR_ANY的情 
    况)。
    实际上客户的源IP地址就是服务器的目的地址,服务器的源IP地址就是客户的目的地址,说到底也就只
    存在两个IP地址:客户IP跟服务器IP。
 
4、listen 函数
  1. #include <sys/socket.h> 
  2.     int listen(int sockfd, int backlog);
  3.                               返回:若成功则为0,出错则为-1。
函数描述:         
    listen 函数仅由 TCP 服务器调用,它做两件事情。
    (1)把一个未连接的套接字(主动)转换成一个被动套接字,指示内核应该接受指向该套接字的连接请
       求。
    (2)backlog 参数规定了内核应该为相应套接字排队的最大连接数。其中内核始终为监听套接字维护两个
       队列。
       (1)未完成连接队列,每个SYN分节对于其中一项:
           已由某个客户发出并到达服务器,而服务器正在等待待完成的TCP三路握手过程。这些套接字处
           于SYN_RCVD状态。
       (2)已完成连接队列
          每个已完成TCP三路握手过程的客户对应其中一项。这些套接字处于ESTABLISHED状态。
          backlog 就是这两个队列和的最大值。
    在三路握手完成之后,但在服务器调用 accept 之前到达的数据应由 TCP 服务器排队,最大数据量为
    相应已连接套接字的接收缓冲区的大小。
 
5、accept 函数
    accept 函数由 TCP 服务器调用,用于从已完成连接队列头返回下一个已完成连接。如果已完成队列为
    空,那么进程被投入睡眠(假设套接字为默认的阻塞方式)。
  1. #include <sys/socket.h>
  2.     int accept(int sockfd ,struct sockaddr *cliaddr, socklen_t *addrlen); 
  3.                         返回:若成功则为非负已连接描述符和对端的IP和端口号,出错则为-1。
参数说明:
    cliaddr、addrlen 用来返回已连接的对端进程(客户)的协议地址 。调用前,我们将由 *addrlen 所
    引用的整数值置为由cliaddr所指的套接字地址结构的长度,返回时,该整数值即为内核存放在该套接字
    地址机构内的确切字节数。
函数描述:
    如果 accept 调用成功,那么其返回值是由内核自动生成的一个全新描述符,代表着与所返回客户的TCP
    连接。在讨论 accept 函数时,我们称它的第一个参数为监听套接字描述符(由 socket 创建,随后用
    作bind 和 listen 的第一个参数的描述符),称它的返回值为已连接套接字描述符。区分这两个套接字
    非常重要。一个服务器通常仅仅创建一个监听套接字,它在服务器的生命期内一直存在。内核为每个服
    务器进程接受的客户连接创建一个已连接套接字(也就是说对于它的TCP三路握手过程已经完成)。当服
    务器完成对某个连接客户的服务时,相应的已连接套接字就要被关闭。
  
6、fork 函数
    是 Unix 中派生新进程的唯一方法。
 
  1. #include<unistd.h>
  2.     pid_t fork(void); 
  3.            返回:在子进程中为0,在父进程中为子进程ID,出错则为-1.
     
函数描述:
    fork 调用一次却返回两次。它在调用进程(父进程)中返回一次,返回值是新派生进程(子进程)
    的进程ID号;在子进程又返回一次,返回值为0。因此,返回值本身告知当前进程是子进程还是父
    进程。
    fork 在子进程返回0而不是父进程的进程ID的原因在于:任何子进程只有一个父进程,而且子进程
    总是可以通过调用 getppid 取得父进程的进程ID。相反,父进程可以有许多子进程。而且无法通过
    函数调用来获取子进程的进程ID。如果父进程想要跟踪所有子进程的进程ID,那么它必须记录每次
    fork 调用的返回值。当前进程可以通过 getpid 系统调用来获得自己的进程ID。    
    父进程中调用 fork 之前打开的所有描述符在 fork 返回之后由子进程共享,数据段会得到一份拷贝
    而不是共享。
    fork 调用的两个典型用法:
    (1)一个进程创建一个自身的副本,这样每个副本都可以在另一个副本执行其他任务的同时处理各自的
    操作。这是网络服务器的典型用法。
    (2)一个进程想要执行另一个程序。既然创建新进程的唯一方法是调用 fork ,该进程于是首先调用
     fork创建一个自身的副本,然后其中一个副本调用 exec 把自身替换成新的程序。这是诸如 shell 之
     类程序的典型用法。
    
6、exec 函数族
    一个 exec 函数可以把当前进程替换成一个新进程,新进程由 path 或 file 参数指定。    
  1. #include <unistd.h>
  2.     extern char **environ;
  3.     int execl(const char *path, const char *arg, ...);
  4.     int execlp(const char *file, const char *arg, ...);
  5.     int execle(const char *path, const char *arg,
  6.                   ..., char * const envp[]);
  7.     int execv(const char *path, char *const argv[]);
  8.     int execvp(const char *file, char *const argv[]);
  9.  int execve(const char *path, char *const argv[], char *const envp[]);
  10.                                       返回:成功则不返回,出错则为-1。
              
函数描述:
    这 6 个 exec 函数之间的区别在于:(a)待执行的程序的文件名是由文件名(file)指定还是由路径名
    (path)指定;(b)新程序的参数是一一列出还是由一个指针数组来引用;(c)把调用进程的环境传递给新
    程序还是给新程序指定新的环境。这些函数只在出错时才返回调用者。否则,控制权将传递给新程序的
    起始点。通常就是 main 函数。一般来说 execve 是内核中的系统调用,其他 5 个都是调用 execve
    的库函数。
 
7、getsockname 和 getpeername 函数
  1. #include <sys/socket.h>
  2.     int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);
  3.     int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);
  4.                                                             返回:成功则不返回,出错则为-1。
    这两个函数返回与某个套接字关联的本地协议地址(getsockname),或者返回与某个套接字关联的外地协
    议地址(getpeername)。

详解基本TCP套接字函数