一、MSG_WAITALL 理解
MSG_WAITALL标志,如果recv设置了这个标志,当没有收到请求的字节数时,recv函数会阻塞。然而,当被信号打断,或者发生了错误,或者断开了连接,或者接收到的下一个数据和已经接收的数据的数据类型不一致时,recv函数即使没有收到请求的字节数,也会返回,返回一个错误。
二、代码示例
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
int server_init(const char *ipstr, ushort port, int backlog)
{
//创建流式IPv6套接字
int s = socket(AF_INET6, SOCK_STREAM, 0);
if(-1 == s){
perror("socket");
return -1;
}
//初始本地地址结构
struct sockaddr_in6 addr = {
.sin6_family = AF_INET6, //IPv6
.sin6_port = htons(port), //服务器端口号
//.sin6_scope_id = 0,
};
//服务器ip,in6addr_any是一个宏,可以是本地任意IP
//给监听套接字绑定的IP是in6addr_any,有两个好处
//1.本地有多个网卡,那么客户端可以通过任意网卡连接的网络来连接服务器
//2.服务器运行在不同主机上,不用改代码重新编译,把当前主机的IP告诉客户端就可以了
//弊端:由于没有指定唯一IP,故此,网络收发包的时候要路由下(找哪个网卡来收发数据)
addr.sin6_addr =in6addr_any;
if(ipstr){
if(inet_pton(AF_INET6, ipstr, addr.sin6_addr.s6_addr16) <= 0){
addr.sin6_addr = in6addr_any;
}else{
addr.sin6_scope_id = 0;//网卡的id
}
}
//地址结构的长度
socklen_t len = sizeof(addr);
//给套接字绑定本地ip和端口
if(0 > bind(s, (struct sockaddr *)&addr, len)){
perror("bind");
goto ERR_STEP;
}
//设置监听数
if(0 > listen(s, backlog)){
perror("listen");
goto ERR_STEP;
}
return s;
ERR_STEP:
close(s);
return -1;
}
int main()
{
int s = server_init(NULL, 59999, 10);
if(0 > s){
return -1;
}
printf("wait for client.\n");
struct sockaddr_in6 c_addr;
//c_len输入参数:告诉accept地址有多少字节数;输出参数:实际接收的字节数
socklen_t c_len = sizeof(c_addr);
int rws = accept(s, (struct sockaddr *)&c_addr, &c_len);
if(0 > rws){
perror("accept");
goto ERR_STEP;
}
//把客户端的IP转成字符串IP并打印
char ipstr[INET6_ADDRSTRLEN];
if(inet_ntop(AF_INET6, c_addr.sin6_addr.s6_addr16, ipstr, INET6_ADDRSTRLEN)){
printf("client ip : %s\n", ipstr);
}
//打印客户端的端口
printf("client port : %u\n", ntohs(c_addr.sin6_port));
//如果知道发送对端发送的字节,使用MSG_WAITALL就可以解决粘包问题
//强调:MSG_WAITALL不是解决粘包问题用的
//熟悉粘包造成原因:内核协议层会把应用传递的缓存数据依据协议拆包、组包,从而造成接收端少收,多收
//从而造成粘包
while(1){
#define MAX (1024*1024)
static char buf[MAX];
//接收数据:从套接字接收缓存读出数据
//使用MSG_WAITALL标志:表示,想读出MAX字节数据,则一定要读出才会返回
//否则返回就是出错或连接断开了
int num = recv(rws, buf, MAX, MSG_WAITALL);
if(-1 == num){
//本地网络出现问题
perror("read");
break;
}else if(0 == num){
//连接断开
printf("link off\n");
break;
}
printf("recv %d bytes\n", num);
}
close(rws);
ERR_STEP:
close(s);
printf("server is leave.\n");
return 0;
}