使用socket()函数创建套接字

在Linux下创建 socket

在 Linux 下使用 <sys/socket.h>头文件中 socket() 函数来创建套接字,原型为:

int socket(int af, int type, int protocol);

1) af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INETAF_INET6。AF 是“Address Family”的简写,INET是“Inetnet”的简写。AF_INET 表示 IPv4 地址,例如127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B

你也可以使用PF前缀,PF是“Protocol Family”的简写,它和AF是一样的。例如,PF_INET 等价于 AF_INET,PF_INET6 等价于 AF_INET6。

2) type 为数据传输方式,常用的有SOCK_STREAM和 SOCK_DGRAM (详解见本页下面章节套接字类型 - socket type).

3) protocol 表示传输协议,常用的有 IPPROTO_TCP和IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。

返回值:成功返回非负整数,它与文件描述符类似,我们称它为套接口描述字,简称套接字。失败分会-1。

有了地址类型(af)和数据传输(type)方式,还不足以决定采用哪种协议(protocol)吗?为什么还需要第三个参数呢?

正如大家所想,一般情况下有了地址类型(af)和数据传输(type)方式 两个参数就可以创建套接字了,操作系统会自动推演出协议类型,除非遇到这样的情况:有两种不同的协议(protocol)支持同一种地址类型(af)和数据传输类型(type)。如果我们不指明使用哪种协议,操作系统是没办法自动推演的。

如果使用IPv4 地址,参数 af 的值为 PF_INET。如果使用 SOCK_STREAM 传输数据,那么满足这两个条件的协议只有 TCP,因此可以这样来调用socket() 函数:

int tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  //IPPROTO_TCP表示TCP协议

这种套接字称为 TCP 套接字。

如果使用 SOCK_DGRAM 传输方式,那么满足这两个条件的协议只有 UDP,因此可以这样来调用 socket() 函数:

int udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);  //IPPROTO_UDP表示UDP协议

这种套接字称为 UDP 套接字。 上面两种情况都只有一种协议满足条件,可以将 protocol 的值设为 0,系统会自动推演出应该使用什么协议,如下所示:

int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);  //创建TCP套接字
int udp_socket = socket(AF_INET, SOCK_DGRAM, 0);  //创建UDP套接字

套接字类型 - socket type

套接字类型是指创建套接字的应用程序要使用的通信服务类型。linux系统支持多种套接字类型,最常用的有以下三种:

(1)SOCK_STREAM:流式套接字,提供面向连接、可靠的数据传输服务,数据按字节流、按顺序收发,保证在传输过程中无丢失、无冗余。TCP协议支持该套接字。

TCP协议 有以下一些特点:

(1)TCP提供可靠的连接。当TCP向另外一端发送数据时,它要求对方返回一个确认回答。如果没有收到确认,则会等待一段时间后重新发送,在数次重发失败后,TCP才会放弃发送。

(2)TCP为发送的数据进行排序。比如发送2048个字节,TCP可能将它分成大小为1024的两个段,并分别进行编号“1”和“2”。接收段将根据编号对数据进行重新排序并判断是否为重复数据。

(3)TCP提供流量控制。它会通知对方自己能够接受数据的容量,称为窗口,这样就确保不会发生缓冲区溢出的情况。

(4)TCP的连接是双工的。在给定连接上的应用进程在任何时刻既可以发送也可以接受数据

(2)SOCK_DGRAM:数据报套接字,提供面向无连接的服务,数据收发无序,不能保证数据的准确到达。UDP协议支持该套接字。

UDP提供无连接的服务,就是说UDP客户与服务器不必保持长期的连接关系。例如,一个UDP客户可以创建一个套接字并发送一个数据报给一个服务器,然后可以立即用同一个套接字发送另一个数据报给另一个服务器。UDP所面临的问题就是缺乏可靠性。因为它没有例如确认、超时重传等复杂机制,因此它不能保证数据的到达以及到达的次序。

那么我们在传送过程中,如果数据丢失了该怎么办呢?其实每个程序在UDP上都有自己的协议,如果在一定时间内没有收到对方发回的确认应答,它将重新发送,直到得到ACK。

UDP实现过程比较简单,因此在一定程度上效率较高,对于一些数据量小,无须交互的通信情况还是适用的。使用UDP的应用程序有:tftp、bootp等。

(3)SOCK_RAW:原始套接字。

除了上面两种常用的套接字类型外,还有一类原始套接字(raw socket),在某些网络应用中担任重要角色。比如我们平时想看一看网络是否通达,就用ping命令测试一下。Ping命令用的是ICMP协议,因此我们不能通过建立一个SOCK_STREAM或SOCK_DGRAM来发送这个包,而只能自己亲自来构建ICMP包来发送。另外一种情况是,许多操作系统只实现了几种常用的协议,而没有实现其它如OSPE、GGP等协议。如果自己有必要编写位于其上的应用,就必须借助raw socket来实现,这是因为操作系统遇到自己不能够处理的数据包,就将这个包交给raw socket处理。

Raw socket的作用主要在三个方面:

(1)通过raw socket来接收和发送ICMP协议包。

(2)接收发向本机的但TCP/IP栈不能够处理的IP包。

(3)用来发送一些自己指定源地址特殊作用的IP包。

Last updated