经典书 看过一些之后难免会有各种感慨 以及一些浅薄的拓展 记录于此
TCP是神奇的东西 但是麻烦得很 有一些重点需要关注:
1 套接字
从程序员角度 第一关注的东西 封装了地址族和协议族的概念 全部都由操作系统处理后返回给你一个“小”的非负整数 何为小 操作系统给你分配一个未被占用的最小数字作为套接字描述符 实现方面也比较简单 可以用空闲链表或旁路数组 好像OS实现是直接线性扫描的 有待研究
建立套接字:int socket(int domain, int type, int protocol);
domain:AF表示地址族 PF表示协议族 常用的PF_INET代表使用的是IP协议族 而后在<bits/socket.h>中又找到定义:
#define AF_INET PF_INET
所以在linux(suse)下和win下一样 这两个都没区别 至多有版本划分
type:在创建套接字时要指定通信语义(communication semantics) 常用的两个无非是:
SOCK_STREAM 和SOCK_DGRAM
流传输的关键是传输的数据流不维护边界 提供可靠交付 并维护全双工连接 所以在后面我们看到需要进行循环接收或发送
数据报的特点自然也是有边界的数据 发多少包就应该收到多少 尽力而为服务 无连接
protocol:具体的传输层协议 注意 上面的domain的协议族是指网络层的 而每个网络层协议都可能对应若干个传输层协议
在这里可以写0来让OS来根据前两个参数来选择默认协议
返回值:成功则返回上述描述符 否则返回-1 然后设置errno
常见错误:EACCESS 没有创建此指定协议族套接字的权限
EAFNOSUPPORT 实现不支持指定的地址族
EINVAL 不知名的协议或协议族不可用
EMFILE 进程文件表项上溢
ENFILE 已经达到系统规定的打开文件总数上限
ENOBUFS或ENOMEM 没有可用内存 直到需要的资源被释放之前不能创建套接字
EPROTONOSUPPORT 指定的协议在当前domain中不支持
以上错误中 经常出现的应该是EMFILE和ENFILE了 后者可以通过调整上限值(root)来尝试消除错误 但前者更容易遇到 两者好像引起的原因类似 区别有待研究
套接字的各种设置后来会着重介绍
2 地址族
地址还是IPV4的天下:
struct sockaddr_in
{
uint8_t sin_len; //长度
sa_family_t sin_family; //AF_INET
in_port_t sin_port; //16位的TCP或UDP端口号 网络字节序
struct in_addr sin_addr; //32位IPV4地址 网络字节序
char sin_zero[8]; //没用 凑字节
};
而在当时没有void*的时候 给通用套接字函数传地址是个难题 于是出现了sockaddr结构体:
struct sockaddr
{
uint8_t sa_len;
sa_family_t sa_family;
char sa_data[14];
};
算了一下 发现大小一样 所以一律可以强转为此类型指针传入(而且一般会一起传入长度 所以不会出问题)
字节序和IP地址处理虽然很基础 但是很重要 熟悉些常用函数:
htons htonl ntohs ntohl inet_ntop inet_pton inet_addr
在此不赘述