Both write() and send() can send and receive data, what's the difference?
- send can use flags to specify optional information, where 0 means the default send behavior
- send When flags is 0, it will wait for the send buffer data to be emptied before putting the data into the send buffer and then return
- write cannot specify optional information and does not block
Both read() and recv() can receive data, what's the difference?
- recv can use flags to specify optional information, where 0 means the default receive behavior
- recv When flags is 0, it will wait for the receive buffer to have data before taking the data out of the receive buffer and return
- read cannot specify optional information and does not block
Data sending and receiving options
#include <sys/socket.h>
ssize_t send(int socketfd, const void *buf, size_t nbytes, int flags);
ssize_t recv(int socketfd, void *buf, size_t nbytes, int flags);
flags - specify optional information when sending and receiving data, where 0 is the default sending and receiving behavior
flags option information (partial)
optional | meaning | send | recv |
MSG_OOB | Used to transmit out-of-band data (Out Of Band Data), that is: urgent data (priority transmission) | √ | √ |
MSG_PEEK | Verify that there is data in the receive buffer (what data is there) | √ | |
MSG_DONTROUTE | The data transmission process does not go through the routing table, but finds the destination in the local area network | √ | |
MSG_DONTWAIT | Non-blocking mode, return immediately when data is sent and received | √ | √ |
MSG_WAITALL | Do not return early until all requested data has been received | √ | |
MSG_MORE | There is more data to send, instructing the kernel to wait for data | √ | |
... | ... |
Note: 不同的操作系统对上述可选项的支持不同,实际工程开发时,需要事先对目标系统中支持的可选项进行调研
MSG_OOB (out-of-band data, urgent data)
native definition
- Data transmitted independently using a different channel from normal data
- The priority of out-of-band data is higher than that of ordinary data (prioritized transmission, the peer receives priority)
Out-of-band data in TCP
- Due to native design limitations,
TCP无法提供真正意义上的带外数据
- In TCP, urgent data can only be transmitted through the flag in the header of the transport protocol, and
长度仅1字节
TCP out-of-band data implementation principle
The URG pointer points to the next position of the urgent message, that is: the previous byte of the position pointed to by the URG pointer stores the urgent message
接收端优先接收紧急数据,并将其存储到特殊缓冲区,之后再接收普通数据
紧急数据:0x03
普通数据:0x01,0x02
TCP Out-of-Band Data Handling Policy
- Since TCP is designed to stream data, true out-of-band data is not possible
Marked urgent data can be received early, into a special buffer (only one byte)
- There is at most one urgent data per TCP packet
- The special buffer only stores the latest urgent data (if not received in time, it will be lost)
What happens when sending and receiving data in the following way
Send normal data, receive in normal mode: normal, data arrive in sequence Send normal data, receive in emergency mode: return an error to send urgent data, receive in normal mode: block normal data recv to send urgent data, receive in urgent mode: normal, receive urgent data data
Programming experiment: sending and receiving TCP urgent data
client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
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), MSG_OOB);
getchar();
close(sock);
return 0;
}
server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
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_OOB);
if (r > 0) {
buf[r] = 0;
printf("OOB: %s\n", buf);
}
r = recv(client, buf, sizeof(buf), 0);
if (r > 0) {
buf[r] = 0;
printf("NORMAL: %s\n", buf);
}
}while (r > 0);
close(client);
}
close(server);
return 0;
}
output:
server start success
client: 4
NORMAL: Delpin-Tan // 注意,普通数据先输出 (因为当 flags 为 MSG_OOB 时不阻塞,而为 0 时会阻塞,直到接收到数据)
OOB: g // 注意,仅输出最后一个字符 !!
Small question: In actual development, how to efficiently receive TCP urgent data?
Use select to receive urgent data
When normal data and urgent data are received on the socket, select will return immediately
- Ordinary data: the socket is in a data readable state (can read ordinary data)
- Urgent data: the socket handles abnormal status (emergency data can be read)
Example of emergency data reception
num = select(max + 1, &temp, 0, &except, &timeout);
if (num > 0) {
for (i=1; i<=max; ++i) {
if (FD_ISSET(i, &except)) {
if (i != server) {
char buf[32] = {0};
int r = recv(i, buf, sizeof(buf), MSG_OOB);
if (r > 0) {
buf[r] = 0;
printf("OOB: %s\n", buf);
}
}
}
if (FD_ISSET(I, &temp)) {
// ...
}
}
}
Programming Experiment: Using select to Receive Urgent Data
client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
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), MSG_OOB);
getchar();
close(sock);
return 0;
}
select-server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int server_handler(int server)
{
struct sockaddr_in addr = {0};
socklen_t asize = sizeof(addr);
return accept(server, (struct sockaddr*)&addr, &asize);
}
int client_handler(int client)
{
char buf[32] = {0};
int ret = recv(client, buf, sizeof(buf) - 1, 0);
if (ret > 0) {
buf[ret] = 0;
printf("Recv: %s\n", buf);
}
return ret;
}
int clint_except_handler(int client)
{
char buf[2] = {0};
int r = recv(client, buf, sizeof(buf), MSG_OOB);
if (r > 0) {
buf[r] = 0;
printf("OOB: %s\n", buf);
}
return r;
}
int main()
{
int server = 0;
struct sockaddr_in saddr = {0};
int max = 0;
int num = 0;
fd_set reads = {0};
fd_set temps = {0};
fd_set except = {0};
struct timeval timeout = {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 listeb error\n");
return -1;
}
printf("server start success\n");
FD_ZERO(&reads);
FD_SET(server, &reads);
max = server;
while (1) {
temps = reads;
except = reads;
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
num = select(max + 1, &temps, 0, &except, &timeout);
if (num > 0) {
int i = 0;
for (i=0; i<=max; ++i) {
if (FD_ISSET(i, &except)) {
if (i != server) {
clint_except_handler(i);
}
}
if (FD_ISSET(i, &temps)) {
if (i == server) {
int client = server_handler(server);
if (client > -1) {
FD_SET(client, &reads);
max = (client > max) ? client : max;
printf("accept client: %d\n", client);
}
} else {
int r = client_handler(i);
if (r == -1) {
FD_CLR(i, &reads);
close(i);
}
}
}
}
int client = server_handler(server);
}
}
}
output:
server start success
accept client: 4
OOB: g
Recv: Delpin-Tan
summary
- read() / write() can be used to send and receive ordinary data (without extended functions)
- send() / recv() can be extended with option information
- TCP urgent data identifies 256 urgent events (abnormal events)
- Through select, urgent data can be processed in time, and ordinary data can be distinguished
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。