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来禁止选项.

 

 

 

 

 

转载于:https://www.cnblogs.com/hgy413/archive/2012/03/17/3693546.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值