套接字是电脑网络中进程间数据流的端点
使用套接字API对UDP/TCP的srver/client进行模拟实现,有助于深刻理解计算机网络
套接字与网络字节序
日常生活中邮件的递送必须需要目标地址
网络信息的递送也需要地址信息,具体来讲,则是:目标IP地址与端口号
可以初步理解为:套接字(Socket)即是 IP address + port( + TCP/UDP )
通过套接字相关函数,才可以完成通信过程。
所谓套接字
是支持TCP/IP的网络通信的基本操作单元,是不同主机之间的进程进行双向通信的端点
实际上套接字是内核中的进程与网络系统的桥梁,进程通过struct socket提取内核网络系统中的数据
IPV4套接字
socket API作为一层抽象的网络编程接口,适用于各种底层网络协议,但不同协议下地址格式并不同
对于IPV4类型的套接字sockaddr_in
具体定义如下
1 | struct sockaddr_in { |
通用类型参数sockaddr
1982年制定套接字地址结构时还没有 void*
的出现,但函数参数需要一种通用的类型
最终,约定使用特殊结构体sockaddr
作为通用地址格式
在具体传參时,使用强制转换完成,如
1 | struct sockaddr_in servaddr; |
BSD实现图示
对于其他常见的套接字类型,举一个较新的BSD实现如下图
其中,sockaddr_storage
是一种新的套接字地址结构
相比 sockaddr
存在以下两点差别
- 如果系统支持的任何套接字地址结构有对齐需要,那么
sockaddr_storage
能够满足最苛刻的对齐要求 sockaddr_storage
足够大,能够容纳系统支持的任何套接字地址结构
网络字节序
数据在内存有大端与小端的区别
由于网络中主机的差异,在数据传输时必须保证大小端的统一
事实上,约定网络数据流以大端的存储方式,在C库中,提供了函数方便大小端数据的相互转换
1 |
|
TCP与UDP
区别概述
- TCP面向连接,发送数据之前需要建立连接;UDP面向无连接,开销相对较小
- TCP保证数据顺序,保证数据交付;UDP尽最大努力交付,开销相对较小
- TCP面向字节流;UDP是面向报文的,没有拥塞控制,功能少
- TCP只支持一对一;UDP支持一对一,一对多,多对一和多对多的交互通信
- TCP的逻辑通信信道是全双工的;UDP半双工
UDP实例
server
1 |
|
其中
int socket(int domain, int type, int protocol);
- 建立套接字文件描述符,应用程序可以像读写文件一样收发数据
- domain:用于设置网络通信的域,函数该参数选择通信协议的族,AF_INET表示IPV4
- type:用于设置套接字通信的类型
- SOCKET_STREAM(流式套接字,TCP)
- SOCK_DGRAM(数据包套接字,UDP)
- protocol:一般为0
- 成功返回一个标识这个套接字的文件描述符,失败返回-1
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
- 对套接字进行地址和端口的绑定后才能进行数据的收发
- sockfd:是用socket()函数创建的文件描述符
- my_addr:是指向套接字地址格式结构体的指针
- addrlen:是my_addr结构的长度,可以为sizeof(struct sockaddr)
- 成功返回0,失败返回-1
- IPV4地址转换函数(点分十进制与4字节格式互转)
- 点分十进制转4字节
int inet_aton(const char *string, struct in_addr*addr);
- 成功返回非0整数
- 4字节转点分十进制
char *inet_ntoa (struct in_addr);
- 失败返回NULL
- 返回值位于静态区,下一次调用会覆盖上一次的结果,可能影响线程安全
- 点分十进制转4字节
- 更安全强大的地址转换函数(但比aton、ntoa参数多)
const char *inet_pton(int domain, const void *restrict addr, char *restrict str, socklen_t size);
- 失败返回NULL
int inet_pton(int domain, const char *restrict str, void *restrict addr);
- 成功返回1,格式无效返回0,出错返回-1
client
1 | // 头文件略 |
TCP实例
server
1 |
|
其中
int listen(int sockfd, int backlog);
- 将sockfd指定为接收连接请求的套接字
- connect请求发生时,完成三次握手后会将连接放到制定队列中,直到被accept处理
- 如果这个队列满了,且有新的连接的时候,对方会收到出错信息
- sockfd:指定的套接字描述符
- backlog:等待连接的队列大小
- 成功返回0,失败返回-1
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- sockfd:socket函数返回的套接字描述符
- addr和addrlen:用来返回已连接的对端进程(客户端)的协议地址(输出型参数)
- 服务器调用
accept
后客户端没有数据时,服务器进入阻塞 - 如果对客户端的协议地址不感兴趣,可以把arrd和addrlen置为空指针
- 成功返回非负描述符,失败返回-1
client
1 |
|
其中
int connect (int sockfd ,struct sockaddr *serv_addr, int addrlen);
- 用于客户端建立tcp连接
- sockfd:套接字文件描述符
- serv_addr:目标主机地址和端口号
- addrlen:缓冲区的长度
- connect会激发TCP的三路握手过程
- 成功返回0,失败返回-1