第二章 套接字类型与协议设置
本章需了解创建套接字时调用的 socket 函数。
2.1 套接字协议及数据传输特性
2.1.1 关于协议
协议是为了完成数据交换而定好的约定
2.1.2 创建套接字
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
/*
成功时返回文件描述符,失败时返回-1
domain:套接字中使用的协议族(Protocol Family)信息
type:套接字数据传输类型信息
protocol:计算机间通信中使用的协议信息
*/
下面给出详细说明
2.1.3 协议族(protocol Family)
通过 socket 函数的第一个参数传递套接字中使用的协议分类信息。此协议分类信息称为协议族,可分如下几类。
下表为头文件 sys/socket.h 中声明的协议族
名称 | 协议族 |
---|---|
PF_INET | IPV4 互联网协议族 |
PF_INET6 | IPV6 互联网协议族 |
PF_LOCAL | 本地通信 Unix 协议族 |
PF_PACKET | 底层套接字的协议族 |
PF_IPX | IPX Novel 协议族 |
本书着重讲解 PF_INET
对应的 IPV4 互联网协议族。另外,套接字中实际采用的最终协议信息是通过 socket 函数的第三个参数传递的。在指定的协议族范围内通过第一个参数决定第三个参数。
2.1.4 套接字类型(Type)
套接字类型指的是套接字的数据传输方式,通过 socket 函数的第二个参数进行传递,只有这样才能决定创建的套接字的数据传输方式。
Q:已经通过第一个参数传递了协议族信息,为什么还要决定数据传输方式?
A:决定了协议族并不能同时决定数据传输方式。换言之,socket 函数第一个参数 PF_INET 协议族中也存在多种数据传输方式。
2.1.5 套接字类型 1:面向连接的套接字(SOCK_STREAM)
如果向 socket 函数的第二个参数传递 SOCK_STREAM,将创建面向连接的套接字。
传输方式特征整理如下:
- 传输过程中数据不会消失
- 按序传输数据
- 传输的数据不存在数据边界(Boundary)
下面假设一种情形
传输数据的计算机通过 3 次调用 write 函数传递了 100 字节的数据,但接收数据的计算机仅仅通过调用 1 次 read 函数调用就接受了全部 100 个字节。
收发数据的套接字内部有缓冲(buffer),简言之就是字节数组。通过套接字传输的数据将保存到该数组。因此,收到数据并不意味着马上调用 read 函数。只要不超过数组容量,则有可能在数据填满缓冲后通过 1 次 read 函数调用读取全部,也有可能分成多次 read 函数调用进行读取。
所以说面向连接的套接字不存在数据边界。
Q:套接字缓冲已满是否意味着数据丢失
A:不会,会根据接收端状态传输数据,如果数据出错会提供重传服务。
套接字连接必须一一对应。面向连接的套接字是可靠的、按序传递的、基于字节的面向连接的数据传输方式套接字。
2.1.6 套接字类型 2:面向消息的套接字(SOCK_DGRAM)
如果向 socket 函数的第二个参数传递 SOCK_DGRAM,则将创建面向消息的套接字。
传输方式特征整理如下:
- 快速传输而非传输顺序
- 传输的数据可能丢失也可能损毁
- 传输的数据有数据边界
- 限制每次传输的数据大小
面向消息的套接字是不可靠的、不按序传递的、以数据的高速传输为目的的套接字。
面向消息的套接字不存在连接的概念。
2.1.7 协议的最终选择
socket 函数的第三个参数决定最终采用的协议。
Q:前面两个参数还不足以决定采用协议吗?为什么还要传递第三个参数?
A:传递前两个参数即可创建所需套接字,大部分情况下可以向第三个参数传递 0,除非遇到以下情况:
『同一协议族中存在多个数据传输方式相同的协议』
本书是基于 IPv4 展开的,且是面向连接的数据传输。满足这两个条件的协议只有 IPPROTO_TCP,因此可以调用如下 socket 函数创建套接字,这种套接字称为 TCP 套接字。
int tcp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
满足 IPv4 协议族,且选择面向消息的数据传输方式 SOCK_DGRAM,满足这两个条件的协议只有 IPPROTO_UDP,因此可以调用如下 socket 函数创建套接字,这种套接字称为 UDP 套接字。
int udp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
2.1.8 面向连接的套接字:TCP 套接字示例
用第一章 hello_server.c 和 hello_client.c 修改部分,以验证TCP套接字性质『传输的数据不存在数据边界』
修改好的代码见:tcp_server.c
文件和 tcp_client.c
文件。
修改好的代码与书上略有不同,结果一致。因为本人认为 read 函数调用次数虽然数值最后和字符串长度一致,因为每次只读取一个字节,但是含义并不相同。所以用 call_cnt 变量代替。
编译运行:
gcc tcp_client.c -o hclient.exe
gcc tcp_server.c -o hserver.exe
./hserver.exe 9190
./hclient.exe 127.0.0.1 9190
运行结果
wzy@wzypc:~/TCP-IP-NetworkNote/chapter-02$ ./hclient.exe 127.0.0.1 9190
Message from server : Hello World!
Function read call count: 13
从运行结果可以看出,服务器端发送了 13 字节的数据,客户端调用 13 次 read 函数进行读取。
2.2 Windows 平台下的实现及验证
略
2.3 习题
- 什么是协议?在收发数据中定义协议有何意义?
协议是为了完成数据交换而定好的约定。意义是能够让计算机之间正常进行数据交换。
- 面向连接的套接字 TCP 套接字传输特性有 3 点,请分别说明。
可靠、按序传输、传输的数据不存在数据边界
- 下面那些是面向消息的套接字的特性?
a. 传输数据可能丢失
c. 以快速传递为目标
e. 与面向连接的套接字不同,不存在连接概念
错误选项
b. 没有数据边界(Boundary)
d. 不限制每次传递数据的大小
- 下列数据适合用哪类套接字进行传输?
演唱会现场直播的多媒体数据(UDP)
某人压缩过的文本文件(TCP)
网上银行用户与银行之间的数据传递(TCP)
- 何种类型的套接字不存在数据边界?这类套接字接收数据时应该注意什么?
TCP。写入速度不要太快,还需正确拿走所需数据。
- 代码修改题
略,大致思路和 2.1.8 代码差不多,多个 sleep 函数大概就能实现。