zoukankan      html  css  js  c++  java
  • 详解基本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)。
  • 相关阅读:
    本地计算机 上的 Redis Server 服务启动后停止
    RabbitMQ 命令行
    mysql ORDER BY 中文出现错误问题
    使用javascript纯前端导出excel
    软件测试概念学习
    excel控件只为简单写入数据表--github找到ExcelUtil笔记
    快速创建Spring web项目
    PQGrid商业化的表格组件
    mybatis传入参数为0被误认为是空字符串的解决方法
    MyBatis like报错
  • 原文地址:https://www.cnblogs.com/delmory/p/3989398.html
Copyright © 2011-2022 走看看