zoukankan      html  css  js  c++  java
  • Socket API简介

     

    一.TCP的三次握手

    建立一个TCP连接会发生下述情形:

    (1)      服务器必须准备好接受外来的连接,这通常通过调用socket、bind和listen这三个函数来完成,我们称之为被动打开(passtive open)

    (2)      客户通过调用connect发起主动打开(active open)。这导致客户TCP发送一个SYN(同步)分节,它告诉服务器客户将在(待建立)连接中发送的数据的初始序列号(J),通常SYN分节不带数据,其所在的IP数据报只含有一个IP首部,一个TCP首部及可能有的TCP选项

    (3)      服务器必须确认(ACK)客户的SYN,同时自己也得发一个SYN分节,它含有服务器将在同一连接中发送的数据的初始序列号(K),服务器在单个分节中发送SYN和对客户SYN的ACK(确认)

    (4)      客户必须确认服务器的SYN

    这种交换至少需要三个分组,因此称之为TCP的三次握手.1>客户端发SYN J到服务器2>服务器发SYN K, ACK J+1到客户端3>客户端回ACK K+1到服务器,第2>步服务器既要确定(ACK)它收到了客户端的消息,又要发一个SYN询问客户端是否准备好进行数据通讯了,两部分做一个发送.(个人理解),ACK带的总是对方序号+1

    二 .socket函数

       SOCKET socket(
    
                  IN int af,//指明了协议族
    
                  IN int type,
    
                  IN int protocol
    
                  );
    
    

    返回值:成功返回非负的描述符(sockfd),失败返回INVALID_SOCKET,如失败可以通过WSAGetLastError得到指定的错误码.

    af:指明了协议族

    family

    说明

    AF_INET

    IPv4协议

    AF_INET6

    IPv6协议

    AF_LOCAL

    Unix域协议

    AF_ROUTE

    路由套接字

    AF_KEY

    密钥套接字

    TCP/UDP一般使用AF_INET就行了,尝试了下其他协议族,在TCP/UDP下,

    AF_INET6返回10047(使用了与请求的协议不兼容的地址)

    Type:套接字类型

    type

    说明

    SOCK_STREAM

    字节流套接字

    SOCK_DGRAM

    数据报套接字

    SOCK_SEQPACKET

    有序分组套接字

    SOCK_RAW

    原始套接字

    TCP是一个字节流协方,仅支持SOCK_STREAM套接字

    UDP仅支持SOCK_DGRAM套接字

    Protocol:

    protocol

    说明

    IPPROTO_TCP

    TCP传输协议

    IPPORTO_UDP

    UDP传输协议

    IPPORTO_SCTP

    SCTP传输协议

    如果指定为0,系统会根据地址格式和套接字类别,自动选择一个合适的协议,推荐这种方法。

    . SOCKADDR_IN和bind函数

     struct sockaddr_in {
    
              short   sin_family;    
    
              USHORT  sin_port;
    
              IN_ADDR sin_addr;
    
              CHAR    sin_zero[8];
    
           } SOCKADDR_IN;
    
    


     

    MSDN解释说此结构用于IPv4,

    sin_family:AF_INET(IPv4协议族)

    sin_port:将要分配给套接字的端口

    in_addr:套接字主机的IP地址,设为INADDR_ANY表示通配地址,因为IPv4的IP地址是一个32位的值,所以可以直接用htonl(INADDR_ANY)赋值

    通配地址应该是指任何分配给主机的IP地址,一般每个机器只有一个IP地址,但有的机器可能会有多个网卡,每个网卡都可以有自己的IP地址。

    创建套接字后,需bind把该套接字绑定到本地的某个地址和端口上

    int bind(

            SOCKET s,

            const struct sockaddr FAR * name,

            int namelen

           );

     

    返回值:成功返回0,失败返回SOCKET_ERROR,通过WSAGetLastError得到指定的错误码.常见错误为EADDRINUSE(地址已使用)

    Name:指向特定于协议的地址结构的指针,基于TCP,IPv4可以用SOCKADDR_IN代替,IPv6可以用SOCKADDR_IN6代替

    Namelen:name的长度

     

    对于TCP,调用bind可以指定一个端口号或指定一个IP地址,可都指定,可指定其中一个,可全都不指定

    如果一个TCP客户或服务器未曾调用bind捆绑端口,当调用connect或listen时,内核就要为相应的套接字选择一个临时端口,让内核选择临时端口对TCP客户是正常的,然而对服务器是罕见的,因为服务器是通过它们的众所周知的端口被大家认识的。

    如果绑定一个特定IP地址,这个IP地址必须属于其所在主机的网络接口之一,对于TCP客户端,这就为在该套接字上发送的IP数据报指定了源IP地址,对于TCP服务器,这就限定该套接字只接收那个目的地址为这个IP地址的客户连接,所以,TCP客户通常不捆绑IP地址,当连接套接字时,内核将根据所用的外出网络接口自动选择源IP地址,所用的外出网络接口取决于到达服务器所需的路径,如果TCP服务器没有捆绑IP地址,内核就把客户发送的SYN的目的IP做为服务器的源IP地址。(getsockname可以获取来自客户的目的地址)

     

    只需指定端口号为0,那么内核就在bind被调用时选择一个临时端口,如果指定IP地址为通配地址,那么内核将在套接字已连接(TCP)或已在套接字上发出数据报(UDP)时才选择一个IP地址.

    四.Listen函数

    int listen(
    
         SOCKET s,
    
         int backlog
    
        );
    
    


     

    返回值: 成功返回0,失败返回SOCKET_ERROR

    MSDN说它把套接字置于一种监听即将到来的连接的状态

    Listen函数仅由TCP服务器调用,它做两件事:

    (1)  当socket函数创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的客户套接字,listen函数据一个未连接的套接字转换成一个被动套接字,指示内核应接收指向该套接字的连接请求,设用listen导致套接字从CLOSED状态转换成LISTEN状态

    (2)  Backlog规定了内核应该为相应套接字排队的最大连接个数

     

    内核为任何一个给定的监听套接字维护两个队列:

    (1)未完成连接队列,队列元素为某个客户发出的SYN分节到达服务器,而服务器正在等待完成相应的TCP三次握手过程,这些套接字处于SYN_RCVD状态

    (2)已完成连接队列,队列元素为某个已完成TCP三次握手过程的客户,这些套接字处理ESTABLISHED状态。

    必须满足:未完成连接队列+已完成连接队列<=Backlog(Berkeley给它设了个模糊因子,所以也可能是Backlog*1.5)

    队列形成过程:当来自客户的SYN到达时,TCP在未完成连接队列是创建一个新项,然后响应第二次握手,也就是服务器发SYN K, ACK J+1(见上面的TCP三次握手),这一项一直保留在未完成连接队列中,直到第三次握手到达或超时为止,如果三次握手正常完成,该项被移到已完成队列的队尾,当进程调用accept时,已完成连接队列中的队头项将返回给进程,如果该队列为空,那么进程进入等待,直到TCP在该队列中放入一项才唤醒它

    历史上Backlog总是设为5,因为这是4..2BSD支持的最大值,现在可以设定较大的值了,因为就算内核不支持这么大,它也会悄悄的把这个值截成自身能支持的最大值。

    当一个客户SYN到达时,若这些队列是满的,TCP就直接忽略该分节(也就是不发RST),因为客户TCP将重发SYN,期望不久能在这些队列中找到可用空间

    对RST google了下:

    RST:(Reset the connection)用于复位因某种原因引起出现的错误连接,也用来拒绝非法数据和请求。如果接收到RST位时候,通常发生了某些错误:

    (1)   建立连接的SYN到达某端口,但是该端口上没有正在监听的服务

    (2)       TCP想取消一个已有连接

    (3)       TCP接收到了一个根本不存在的的连接上的分节

    可使用RST攻击

    可使用SYN flooding发起攻击,通过伪装的SYN装填未完成连接队列,使合法的SYN排不上队,从而导致针对合法客户的服务被拒绝.

    五.accpet函数

        SOCKET accept(
    
           SOCKET s,
    
           struct sockaddr FAR * addr,
    
           int FAR * addrlen
    
           );
    
    


     

    返回值:成功返回非负描述符,失败返回INVALID_SOCKET

    Accpet函数由TCP服务器调用,用于从已完成连接队列队头返回下一个已完成连接,如果已完成连接为空,那么进程处于等待状态(假定套接字为阻塞方式)

    addr:是out型的,用来返回已连接的对端进程(客户)的协议地址,

    addrlen:调用前,置为Addr所指的套接字地址结构的长度,返回时,该整数值即为由内核存放在该套接字地址结构内的确切字节数

    accept的第一个参数为监听套接字,而如果成功,返回值为一个已连接套接字,一个服务器通常仅创建一个监听套接字,它在该服务器的生命期一直存在,内核为每个由服务器进程接受的客户连接创建一个已连接套接字(也就是通过了TCP三次握手的客户),当服务器完成对某个给定客户的服务时,相应的已连接套接字就被关闭了(手动调用closesocket关掉已处理完的已连接套接字)

    六.recvfrom和sendto函数

    UDP套接字状态下,客户不与服务器建立连接,只管sendto向服务器发送数据,其中必须指定目的地,同样,服务器不接受来自客户的连接,而只管调用recvfrom等待某个客户的数据的到来,recvfrom将与所接收的数据报一同返回客户的协议地址,因此服务器可以把响应发给正确的客户。

    int recvfrom(
    
        SOCKET s,
    
        char FAR* buf,
    
        int len,
    
        int flags,
    
        struct sockaddr FAR* from,
    
        int FAR* fromlen
    
        );
    
    


     

    返回值:成功返回0,失败返回SOCKET_ERROR

    类似read函数,不过多了三个额外的参数

    S:准备接收数据的套接字

    buf:读入缓冲区的指针

    len: buf的大小

    flags:一般置为0

    from:接收发送方(客户)的地址信息

    fromlen:调用前,置为from所指的套接字地址结构的长度,返回时,该整数值即为由内核存放在该套接字地址结构内的确切字节数

    七.getsockopt和setsockopt

    (参看UNIX网络编程第7章)

    int setsockopt(
    
         SOCKET s,
    
         int level,
    
         int optname,
    
         const char * optval,
    
         int optlen
    
        );
    
    int getsockopt(
    
         SOCKET s,
    
         int level,
    
         int optname,
    
         char  * optval,
    
         int  * optlen
    
        );
    
    


     

    返回值:成功返回0,失败返回SOCKET_ERROR

    s:被查询或被设置的套接字

    level:,如果想要在套接字级别上设置选项,就必须把level设置为 SOL_SOCKET

    SOL_SOCKET

    套接字层

    IPPROTO_IP

    IPV4层

    IPPROTO_ICMPV6

    IPPROTO_IPV6

    IPV6层

    IPPROTO_IP/IPPROTO_IPV6

    IPPROTO_TCP

    TCP层

    IPPROTO_SCTP

    SCTP层

    optname: 参看UNIX网络编程第7章,自己程序用到了SO_BROADCAST设置

    套接字选项分为两大类:

    (1)    启用或禁止某个特性的二元选项(标志选项)

    (2)    取得并返回我们可以设置或检查的特定值的选项

    调用getsockopt时,optval是一个整数,它返回的值为0表示相应的选项被禁止了,不为0表示相应选项被启用,类似的,setsockopt函数需要一个不为0的optval来启用选项,一个为0的optval来禁止选项.

  • 相关阅读:
    开启mysql远程登录
    InfluxDB安装及配置
    基于ssh反向代理实现的远程协助
    小工具之进程守护器
    生成freeswitch事件的几种方式
    freeswitch模块之event_socket
    freeswitch对接其它SIP设备
    freeswitch注册过程分析
    redis参考文档
    创建型模式之Builder模式及实现
  • 原文地址:https://www.cnblogs.com/hgy413/p/3693546.html
Copyright © 2011-2022 走看看