MSG_PEEK (数据窥探)
使用 MSG_PEEK 选项能够获取接收缓冲区数据的拷贝
- recv() 专用选项,可用于数据预接收
- 指定 MSG_PEEK 选项时,
不会清空缓冲区
- 可用于获取接收缓冲区种的数据量(字节数)
当接收缓冲区中没有数据时,MSG_PEEK 也会导致线程阻塞
下面的代码输出什么?为什么?
static char c_temp[1024 * 2] = {0};
char buf[32] = {0];
sleep(1);
r = recv(client, c_temp, sizeof(c_temp), MSG_PEEK);
c_temp[r] = 0;
printf("r = %d\n", r);
printf("c_temp = %s\n", c_temp);
r = recv(client, buf, sizeof(buf), 0);
buf[r] = 0;
printf("r = %d\n", r);
printf("buf = %s\n", buf);
client.c
#include
#include
#include
#include
#include
#include
#include
int main()
{
int sock = {0};
struct sockaddr_in addr = {0};
int len = 0;
char *test = "Delpin-Tang";
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1) {
printf("socket error\n");
return -1;
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(8888);
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
printf("connect error\n");
return -1;
}
printf("connect success\n");
len = send(sock, test, strlen(test), 0);
getchar();
close(sock);
return 0;
}
server.c
#include
#include
#include
#include
#include
#include
#include
int main()
{
int server = 0;
struct sockaddr_in saddr = {0};
int client = 0;
struct sockaddr_in caddr = {0};
socklen_t asize = 0;
int len = 0;
char buf[32] = {0};
int r = 0;
server = socket(PF_INET, SOCK_STREAM, 0);
if (server == -1) {
printf("server socket error\n");
return -1;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(8888);
if (bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
printf("server bind error\n");
return -1;
}
if (listen(server, 1) == -1) {
printf("server listen error\n");
return -1;
}
printf("server start success\n");
while (1) {
asize = sizeof(caddr);
client = accept(server, (struct sockaddr*)&caddr, &asize);
if (client == -1) {
printf("client accept error");
return -1;
}
printf("client: %d\n", client);
do {
r = recv(client, buf, sizeof(buf), MSG_PEEK);
if (r > 0) {
buf[r] = 0;
printf("r = %d\n", r);
printf("data: %s\n", buf);
r = recv(client, buf, sizeof(buf), 0);
buf[r] = 0;
printf("r = %d\n", r);
printf("data: %s\n", buf);
} else {
printf("no data in receive buf\n"); // 注意这里!(如果未打印表示阻塞)
}
}while (r > 0);
close(client);
}
close(server);
return 0;
}
输出:
server start success
client: 4
r = 11
data: Delpin-Tang
r = 11
data: Delpin-Tang
MSG_DONTWAIT (立即收发模式)
数据收发时不阻塞,立即返回
- sned() : 如果无法将数据送入发送缓冲区,那么直接返回错误 (比如:发送缓冲器 1024 字节大小,欲想发送 2048 字节时)
- recv() : 如果接收缓冲区中没有数据,那么直接返回错误
send() / recv() 返回值:
-1, 错误发生
0, 对端调用 close 关闭
n, 发送 / 接收 的数据量
下面的代码输出什么?为什么?
printf("connect success\n");
sleep(1);
test = "D.T.software";
send(sock, test, strlen(test), 0);
sleep(2);
test = "quit";
send(sock, test, strlen(test), 0);
do {
char buf[32] = {0};
r = recv(client, buf, sizeof(buf), MSG_DONTWAIT);
printf("r = %d\n", r);
if (r > 0) {
buf[r] = 0;
printf("buf = %s\n", buf);
if (strcmp(buf, "quit") == 0) {
break;
}
}
else {
printf("no data receive\n");
sleep(1);
}
}while (1);
server.c
#include
#include
#include
#include
#include
#include
#include
int main()
{
int server = 0;
struct sockaddr_in saddr = {0};
int client = 0;
struct sockaddr_in caddr = {0};
socklen_t asize = 0;
int len = 0;
char buf[32] = {0};
int r = 0;
server = socket(PF_INET, SOCK_STREAM, 0);
if (server == -1) {
printf("server socket error\n");
return -1;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(8888);
if (bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
printf("server bind error\n");
return -1;
}
if (listen(server, 1) == -1) {
printf("server listen error\n");
return -1;
}
printf("server start success\n");
while (1) {
asize = sizeof(caddr);
client = accept(server, (struct sockaddr*)&caddr, &asize);
if (client == -1) {
printf("client accept error");
return -1;
}
printf("client: %d\n", client);
do {
char buf[32] = {0};
r = recv(client, buf, sizeof(buf), MSG_DONTWAIT);
printf("r = %d\n", r);
if (r > 0) {
buf[r] = 0;
printf("buf = %s\n", buf);
if (strcmp(buf, "quit") == 0) {
break;
}
}
else {
printf("no data receive\n");
sleep(1);
}
}while (1);
close(client);
}
close(server);
return 0;
}
client.c
#include
#include
#include
#include
#include
#include
#include
int main()
{
int sock = {0};
struct sockaddr_in addr = {0};
int len = 0;
char *test;
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1) {
printf("socket error\n");
return -1;
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(8888);
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
printf("connect error\n");
return -1;
}
printf("connect success\n");
sleep(1);
test = "D.T.software";
send(sock, test, strlen(test), 0);
sleep(2);
test = "quit";
send(sock, test, strlen(test), 0);
getchar();
close(sock);
return 0;
}
输出:
server start success
client: 4
r = -1
no data receive
r = -1
no data receive
r = 12
buf = D.T.software
r = -1
no data receive
r = -1
no data receive
r = 4
buf = quit
再论阻塞发送模式 (flags → 0)
send()
- 发送数据长度 > 发送缓冲区长度 返回错误
- 发送数据长度 <= 发送缓冲区剩余长度 复制数据到发送缓冲区 返回发送的字节数
- 发送缓冲区剩余长度 < 发送数据长度 <= 发送缓冲区长度 等待发送缓冲器清空 复制数据到发送缓冲区 返回发送的字节数
resv()
- 接收缓冲区中没有数据时 等待数据
- 接收缓冲区数据量 <= 接收区长度 数据全部拷贝到接收区
- 接收缓冲区数据量 > 接收区长度 拷贝部分数据到接收区 返回接收的字节数
通讯框架的迭代增强
int TcpClient_Available(TcpClient *client)
{
static char c_temp[1024 * 2] = {0};
int ret = -1;
Client *c = (Client*)client;
if (c) {
ret = recv(c->fd, c_temp, sizeof(c_temp), MSG_PEEK | MSG_DONTWAIT);
}
return -1;
}
tcp_client.h
#ifndef TCP_CLIENT_H
#define TCP_CLIENT_H
#include "message.h"
typedef void TcpClient;
TcpClient *TcpClient_New();
TcpClient *TcpClient_From(int fd);
int TcpClient_SendMsg(TcpClient *client, Message *msg);
int TcpClient_SendRaw(TcpClient *client, char *buf, int length);
Message *TcpClient_RecvMsg(TcpClient *client);
int TcpClient_RecvRaw(TcpClient *client, char *buf, int length);
int TcpClient_Available(TcpClient *client);
int TcpClient_Connect(TcpClient *client, char *ip, int port);
int TcpClient_IsValid(TcpClient *client);
void TcpClient_Close(TcpClient *client);
void TcpClient_Del(TcpClient *client);
void TcpClient_SetData(TcpClient *client, void *data);
void *TcpClient_GetDate(TcpClient *client);
#endif
tcp_client.c
#include "tcp_client.h"
#include "msg_parser.h"
#include
#include
#include
#include
#include
#include
#include
typedef struct tcp_client {
int fd;
MParser *parser;
void *data;
}Client;
TcpClient *TcpClient_New()
{
return TcpClient_From(-1);
}
TcpClient *TcpClient_From(int fd)
{
Client *ret = malloc(sizeof(Client));
if (ret) {
ret->fd = fd;
ret->parser = MParser_New();
ret->data = NULL;
}
return (ret && ret->parser) ? ret : (free(ret), NULL);
}
int TcpClient_SendMsg(TcpClient *client, Message *msg)
{
int ret = 0;
Client *c = (Client*)client;
if (c && msg) {
int len = Message_Size(msg);
char *data = (char*)Message_H2N(msg);
ret = (send(c->fd, data, len, 0) != -1);
Message_N2H(msg);
}
return ret;
}
int TcpClient_SendRaw(TcpClient *client, char *buf, int length)
{
int ret = 0;
Client *c = (Client*)client;
if (c && buf) {
ret = send(c->fd, buf, length, 0);
}
return ret;
}
Message *TcpClient_RecvMsg(TcpClient *client)
{
Message *ret = NULL;
Client *c = (Client*)client;
if (c) {
ret = MParser_ReadFd(c->parser, c->fd);
}
return ret;
}
int TcpClient_RecvRaw(TcpClient *client, char *buf, int length)
{
int ret = 0;
Client *c = (Client*)client;
if (c && buf) {
ret = recv(c->fd, buf, length, 0);
}
return ret;
}
int TcpClient_Connect(TcpClient *client, char *ip, int port)
{
int ret = TcpClient_IsValid(client);
Client *c = (Client*)client;
if (!ret && ip && c && ((c->fd = socket(PF_INET, SOCK_STREAM, 0)) != -1)) {
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip);
addr.sin_port = htons(port);
ret = (connect(c->fd, (struct sockaddr*)&addr, sizeof(addr)) != -1);
}
return ret;
}
int TcpClient_IsValid(TcpClient *client)
{
int ret = 0;
Client *c = (Client*)client;
if (c) {
struct tcp_info info = {0};
int l = sizeof(info);
getsockopt(c->fd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t*)&l);
ret = (info.tcpi_state == TCP_ESTABLISHED);
}
return ret;
}
void TcpClient_Close(TcpClient *client)
{
Client *c = (Client*)client;
if (c) {
close(c->fd);
c->fd = -1;
MParser_Reset(c->parser);
}
}
void TcpClient_Del(TcpClient *client)
{
Client *c = (Client*)client;
if (c) {
TcpClient_Close(c);
MParser_Del(c->parser);
free(c);
}
}
void TcpClient_SetData(TcpClient *client, void *data)
{
Client *c = (Client*)client;
if (c) {
c->data = data;
}
}
void *TcpClient_GetDate(TcpClient *client)
{
void *ret = NULL;
Client *c = (Client*)client;
if (c) {
ret = c->data;
}
return ret;
}
int TcpClient_Available(TcpClient *client)
{
static char c_temp[1024 * 2] = {0};
int ret = -1;
Client *c = (Client*)client;
if (c) {
ret = recv(c->fd, c_temp, sizeof(c_temp), MSG_PEEK | MSG_DONTWAIT);
}
return ret;
}
MSG_WAITALL(等待数据)
- 接收专用,等待需要的数据完全满足时,recv() 才返回
MSG_MORE(更多数据)
- 发送专用,指示内核不着急将发送缓冲区中的数据进行传输
下面的代码输出什么?为什么?
client.c
#include
#include
#include
#include
#include
#include
#include
int main()
{
int sock = {0};
struct sockaddr_in addr = {0};
int len = 0;
char *test;
int i = 0;
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1) {
printf("socket error\n");
return -1;
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(8888);
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
printf("connect error\n");
return -1;
}
printf("connect success\n");
/***************************************/
test = "Delphi-Tang";
for (i=0; i
server.c
#include
#include
#include
#include
#include
#include
#include
int main()
{
int server = 0;
struct sockaddr_in saddr = {0};
int client = 0;
struct sockaddr_in caddr = {0};
socklen_t asize = 0;
char buf[32] = {0};
int r = 0;
server = socket(PF_INET, SOCK_STREAM, 0);
if (server == -1) {
printf("server socket error\n");
return -1;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(8888);
if (bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
printf("server bind error\n");
return -1;
}
if (listen(server, 1) == -1) {
printf("server listen error\n");
return -1;
}
printf("server start success\n");
while (1) {
asize = sizeof(caddr);
client = accept(server, (struct sockaddr*)&caddr, &asize);
if (client == -1) {
printf("client accept error");
return -1;
}
printf("client: %d\n", client);
do {
/***************************************/
int len[2] = {11, 4};
int i = 0;
for (i=0; i<2; ++i) {
r = recv(client, buf, len[i], MSG_WAITALL);
printf("r = %d\n", r);
if (r > 0) {
buf[r] = 0;
printf("data = %s\n", buf);
if (strcmp(buf, "quit") == 0) {
break;
}
}
}
/***************************************/
}while (0);
close(client);
}
close(server);
return 0;
}
输出:
client: 4
r = 11
data = Delphi-Tang
r = 4
data = quit
思考:如何使用 UDP 进行数据收发?