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

## 在Linux下创建 socket

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

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

1\) af 为地址族（Address Family），也就是 IP 地址类型，常用的有 `AF_INET`和 `AF_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）方式** 两个参数就可以创建套接字了，操作系统会自动推演出协议类型，除非遇到这样的情况：有两种不同的协&#x8BAE;**（protocol）**&#x652F;持同一种地址类&#x578B;**（af）**&#x548C;数据传输类&#x578B;**（type）**。如果我们不指明使用哪种协议，操作系统是没办法自动推演的。

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

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

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

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

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

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

```c
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包。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xiaoxiami.gitbook.io/linux-server/socket/socket-xiang-guan-han-shu/shi-yong-socket-han-shu-chuang-jian-tao-jie-zi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
