1. socket
网络层中的IP地址可以唯一确定一台主机,而传输层的 协议+端口 可以唯一表示主机中的应用程序(进程),因此利用三元组(Ip 地址,协议,端口)就可以表示网络中的进程。
进程间的通信是通过socket来完成的,socket类似于文件操作,封装了一组接口,利用这些接口完成**“打开、读/写、关闭操作”**
以TCP协议通信的socket为例,下图是TCP的交互过程:
具体过程如下:
(1)服务器根据地址类型( ipv4, ipv6 )、 socket 类型、协议创建 socket
(2)bind(): 服务器为 socket 绑定 IP 地址和端口号
(3)listen(): 服务器监听端口号请求,随时接受客户端发来的链接请求,此时,服务器的socket并没有打开
(4)客户端创建socket
(5)connect(): 客户端打开socket,并根据服务器的IP地址和端口号尝试链接服务器socket
(6)accept(): 服务器监听到客户端的socket请求,被动打开socket,开始接收客户端请求,此时,服务端socket进入阻塞状态,即accept()方法,直到客户端返回连接信息(三次握手过程)后才返回。
(7)客户端连接成功,向服务器发送连接状态信息
(8)服务器accept()方法返回,连接成功
(9)send(): 客户端向socket写入信息
(10)recv(): 服务端读取信息
(11)客户端关闭
(12)服务器端关闭
2. 接口详解
(1)socket函数
函数原型: int socket(int domain , int type , int protocol) ;
返回值:整型的socket描述符。 socket函数对应于普通的文件打开操作,普通文件打开操作返回的是一个文件描述符,而socket()函数返回的是socket描述符,它唯一标识一个socket
参数:
参数名 | 描述 |
---|---|
domain | 协议域,又称协议族。 常用地协议族有:AF_INET、 AF_INET6 、 AF_LOCAL (或称 AF UNIX, Unix 域 socket ) 、 AF_ROUTE 等 。 协议族决定了 socket的地址类型,在通信中必须采用对应的地址,如 AF_INET 决定了要用 ipv4 地址 ( 32 位) 与端口号( 16 位)的组合 、 AF_ROUTE 决定了要用一个绝对路径名作为地址 。 |
type | 指定socket类型。 常用的socket类型:SOCK_STREAM 、 SOCK_DGRAM 、SOCK_RAW 、 SOCK_PACKET 、 SOCK_SEQPACKET 等 。 SOCK_STREAM: 面向连接的可靠传输,即TCP协议。 SOCK_DGRAM: 面向无连接的不连续不可靠的传输,即UDP协议。 SOCK_RAW : 原始socket,可以读写ICMP及ICMP6、特殊的IP数据包(如自定义包)。 SOCK_PACKET: 与网络驱动程序直接通信,内核将不对网络数据进行处理而直接交给用户。 SOCK_STREAM: 提供连续可靠的数据包连接。 |
protocol | 协议 常用协议:IPPROTO_TCP(TCP协议) , IPPTOTO_UDP(UDP协议) , IPPROTO_SCTP(SCTP协议) 、 IPPROTO_TIPC(TIPC协议) 当protocol为0时,自动为type选择对应的协议 |
socket()函数调用成功,则返回一个新创建的套接字描述符,调用失败,则返回INVALID_SOCKET(linux中为-1)。套接字描述符是一个整型值,每个进程的进程空间里都有一个套接字描述符表,存放着套接字描述符和套接字数据结构的对应关系。
(2)bind函数
函数原型: int bind (int sockfd, const struct sockaddr *addr, socklen_t addrlen) ;
通常服务器在启动的时候都会绑定一个众所周知的地址(如 ip 地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的 IP 地址组合 。bind函数就是把一个地址族中的特定地址赋值给socket(包含IP地址和端口号)。
返回值:如果函数执行成功,返回0,否则,返回SOCKET_ERROR
参数:
参数名 | 描述 |
---|---|
sockfd | 即socket描述符。 |
addr | 一个 const struct sockaddr*指针,指向要绑定给 sockfd 的协议地址 。 该协议地址结构根据根据创建socket时使用的地址协议族的不同而不同,不同地址协议族的协议地址结构将会在表格下面列出。 |
addrlen | 地址的长度 |
不同地址协议族的地址结构:
IPv4地址结构代码:
struct sockaddr_in {
sa_family_t sin_family; // 协议族为:AF_INET
in_port_t sin_port; // 按网络字节顺序的端口号
struct in_addr sin_addr; // 互联网地址
};
// 互联网地址
struct in_addr {
uint32_t s_addr; // 网络字节顺序的地址
};
IPv6地址结构代码:
struct sockaddr_in6 {
sa_family_t sin6_family; // 协议族为:AF_INET6
in_port_t sin6_port; // 按网络字节顺序的端口号
uint32_t sin6_flowinfo; // IPv6流信息
struct in6_addr sin6_addr; // 互联网地址
uint32_t sin6_scope_id; // 范围ID
};
// IPv6地址
struct in6_addr {
unsigned char s6_addr[16];</