socket总结

socket学习总结

《UNIX 网络编程 卷1:套接字联网API》第4章 基本TCP套接字编程

1.socket函数

用于获得套接字描述符。

函数定义

#include <sys/socket.h>
int socket(int family, int type, int protocol); 
返回:若成功则为非负描述符,若出错则为-1

函数中各个参数的含义如下:

  • family
    指明协议簇,它是图1.1所示的某个常值。该参数也常常被称为协议域。
  • type
    指明套接字类型,它是图1.2所示的某个常值。
  • protocol
    应设为图1.3所示的某个协议类型常值,或者设为0,以选择所给定的family和type组合的系统默认值。

使用说明

并非所有套接字family与type的组合都是有效的,图1.4给出了一些有效的组合和对应的真正协议。其中标为“是”的项也是有效的,但还没有找到便捷的缩略词。而空白项则是无效组合。

图1.1 socket的family值:

family说明
AF_INETIPv4协议
AF_INET6IPv6协议
AF_LOCALUnix域协议
AF_ROUTE路由套接字
AF_KEY密钥套接字

图1.2 socket的type值

type说明
SOCK_STREAM字节流套接字
SOCK_DGRAM数据报套接字
SOCK_SEGPACKET有序分组套接字
SOCK_RAW原始套接字

图1.3 socket函数的AF_INET或AF_INET6的protocol常值

protocol说明
IPPROTO_TCPTCP传输协议
IPPROTO_UDPUDP传输协议
IPPROTO_SCTPSCTP传输协议

图1.4 socket函数的family和type的组合

-AF_INETAF_INET6AF_LOCALAF_ROUTEAF_KEY
SOCK_STREAMTCP|SCTPTCP|SCTP
SOCK_DGRAMUDPUDP
SOCK_SEQPACKETSCTPSCTP
SOCK_RAWIPv4IPv6

socket函数成功返回一个小的非负整数值,它与文件描述符类似,我们把它称为套接字描述符,简称sockfd。

对比AF_XXX和PF_XXX

AF_前缀表示地址族,PF_前缀表示协议族。在Linux系统中AF_XXX和PF_XXX是等价的。通常仅使用AF_常值。

2.connect函数

客户端程序使用connect函数来建立与服务器的连接。

函数定义

#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
返回:如成功则为0,若出错则为-1

函数中各个参数的含义如下:

  • sockfd
    由socket函数返回的套接字描述符
  • servaddr
    指向套接字地址结构的指针,套接字地址结构必须含有服务器的IP地址和端口号
  • addrlen
    servaddr结构的大小

使用说明

1.客户端在调用connect前不必非得调用bind,内核会确定源IP地址,并选择一个临时端口作为源端口。
2.如果是TCP套接字,调用connect函数将触发TCP的3次握手过程,而且仅在连接建立成功或者出错时才返回。其中出错返回可能有以下几种情况:
(1)客户端没有收SYN分节的响应,则返回ETIMEDOUT错误。
备注:此处有定时器。以4.4BSD为例:发送SYN后,若无响应则等待6s后再次发送一个,若仍无响应则等待24s后再次发送一个。若总共等待了75s后未收到响应则返回本错误。

(2)若对客户端的SYN的响应是RST,则表明该服务器主机在我们指定的端口上没有进程在监听。这是一种硬错误(hard error),客户端一接收到RST就马上返回ECONNREFUSED错误。

(3)若客户发出的SYN在中间某个路由器上引发了一个“destination unreasonable”(目的地不可达)ICMP错误,则认为是一种软错误(soft error)。客户端主机内核保存该消息,并按第一种情况所描述的时间间隔继续发送SYN。若在某个规定的时间(4.4BSD规定75s)后仍未收到响应,则把保存的消息(即ICMP错误)作为EHOSTUNREACH或ENETUNREACH错误返回给进程。以下两种情况也是有可能的:一是按照本地系统的转发表,根本没有到达远程系统的路径;而是connect调用根本不等待就返回。

备注1:许多早期系统在收到ICMP错误时会不正确的放弃建立连接的尝试。说不正确是因为ICMP错误可能只是某个暂时状态,可修复。

备注2:网络不可达的错误被认为已过时,应用进程应该把EHOSTUNREACH和ENETUNREACH作为相同的错误对待。

3.bind函数

bind函数把一个本地协议地址赋予一个套接字。

函数定义

#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
返回:如成功则为0,若出错则为-1

函数中各个参数的含义如下:

  • sockfd
    由socket函数返回的套接字描述符
  • myaddr
    指向套接字地址结构的指针,可以指定一个端口号,或指定一个IP地址,也可以两者都指定,还可以两者都不指定。具体效果见图3.1所示
  • addrlen
    servaddr结构的大小

使用说明

图3.1给bind函数指定要捆绑的IP地址、端口号组合产生的结果

