文章目录
1 socket概述
socket 是一种 IPC 方法,它允许位于同一主机(计算机)或使用网络连接起来的不同主机上的应用程序之间交换数据。
1.1 domain
domain, socket存在于一个通信domain中,他确定
- 识别出一个 socket 的方法(即 socket“地址”的格式);
- 通信范围(即是在位于同一主机上的应用程序之间还是在位于使用一个网络连接起来的不同主机上的应用程序之间)
Domain | 执行的通信 | 应用程序间的通信 | 地址格式 | 地址格式 |
---|---|---|---|---|
AF_UNIX | 内核中 | 同一主机 | 路径名 | sockaddr_un |
AF_INET | 通过IPv4 | 通过 IPv4 网络连接起来的主机 | 32 位 IPv4 地址+16 位端口号 | sockaddr_in |
AF_INET6 | 通过IPv6 | 通过 IPv6 网络连接起来的主机 | 128 位 IPv6 地址+16 位端口号 | sockaddr_in6 |
1.2 scket类型
每个 socket 实现都至少提供了两种 socket:流和数据报。这两种 socket 类型在 UNIX 和Internet domain 中都得到了支持
一个流 socket 类似于使用一对允许在两个应用程序之间进行双向通信的管道,它们之间的差别在于( Internet domain) socket 允许在网络上进行通信。
数据报 socket( SOCK_DGRAM)允许数据以被称为数据报的消息的形式进行交换。在数据报 socket 中,消息边界得到了保留,但数据传输是不可靠的。消息的到达可能是无序的、重复的或者根本就无法到达。
1.3 socket系统调用
- socket() 系统调用创建一个新 socket。
- binder() 系统调用将一个 socket 绑定到一个地址上。通常,服务器需要使用这个调用来将其 socket绑定到一个众所周知的地址上使得客户端能够定位到该 socket 上
- listen() 系统调用允许一个流 socket 接受来自其他 socket 的接入连接。
- accept() 系统调用在一个监听流 socket 上接受来自一个对等应用程序的连接, 并可选地返回对等 socket 的地址
- connect() 系统调用建立与另一个 socket 之间的连接
socket I/O 可以使用传统的 read()和 write()系统调用或使用一组 socket 特有的系统调用(如send()、 recv()、 sendto()以及 recvfrom())来完成。在默认情况下,这些系统调用在 I/O 操作无法被立即完成时会阻塞。
2系统调用
2.1 创建一个socket ,socket()
socket()系统调用创建一个新的socket。
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
- domain 参数指定了 socket 的通信 domain。 type 参数指定了 socket 类型。创建流 socket 时会被指定为SOCK_STREAM,创建数据报 socket 时会被指定为SOCK_DGRAM。
- protocol 当创建SOCK_STREAM时值为IPPROTO_TCP,在创建SOCK_DGRAM时值为IPPROTO_UDP。也可以直接指定为0,系统会自动推演出应该使用什么协议。一般 socket 类型中总会被指定为 0。在一些 socket 类型中会使用非零进行描述。
- socket()在成功时返回一个引用在后续系统调用中会用到的新创建的 socket 的文件描述符
2.2 将socket绑定到地址 bind()
bind()系统调用将一个 socket 绑定到一个地址上。
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- sockfd 参数是在上一个 socket()调用中获得的文件描述符。
- addr 参数是一个指针,它指向了一个指定该 socket 绑定到的地址的结构。传入这个参数的结构的类型取决于 socket domain。
- addrlen 参数指定了地址结构的大小
- 每种 socket domain 都使用了不同的地址格式。如 UNIX domain socket 使用路径名,而Internet domain socket 使用了 IP 地址和端口号。sockaddr 结构通常被定义成如下所示的结构,来满足任意类型的地址结构。
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
3 流socket
- 一个应用程序调用bind()以将 socket 绑定到一个地址上
- 调用listen()通知内核它接受接入连接。使用 accept()接受连接
- 其他应用程序通过调用 connect()建立连接,同时指定需连接的 socket 的地址。
- 如果在对等应用程序调用 connect()之前执行了 accept(),那么 accept()就会阻塞
- 一旦建立了一个连接之后就可以在应用程序之间进行双向数据传输直到其中一个使用 close()关闭连接为止
- 通信是通过传统的 read()和 write()系统调用或通过一些提供了额外功能的 socket 特定的系统调用(如 send()和 recv())来完成的。
3.1 监听接入连接listen()
- listen()系统调用将文件描述符 sockfd 引用的流 socket 标记为被动。这个 socket 后面会被用来接受来自其他(主动的) socket 的连接。
- 无法在一个已连接的 socket(即已经成功执行 connect()的 socket 或由 accept()调用返回的socket)上执行 listen()。
#include <sys/socket.h>
int listen(int sockfd, int backlog);
- sockfd为需要进入监听状态的套接字,
- backlog 为请求队列的最大长度。
3.2 接受连接 accept()
accept()系统调用在文件描述符 sockfd 引用的监听流 socket 上接受一个接入连接。如果在调用accept()时不存在未决的连接,那么调用就会阻塞直到有连接请求到达为止。
#include <sys/socket.h>
int accept(