• 第四章 基本TCP套接字编程


    //1.
    socket:
    第一个参数:指明协议族
    		  常用的如下:
    		  AF_INET:IPv4
    		  AF_INET6:IPv6
    
    第二个参数:指明套接字类型
    		  SOCK_STREAM:字节流套接字
    		  SOCK_DGRAM:数据报套接字
    		  SOCK_SEQPACKET:有序分组套接字,用于SCTP中
    		  SOCK_RAW:原始套接字
    
    第三个参数:某个协议值类型常量,或设为0,以选择所给定的第一个参数和第二个参数组合的系统默认值
    		  IPPROTO_TCP:TCP传输协议
    		  IPPROTO_UDP:UDP传输协议
    		  IPPROTO_SCTP:SCTP传输协议
    
    函数成功返回一个小的非负整数值,类似于文件描述符,称为套接字描述符,失败返回-1
    #define INVALID_SOCKET  (SOCKET)(~0)	//定义于WinSock2.h中
    
    socket 函数中前两个参数的组合 (空白项为无效组合,非空白项皆为有效组合)
    
    //2.
    TCP客户用 connect 函数来建立于TCP服务器的连接
    客户在调用 connect 前不必非得调用 bind ,因为如果需要的话,内核会确定源IP地址,并选择一个临时端口作为源端口
    如果是TCP套接字,调用 connect 会触发TCP的三路握手过程,仅在建立成功或出错时才返回,其中出错有如下几种情况:
    (1):若TCP客户没有收到SYN分节的响应,则会返回错误。举例来说:调用 connect 函数时,4.4BSD内核发出一个SYN,若无响应,
    	等待6s后在发送一个,若仍无响应,则等待24s在发送一个。若总共等待75s后仍未收到响应则返回错误
    (2):若对客户的SYN响应时RST(表示复位),则表明该服务器主机在我们指定的端口上没有进程等待其连接,客户收到RST后将立刻返回错误
    	RST是TCP在发生错误时候发送的TCP分节,产生RST的三个条件:
    	A:目的地为某端口的SYN到达,然而该端口上没有正在监听的服务器
    	B:TCP想取消一个已有连接
    	C:TCP接收到一个根本不存在的连接上的分节
    (3):若客户发出的SYN在中间的某个路由器上引发一个 destination unreachable (目的地不可达) ICMP错误,则客户主机保存该消息,
    	继续按照(1)中规则发送SYN,若规定时间内仍未接收到响应,则返回错误。以下两种情况也有可能:
    	A:按照本地系统的转发表,根本没有到达远程系统的路径
    	B:connect 调用根本不等待就返回
    
    若 connect 失败,则该套接字不再可用,必须关闭套接字,不能在对这个套接字再次调用 connect
    备注:若 connect 失败,继续使用此套接字继续 connect ,当服务器 listen 后,客户端调用的 connect 是可用连接上的(测试可用)
    	 但是,为了防止意外,还是以书中所讲为准
    
    connect 函数成功返回0,失败返回-1
    #define SOCKET_ERROR            (-1)	//定义于Winsock2.h中
    
    
    //3.
    bind 函数把一个本地协议赋予一个套接字。对于网际网协议,协议地址是32位IPv4地址或者128为IPv6地址和16位号端口的组合。
    对于TCP,调用 bind 函数可以指定一个端口号或指定一个地址,也可两者皆指定或皆不指定:
    A:如果一个TCP客户或服务器未曾调用 bind 捆绑一个端口,当调用 connect 或者 listen 时,内核就要为相应的套接字选择一个临时端口
      让内核来选择临时端口,对于TCP客户来说是正常的,然而对于TCP服务器来说是罕见的,因为服务器是通过端口被认识的
    B:进程也可以讲本地的一个IP绑定到套接字上,对于TCP客户来说,这就为该套接字上发送的IP数据报指派了源IP地址。
      对于TCP服务器,这就限定该套接字只接受那些目的地为这个地址的客户连接
      TCP客户通常不把IP地址绑定到他的套接字上。当连接套接字时,内核将根据所用外出网络接口确定源IP地址,而所用外出接口则取决于到达服务器所需的路径
      如果TCP服务器没有把IP地址捆绑到他的套接字上,那么内核就把客户发送的SYN的目的IP地址作为服务器的源IP地址
    
    bind 函数指定要捆绑的IP地址或端口号产生的结果
    
    如果指定端口号为0,那么内核就在 bind 被调用时选择一个临时端口,如果指定IP地址为通配地址,那么内核将等到套接字已连接(TCP)或在已连接上发出数据报(UDP)
    时,才选择一个本地地址
    系统使用 INADDR_ANY(IPv4) 和 in6addr_any(IPv6) 来标示通配地址
    虽然 INADDR_ANY 的值一般为0,但是为了统一起见,仍最好使用 htonl 对其进行字节序设置
    为了得到内核选择的临时端口值,可以使用 getsockname 来返回协议地址
    
    
    //4.
    listen 函数仅由TCP服务器调用,它做两件事情:
    (1):当 socket 函数创建一个套接字时,他被假设为一个主动套接字,也就是说,他是一个将调用 connect 发起连接的客户套接字。 listen 函数把一个未连接的套接字
    	转换成一个被动套接字,指示内核应接受指向该套接字的连接请求。 调用 listen 将导致套接字从 CLOSED 转换为 LISTEN 状态
    (2):第二个参数规定了内核应该为相应套接字排队的最大连接个数
    
    内核为任意一个给定的监听套接字维护两个队列:
    (1):未完成连接队列(incomplete connection queue),每个这样的SYN分节对应其中一项:已由某个客户发出并到达服务器,而服务器正在等待完成相应的TCP三路握手过程,
    	这些套接字处于 SYN_RCVD 状态
    (2):已完成连接队列(completed connection queue),每个已完成TCP三路握手过程的客户对应其中的一项。这些套接字处于 ESTABLISHED 状态
    
    TCP为监听套接字维护的两个队列:
    
    每当在未完成连接队列中创建一项,来自监听套接字的参数就复制到即将建立的队列中。连接的创建机制是完全自动的,无需服务器插手
    
    TCP三路握手和监听套接字的两个队列
    
    当来自客户的SYN到达时,TCP在未完成连接队列中创建一个新项,然后响应三路握手的第二个分节:服务器的SYN响应,其中捎带过去对客户SYN的ACK。这一项一直保留在未完成
    队列中,直到三路握手的第三个分节(客户对服务器SYN的ACK)到达或者该项超时为止。如果三路握手正常完成,该项就从未完成连接队列转移到已完成连接队列的队尾。当进程调用
    accept 时,已完成队列中的队头将被返回给进程,或者改队列为空,那么进程将被投入睡眠,直到TCP在该队列中放入一项才唤醒
    
    关于上述两个队列,需注意一下几点:
    A:listen 函数的第二个参数曾被规定为这两个队列总和的最大值
    B:源自Berkeley的实现给 listen 的第二个参数一个模糊因子:把他乘以1.5得到未处理队列最大长度
    C:不要把 listen 第二个参数定义为0,不同的实现对此有不同的解释
    D:在三路握手正常完成前提下(没有丢失分节,没有发生重传),未完成连接队列的任何一项在其中的存留时间就是一个RTT,而RTT的值取决于特定的客户与服务器。
      对于一个Web服务器,许多客户与单个服务器之间的中值RTT为187ms
    E:当一个客户的SYN到达时,若这些队列是满的,TCP就忽略该分节,也就是不发送RST。这么做是因为:这种情况是暂时的,客户TCP将重发SYN,期望不就就能在这些队列中找到可用空间,
      要是服务器立即响应一个RST,客户的 connect 就会立即返回一个错误,强制进程处理这种情况,而不是让TCP的重传机制来处理。客户无法区分响应SYN的RST是因为该端口无服务还是
      该端口有服务,但是他的队列满了。有些实现在这些队列满时的确发送了RST,由于上述原因,这种做法是不正确的。
    F:在三路握手完成后,但在服务器调用 accept 前到达的数据应由服务器TCP排队,最大数据量为相应已连接套接字的接收缓冲区大小
    
    备注:listen 的第二个参数不是支持的连接数目,而是一个与其两个队列相关的值,即使 listen 的第二个参数设为1,也不会影响服务器连接客户端数目的上限
    
    
    //5.
    accept 函数由TCP服务器调用,用于从已完成队列队头返回下一个已完成连接,如果已完成连接队列为空,那么进程将投入睡眠(假定套接字为阻塞方式)
    accept 函数调用成功,那么其返回值是由内核自动生成的一个全新的套接字,代表与客户的TCP连接
    accept 函数的第二个与第三个参数可以为空,代表服务器并不关心客户的身份
    
    
    //6.
    通常 UNIX 的 close 函数也用来关闭套接字,并终止TCP连接
    close 一个TCP套接字的默认行为是将该套接字标记为已关闭的,然后立即返回到调用进程。该套接字描述符不能再由调用进程使用,也就是说不能再作为 read 和 write 的第一个参数。
    然而TCP将尝试发送已排队等待发送的数据到对端的任何数据,发送完毕后发生的是正常的TCP连接终止序列
    
    
    //7.
    getsockname 函数用于获取与某个套接字关联的本地协议地址 
    getpeername 函数用于获取与某个套接字关联的外地协议地址
    

      

  • 相关阅读:
    解决Hash冲突的几种方式
    深入理解JDK8中的HashMap
    JAVA中两个int类型的变量在不借助第三个变量的情况下完成值的互换
    Feign调用时读取超时(Read timed out executing GET)解决
    windows上Jenkins安装及其配置
    windows下查看端口被占用及处理
    flutter IOS模拟器无法弹出软键盘
    Android-ION内存管理简介
    移动GPU分类/百科
    ApiGen4.1 windows安装教程
  • 原文地址:https://www.cnblogs.com/szn409/p/7801266.html
Copyright © 2020-2023  润新知