Linux网络: socket初识
一些概念
简单了解一下TCP,UDP这两个协议,和一些概念
TCP与UDP
学校教过TCP是
- 传输层协议
- 有连接
- 可靠传输
- 面向字节流
而UDP是
- 传输层协议
- 无连接
- 不可靠传输
- 面向数据报
当时完全不知道这些什么意思
网络字节序
网络通信,要接收和发送数据。我们知道机器分为大端和小端存储,那传输数据的时候就需要一个固定的标准,防止出错。
于是有了:TCP/IP规定的网络字节序,低地址高字节
1.发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
2.因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节. 不管这台主机是大端机还是小端机,
都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
3.如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;
主机序列与网络序列的相互转换
在头文件<arpa/inet.h> 给了4个接口
函数 | 含义 | 作用 |
---|---|---|
htons | host to network short | 本地主机的 16 位整数转为网络序 |
htonl | host to network long | 本地主机的 32 位整数转为网络序 |
ntohs | network to host short | 网络序的 16 位整数转为主机序 |
ntohl | network to host long | 网络序的 32 位整数转为主机序 |
Socket编程
sockaddr
sockaddr是网络通信的通用结构体
struct sockaddr {sa_family_t sa_family; // 地址族(协议族),如 AF_INETchar sa_data[14]; // 协议地址(具体内容依赖于地址族)
};
还有另外两个结构体:
- sockaddr_in
struct sockaddr_in {sa_family_t sin_family; // 地址族,必须是 AF_INETuint16_t sin_port; // 端口号(网络字节序)struct in_addr sin_addr; // IP 地址char sin_zero[8]; // 填充字节,保持与 sockaddr 一致
};
特别提示: sin_addr
结构体in_addr封装如下
struct in_addr {in_addr_t s_addr; // IPv4 地址,32 位,网络字节序
};
所以赋值的时候:要对s_addr进行操作
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 点分十进制转网络字节序
地址类型给AF_INET,表示网络通信
2. sockadd_un
struct sockaddr_un {sa_family_t sun_family; // 地址族,必须是 AF_UNIXchar sun_path[108]; // 本地文件路径,作为套接字标识
};
地址类型给AF_UNIX,表示本地通信
IP地址格式
IP地址有点分十进制(“127.0.0.1”)和十六进制4字节序列(0xC0A80101)
点分十进制是为了方便阅读,但是,s_addr是4字节序列的格式,像下面这样
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 点分十进制转网络字节序
就需要调用函数来进行转换
函数 | 作用 |
---|---|
inet_aton | 点分十进制 → 4 字节网络序 |
inet_ntop | 4 字节网络序 → 点分十进制 |
in_addr_t inet_addr(const char *cp);
网络字节序再转换一次?
转换为4字节序列后,需要转换成网络字节序再存入sockaddr_in
好消息,好消息!
inet_addr先完成转换到4字节序列,再转换为网络字节序
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,
socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,
socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
补充:内存置0另一种方法(bzero与memset)
memset很常用,这里补充一个新的接口
void bzero(void *s, size_t n);
//s:指向要清零的内存块的起始地址。
//n:要清零的字节数。
举个例子
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
他等价于
memset(&addr, 0, sizeof(addr));
当然:依然推荐用memset
因为bzero 在 POSIX 标准中已废弃
小结
介绍了socket基本概念和socket初始化的操作