指定IP地址指定端口号结果
通配地址0内核选择IP地址和端口
通配地址非0内核选择IP地址,进程指定端口
本地IP地址0进程指定IP地址,内核选择端口
本地IP地址非0进程指定IP地址和端口

如果指定端口号为0,那么内核就在bind被调用时选择一个临时端口。

如果指定IP地址为通配地址,那么内核将等到套接字已连接(TCP)或已在套接字上发出数据报(UDP)时才选择一个本地IP地址。

对于IPv4,IP地址是一个32位的值,通配地址由常值INADDR_ANY来指定,其值一般为0。使用方式如下:

struct sockaddr_in serverAddr
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);

对于IPv6,IP地址是一个128位的结构体,不能用常量表示。系统预先分配in6addr_any变量并将其初始化为IN6ADDR_ANY_INIT。头文件

struct sockaddr_in6 serverAddr
serverAddr.sin_family = AF_INET6;
serverAddr.sin_addr.s_addr = in6addr_any;

无论是网络字节序还是主机字节序,INADDR_ANY的值(为0)都一样,因此使用htonl并非必需。不过既然头文件

4.listen函数

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

(1)当socket函数创建一个套接字时,它被认为一个主动套接字(将调用connect发起连接的客户端套接字)。listen函数把它转换成一个被动套接字,指示内核应该接受指向该套接字的连接请求。根据TCP状态转换图,调用listen导致套接字从CLOSED状态转换到LISTEN状态。

(2)本函数的第二个参数指定了内核应该为相应套接字排队的最大连接个数。

函数定义

#include <sys/socket.h>
int listen(int sockfd, int backlog);
返回:如成功则为0,若出错则为-1

函数中各个参数的含义如下:

  • sockfd
    由socket函数返回的套接字描述符
  • backlog
    排队的最大连接个数

使用说明

其中backlog参数较为复杂,我们先来了解一些基础知识。内核为任何一个监听套接字维护两个队列:

(1)未完成连接队列(incomplete connection queue),已收到SYN分节,而服务器正在等待完成3次握手过程。这些套接字处于SYN_RCVD状态。

(2)已完成连接队列(completed connection queue),已完成3次握手过程。这些套接字处于ESTABLISHED状态。

listen函数的backlog参数曾被定义为这两个队列总和的最大值,但不同系统实现时都有不同算法,通常允许的实际最大值都比这个参数要大(+1,+2或者*1.5等不同实现方式)。

备注:当一个客户端SYN到达时,若这些队列是满的,TCP就忽略该分节,也不会发送RST。这么做是因为:这种情况是暂时的,客户端将重发SYN,期望不久就能在队列中找到可用空间。要是服务器TCP立即响应一个RST,客户端的connect调用就会立即返回一个错误,强制应用程序处理这种情况,而不是让TCP的正常重传机制来处理。另外,客户端无法区分响应SYN的RST究竟意味着“该端口没有服务器在监听”,还是意味着“该端口有服务器在监听,不过它的队列满了”

5.accept函数

accept函数由TCP服务器调用,用于从已完成连接队列的队头返回下一个已完成连接。如果已完成连接队列为空,那么进程被投入睡眠(假定套接字为默认的阻塞方式)。

函数定义

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t* addrlen);
返回:如成功则为非负描述符,若出错则为-1

函数中各个参数的含义如下:

  • sockfd
    由socket函数返回的套接字描述符

  • cliaddr
    用来返回已连接的对端进程的协议地址

  • addrlen
    值-结果参数,cliaddr结构体长度

使用说明

本函数最多返回3个值:return值,cliaddr,addrlen。如果对客户端的协议地址不感兴趣,那么可以把cliaddr和addrlen均置为空指针。

close函数

用来关闭套接字,并终止TCP连接。

函数定义

#include <sys/socket.h>
int close(int sockfd);
返回:如成功则为0,若出错则为-1

函数中各个参数的含义如下:

  • sockfd
    由socket函数返回的套接字描述符

使用说明

close一个套接字的默认行为是把该套接字标记成已关闭,然后立即返回到调用进程。该套接字描述符不能再由调用进程使用,也就是说它不能再作为read或write的第一个参数。

TCP在close后将尝试发送已排队等待发送到对端的任何数据,发送完毕后发生的是正常的TCP连接终止序列(4次通信)。

SO_LINGER套接字选项可以用来改变TCP套接字的这种默认行为,此参数的具体用法我会在其他文章中说明。

描述符引用计数

每个文件或套接字都有一个引用计数。引用计数在文件表项中维护,它是当前打开着的引用该文件或套接字的描述符的个数。当调用fork后,父进程的描述符会在父子进程中都存在,描述符引用计数将会变成2。只有2个进程都调用了close后,描述符的引用计数才会减为0。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值