第一章《TCPIP 网络编程-01-入门》讲到了 socket 函数,这里将详细看看套接字逻辑
1 |
|
domain参数代表的协议族分类可分为以下几类
- PF_INET: IPv4 互联网协议族
- PF_INET6: IPv6 互联网协议族
- PF_LOCAL: 本地通信的 UNIX 协议族
- PF_PACKET: 底层套接字的协议族
- PF_IPX: IPX Novell 协议族
type 参数代表的套接字数据的传输方式
- SOCK_STREAM: 面向连接的套接字(TCP)。 面向连接的特点,传输过程中数据不会消失,按序传输数据,传输的数据不存在数据边界(面向连接的可靠字节流)
- 可靠的(不丢失)
- 按序传输数据
- 传输的数据没有边界
- SOCK_DGRAM: 面向消息的套接字(UDP)。有以下特点
- 强调快速而非传输顺序
- 传输的数据可能丢失也可能损毁
- 传输数据有边界
- 限制每次传输的数据大小
protocol 参数代表决定了最终的协议,为什么前两个参数无法决定?
因为同一协议族中可能存在多个数据传输方式相同的协议。所以一般可以传一个 0 表示使用默认协议
Tips:
- 为了接收数据,套接字内部有字节数组缓冲区。调用 read 函数从缓冲区读取部分数据,所以缓冲区并不总是满的。即使读的比较慢,缓冲区满了,套接字无法再接受数据,也不会发生数据丢失。因为套接字会根据接收端的状态传输数据(滑动窗口),传输错误也会有重传服务
示例地址:https://github.com/XBoom/network-ip.git 中的 apps/socket/03/
运行结果如下:
1 | [03/server.c:44 main info] server recv 48 len msssage: |
可以看到,即使一个一个字符的读取也是能读到所有的内容(注意字符串结束符会影响打印)
问题 1:既然数据是没有边界的,那么上层是如何获取对应的数据结构的?
- 固定长度的消息:每次读取固定长度,既浪费空间也不灵活
- 消息头包含长度信息:在消息头包含一个字段,表示整个消息的长度。接收方先读消息头,然后根据长度信息读取对应长度的数据
- 使用消息格式协议:使用 JSON、 XML 等消息格式协议
- 使用消息起始标志与结束标志:通过检测这些标志来确定消息的边界
问题 2:如何使用 SOCK_DGRAM
又如何编程?
示例地址:https://github.com/XBoom/network-ip.git 中的 apps/socket/04/
区别在于 客户端并不需要执行连接请求,只需要在发送的时候指定发送地址。而服务端也不需要 bind/listen
1 | for(int i = 0; i < 3; i++) |
而服务端也不需要监听,直接读取数据
1 | while((read_len = recvfrom(server_sock, &message[str_len], MAX_BUFF_LEN, 0, |
而且数据也不一个一个读取,这是读取 MAX_BUFF_LEN
的结果
1 | [04/server.c:32 main info] recv 0 17 |
而这是一次读取一个的情况
1 | [04/server.c:32 main info] recv 0 1 |
每次读取一个之后整个数据其实就丢了
最后来看看正常情况下是不是实现了 UDP 通信

可以看到 UDP 并不需要三次握手也就不需要 connect 以及 listen 这类操作直接进行数据发送与接收,详细的过程后续在介绍 UDP 在详细查看
参考文档
- 《TCPIP 网络编程》