内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,网络数据流同样有大端小端之分。
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据。为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
h表示host,n表示network,l表示32位长整数,s表示16位短整数。
htonl表示将32位的长整数从主机字节序转换为网络字节序。
ntohl表示将32位的长整数从网络字节序转换为主机字节序。
socket套接字通常指的是封装了ip和port的结构体,其是网络编程中的一种通信机制,支持TCP/IP的网络通信的基本操作单元,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。
// 创建 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);
套接字分类:
理论上是三种应用场景,对应的应该是三套接口,但是Linux设计套接字的时候不想设计过多的接口,所以Linux将所有的接口进行了统一,只使用sockaddr结构体来描述这三种场景。 但是真正在基于IPv4编程时, 使用的数据结构是sockaddr_in; 这个结构里主要有三部分信息: 地址类型, 端口号, IP地址。
#include
#include
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
//参数
domain:指定网络层的协议
AF_INT: 使用ipv4版本的ip协议
AF_INT6:使用ipv6版本的ip协议
AF_UNIX:本地通信
type: 指定套接字的类型
SOCK_DGRAM :使用UDP数据报套接字
SOCK_STREAM:使用TCP字节流套接字
protocol:指定使用的协议
0:使用套接字类型对应的默认协议
int bind(int socket, const struct sockaddr* address,socklen_t address_len);*
//参数
sockfd: socket函数返回的套接字描述符
addr: 地址信息结构体成员,struct sockaddr 是一个通用地址信息结构
address_len:地址信息结构的长度
ssize_t sendto(int sockfd, const void* buf, size_t len, int flags, const struct sockaddr* dest_addr, socklen_t addrlen);
//参数
sockfd: 套接字描述符
buf: 要发送的数据
len: 发送数据的长度
flags: 0-阻塞发送
dest_addr: 目标主机的地址信息结构体
addrlen: 目标主机地址信息结构体的长度
//返回值
成功返回发送的字节数量,失败返回-1
ssize_t recvfrom(int sockfd, void* buf, size_t len, int flags,struct sockaddr* src_addr, socklen_t* addrlen);
//参数
sockfd: 套接字描述符
buf: 将数据接收到buf当中
len: buf的最大接收能力
flags: 0-阻塞接收
src_addr: 数据来源的主机地址信息结构体
addrlen: 输入输出型参数
//返回值
成功则返回实际接收到的字符数,失败返回-1,错误原因会存于errno中
close(int sockfd);
//sockfd: 套接字描述符
#include
#include
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
//参数
domain:指定网络层的协议
AF_INT: 使用ipv4版本的ip协议
AF_INT6:使用ipv6版本的ip协议
AF_UNIX:本地通信
type: 指定套接字的类型
SOCK_DGRAM :使用UDP数据报套接字
SOCK_STREAM:使用TCP字节流套接字
protocol:指定使用的协议
0:使用套接字类型对应的默认协议
//返回值:
socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符,程序可以像读写文件一样用read/write在网络上收发数据;调用出错则返回-1
int bind(int socket, const struct sockaddr* address,socklen_t address_len);*
//参数
sockfd: socket函数返回的套接字描述符
addr: 地址信息结构体成员,struct sockaddr 是一个通用地址信息结构
address_len:地址信息结构的长度
//listen()声明sockfd处于监听状态
int listen(int sockfd, int backlog);
//参数
sockfd: 套接字描述符
backlog:已完成连接队列的大小
//返回值:
成功:0
失败:-1
当客户端和服务端进行三次握手的时候会存在两种状态:连接还未建立和连接已建立,此时操作系统内核中就会存在两个队列:未完成连接队列和已完成连接队列。当完成三次握手后会由未完成连接队列放到已完成连接队列,而backlog就是已完成连接队列的大小,backlog影响了服务端并发接收连接的能力。
//从已经完成连接队列中获取已经完成三次握手的连接,没有连接时,调用accept会阻塞等待。
int accept(int sockfd, struct sockaddr* addr, socklen_t * addrlen);
//参数
sockfd:套接字描述符(listen_sockfd)
addr:输出型参数,保存客户端地址信息结构(客户端IP,客户端的端口)
addrlen:输入输出参数,传入缓冲区的大小,传出客户端地址信息结构的长度
//返回值
成功:返回新连接的套接字描述符
失败:返回-1
三次握手的时候是对listen_sockfd进行操作,当调用accept()会在Tcp服务端内部创建一个新的套接字new_sockfd,三次握手之后的数据收发都是多new_sockfd进行操作。
//客户端需要调用connect()连接服务器
int connect(int sockfd, const struct sockaddr * addr,socklen_t addrlen);
//参数
sockfd:套接字描述符(listen_sockfd)
addr:服务端地址信息结构(服务端IP,服务端的端口)
addrlen:服务端地址信息结构的长度
//返回值
成功:返回0
小于0,连接失败
ssize_t send(int sockfd, const void * buf, size_t len, int flags);
//参数
sockfd:套接字描述符(new_sockfd)
buf:待要发送的数据
len:发送数据的长度
flags:
0:阻塞发送
MSG_OOB:发送带外数据,在紧急情况下所产生的数据,会越过前面进行排队的数据优先进行发送。
//返回值
大于0:返回发送的字节数量
-1:发送失败
ssize_t recv(int sockfd, void * buf, size_t len, int flags);
//参数
sockfd:套接字描述符(new_sockfd)
buf:将接收的数据放到buf
len:buf的最大接收能力
flags:0:阻塞发送;如果客户端没有发送数据,调用recv会阻塞
//返回值
大于0:正常接收了多少字节数据
等于0:对端将连接关闭了
小于0:接受失败
close(int sockfd);
//sockfd: 套接字描述符
netstat -anp | grep [端口号]
sockaddr_in中的成员struct in_addr sin_addr表示32位 的IP 地址,但是我们通常用点分十进制的字符串表示IP 地址,以下函数可以在字符串表示 和in_addr表示之间转换
字符串转in_addr的函数:
in_addr转字符串的函数:
inet_ntoa函数把这个返回结果放到了静态存储区,这个时候不需要我们手动进行释放。
UDP案例
UDP · 程序员Jared/Linux - 码云 - 开源中国 (gitee.com)
TCP案例
TCP · 程序员Jared/Linux - 码云 - 开源中国 (gitee.com)