zoukankan      html  css  js  c++  java
  • 第四章 基本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 函数用于获取与某个套接字关联的外地协议地址
    

      

  • 相关阅读:
    命令拷屏之网络工具
    PHP 设计模式 笔记与总结(1)命名空间 与 类的自动载入
    Java实现 计蒜客 1251 仙岛求药
    Java实现 计蒜客 1251 仙岛求药
    Java实现 计蒜客 1251 仙岛求药
    Java实现 蓝桥杯 算法训练 字符串合并
    Java实现 蓝桥杯 算法训练 字符串合并
    Java实现 蓝桥杯 算法训练 字符串合并
    Java实现 LeetCode 143 重排链表
    Java实现 LeetCode 143 重排链表
  • 原文地址:https://www.cnblogs.com/szn409/p/7801266.html
Copyright © 2011-2022 走看看