#include <sys/socket.h> //成功字节,失败-1 ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags); //成功字节(收到EOF返回0),失败-1 ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
flags | 含义 | send | recv |
---|---|---|---|
MSG_OOB | 用于传输带外数据(Out-of-band data) | * | * |
MSG_PEEK | 验证输入缓冲中是否存在接收的数据 | * | |
MSG_DONTROUTE | 数据传输过程中不参照路由(Routing)表,在本地(Local)网络中寻找目的地 | * | |
MSG_DONTWAIT | 非阻塞(Non-blocking)I/O | * | * |
MSG_WAITALL | 防止函数返回,直到接收全部请求的字节数 | * |
MSG_OOB用于创建特殊发送方法和通道以发送紧急消息。
oob_send.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define BUF_SIZE 30 void error_handling(char *message); int main(int argc, char *argv[]) { int sock; struct sockaddr_in addr; socklen_t addr_size; if(argc!=3) { printf("Usage : %s <IP> <port>\n", argv[0]); exit(1); } sock = socket(PF_INET, SOCK_STREAM, 0); if(sock==-1) error_handling("socket() error"); addr_size = sizeof(addr); memset(&addr, 0, addr_size); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(argv[1]); addr.sin_port = htons(atoi(argv[2])); if(connect(sock, (struct sockaddr*)&addr, addr_size)==-1) error_handling("connect() error"); write(sock, "123", strlen("123")); send(sock, "4", strlen("4"), MSG_ODB); write(sock, "567", strlen("567")); send(sock, "890", strlen("890"), MSG_ODB); close(sock); return 0; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }
gcc oob_send.c -o send ./send 127.0.0.1 9190
oob_recv.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <sys/socket.h> #include <netinet/in.h> #include <fcntl.h> #define BUF_SIZE 30 void error_handling(char *message); void urg_handler(int signo); int listen_sock; int connect_sock; int main(int argc, char *argv[]) { struct sockaddr_in listen_addr; struct sockaddr_in connect_addr; socklen_t addr_size; int str_len, state; struct sigaction act; char buf[BUF_SIZE]; if(argc!=2) { printf("Usage : %s <port>\n", argv[0]); exit(1); } listen_sock= socket(PF_INET, SOCK_STREAM, 0); if(listen_sock==-1) error_handling("socket() error"); addr_size = sizeof(struct sockaddr_in); memset(&listen_addr, 0, addr_size); listen_addr.sin_family = AF_INET; listen_addr.sin_addr.s_addr = inet_addr(argv[1]); listen_addr.sin_port = htons(atoi(argv[2])); if(bind(listen_sock, (struct sockaddr*)&listen_addr, addr_size)==-1) error_handling("bind() error"); listen(listen_sock, 5); connect_sock=accept(listen_sock, (struct sockaddr*)&listen_addr, &addr_size); //fcntl函数控制文件描述符 //指定connect_sock套接字的拥有者(F_SETOWN)为getpid()表示的进程 //套接字的拥有者实际为操作系统 //这里指负责套接字所有事务的主体 //处理SIGURG信号的主体为当前进程,并非所有进程都会处理SIGURG信号 //文件描述符connect_sock指向的套接字引发的SIGURG信号处理进程变为将getpid函数返回值用作ID的进程 fcntl(connect_sock, F_SETOWN, getpid()); act.sa_handler=urg_handler; sigemptyset(&act.sa_mask); act.sa_flags=0; state=sigaction(SIGURG, &act, 0); //收到MSG_OOB紧急信息将产生SIGURG信号 while((str_len=recv(connect_sock, buf, sizeof(buf), 0))!=0) { if(str_len==-1) continue; buf[str_len]=0; puts(buf); } close(connect_sock); close(listen_sock); return 0; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); } void urg_handler(int signo) { int str_len; char buf[BUF_SIZE]; str_len=recv(connect_sock, buf, sizeof(buf)-1, MSG_OOB);//一次urg_handler调用只能读取1个字节 buf[str_len]=0; printf("Urgent message: %s\n", buf); }
gcc oob_recv.c -o recv ./recv 9190
MSG_OOB(Out-of-band)并非真正意义上的"带外数据"(通过单独的通信路径高速传输数据)。
TCP不提供,可利用紧急模式(Urgen mode)传输。
TCP保持传输顺序的特性不会改变,MSG_OOB的真正意义在于督促数据接收对象尽快处理数据。
MSG_DONTWAIT,非阻塞。
MSG_PEEK,recv读取缓冲数据后不删除。
recv+MSG_DONTWAIT|MSG_PEEK,非阻塞方式验证带读取数据是否存在。
peek_send.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> void error_handling(char *message); int main(int argc, char *argv[]) { int sock; struct sockaddr_in addr; socklen_t addr_size; if(argc!=3) { printf("Usage : %s <IP> <port>\n", argv[0]); exit(1); } sock = socket(PF_INET, SOCK_STREAM, 0); if(sock==-1) error_handling("socket() error"); addr_size = sizeof(struct sockaddr_in); memset(&addr, 0, addr_size); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(argv[1]); addr.sin_port = htons(atoi(argv[2])); if(connect(sock, (struct sockaddr*)&addr, addr_size)==-1) error_handling("connect() error"); write(sock, "123", strlen("123")); close(sock); return 0; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }
gcc peek_send.c -o send ./send 127.0.0.1 9190
peek_recv.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <sys/socket.h> #include <arpa/inet.h> #define BUF_SIZE 30 void error_handling(char *message); int main(int argc, char *argv[]) { int listen_sock; int connect_sock; struct sockaddr_in listen_addr; struct sockaddr_in connect_addr; socklen_t addr_size; int str_len, state; char buf[BUF_SIZE]; if(argc!=2) { printf("Usage : %s <port>\n", argv[0]); exit(1); } listen_sock= socket(PF_INET, SOCK_STREAM, 0); if(listen_sock==-1) error_handling("socket() error"); addr_size = sizeof(struct sockaddr_in); memset(&listen_addr, 0, addr_size); listen_addr.sin_family = AF_INET; listen_addr.sin_addr.s_addr = inet_addr(argv[1]); listen_addr.sin_port = htons(atoi(argv[2])); if(bind(listen_sock, (struct sockaddr*)&listen_addr, addr_size)==-1) error_handling("bind() error"); listen(listen_sock, 5); connect_sock=accept(listen_sock, (struct sockaddr*)&listen_addr, &addr_size); while(1) { str_len=recv(connect_sock, buf, sizeof(buf)-1, MSG_PEEK|MSG_DONTWAIT); if(str_len>0) break; } buf[str_len]=0; printf("Buffering %d bytes: %s\n", str_len, buf); str_len=recv(connect_sock, buf, sizeof(buf)-1, 0); buf[str_len]=0; printf("Read again: %s\n", buf); close(connect_sock); close(listen_sock); return 0; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); } void urg_handler(int signo) { int str_len; char buf[BUF_SIZE]; str_len=recv(connect_sock, buf, sizeof(buf)-1, MSG_OOB);//一次urg_handler调用只能读取1个字节 buf[str_len]=0; printf("Urgent message: %s\n", buf); }
gcc oob_recv.c -o recv ./recv 9190
对数据进行整合传输及发送的函数。
writev将分散保存在多个缓存中的数据一并发送,readv由多个缓冲分别接收,可以减少I/O函数的调用次数。
#include <sys/uio.h> //成功字节数,失败-1 ssize_t writev(int fileds, const struct iovec *iov, int iovcnt);
struct iovec { void *iov_base;//缓冲地址 size_t iov_len;//缓冲大小 };
writev.c
#include <stdio.h> #include <sys/uio.h> int main(int argc, char *argv[]) { struct iovec vec[2]; char buf1[]="ABCDEFG"; char buf2[]="1234567"; int str_len; vec[0].iov_base=buf1; vec[0].iov_len=3; vec[1].iov_base=buf2; vec[1].iov_len=4; str_len=writev(1, vec, 2); puts(""); printf("Write bytes: %d\n", str_len); return 0; }
gcc writev.c -o wv ./wv //ABC1234 //Write bytes: 7
#include <sys/uio.h> //成功字节数,失败-1 ssize_t readv(int fileds, const struct iovec *iov, int iovcnt);
readv.c
#include <stdio.h> #include <sys/uio.h> #define BUF_SIZE 100 int main(int argc, char *argv[]) { struct iovec vec[2]; char buf1[BUF_SIZE]={0, }; char buf2[BUF_SIZE]={0, }; int str_len; vec[0].iov_base=buf1; vec[0].iov_len=5; vec[1].iov_base=buf2; vec[1].iov_len=BUF_SIZE; str_len=readv(0, vec, 2); printf("Read bytes: %d\n", str_len); printf("First message: %s\n", buf1); printf("Second message: %s\n", buf2); return 0; }
gcc readv.c -o rv ./rv I like TCP/IP socket programming //Read bytes: 33 //First message: I lik //Second message: e TCP/IP socket programming
Out-of-band数据属于异常(不同寻常的程序执行流)。Windows平台利用select的这一特性接收Out-of-band数据。
oob_send_win.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <winsock2.h> #define BUF_SIZE 30 void ErrorHanding(char *message); int main(int argc, char *argv[]) { WSADATA wsaData; SOCKET sock; SOCKADDR_IN addr; int addr_size; if(argc!=3) { printf("Usage : %s <IP> <port>\n", argv[0]); exit(1); } if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0) ErrorHanding("WSAStartup() error!"); sock = socket(PF_INET, SOCK_STREAM, 0); if(sock==INVALID_SOCKET) ErrorHanding("socket() error"); addr_size = sizeof(SOCKADDR_IN); memset(&addr, 0, addr_size); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(argv[1]); addr.sin_port = htons(atoi(argv[2])); if(connect(sock, (SOCKADDR*)&addr, addr_size)==SOCKET_ERROR) ErrorHanding("connect() error"); send(sock, "123", strlen("123"), 0); send(sock, "4", strlen("4"), MSG_OOB); send(sock, "567", strlen("567"), 0); send(sock, "8905ab", strlen("8905ab"), MSG_OOB); //MSG_OOB只发生一个字符b,"8905a"普通字符串 closesocket(sock); WSACleanup(); return 0; } void ErrorHanding(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }
cl /EHsc oob_send_win.c /Fe:sendWin.exe /link ws2_32.lib sendWin 127.0.0.1 9190
oob_recv_win.c
#include <stdio.h> #include <stdlib.h> #include <winsock2.h> #define BUF_SIZE 30 void ErrorHanding(char *message); int main(int argc, char *argv[]) { WSADATA wsaData; SOCKET listen_sock; SOCKET connect_sock; SOCKADDR_IN listen_addr; SOCKADDR_IN connect_addr; int addr_size; int str_len; char buf[BUF_SIZE]; int result; int i; fd_set reads, reads_copy; fd_set excepts, excepts_copy; struct timeval timeout; if(argc!=2) { printf("Usage : %s <port>\n", argv[0]); exit(1); } if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0) ErrorHanding("WSAStartup() error!"); listen_sock = socket(PF_INET, SOCK_STREAM, 0); if(listen_sock==INVALID_SOCKET) ErrorHanding("socket() error"); addr_size = sizeof(SOCKADDR_IN); memset(&listen_addr, 0, addr_size); listen_addr.sin_family = AF_INET; listen_addr.sin_addr.s_addr = htonl(INADDR_ANY); listen_addr.sin_port = htons(atoi(argv[1])); if(bind(listen_sock, (struct sockaddr*)&listen_addr, addr_size)==SOCKET_ERROR) ErrorHanding("bind() error"); if(listen(listen_sock, 5)==SOCKET_ERROR) ErrorHanding("listen() error"); FD_ZERO(&reads); FD_SET(listen_sock, &reads); FD_ZERO(&excepts); FD_SET(listen_sock, &excepts); while(1) { reads_copy = reads; excepts_copy = excepts; timeout.tv_sec = 55; timeout.tv_usec = 5000; if((result = select(0, &reads_copy, 0, &excepts_copy, &timeout))==SOCKET_ERROR) break; if(result==0) continue; for(i=0; i<reads.fd_count; i++) { if(FD_ISSET(reads.fd_array[i], &excepts_copy)) { if(reads.fd_array[i]==listen_sock) { ErrorHanding("listen_sock error"); } else { //MSG_OOB,一次发送一个字符 //MSG_OOB,一次可以读取多个字符 str_len = recv(reads.fd_array[i], buf, BUF_SIZE-1, MSG_OOB); buf[str_len] = 0; printf("Urgent message: %s\n", buf); } } if(FD_ISSET(reads.fd_array[i], &reads_copy)) { if(reads.fd_array[i]==listen_sock) { connect_sock = accept(listen_sock, (SOCKADDR*)&connect_addr, &addr_size); FD_SET(connect_sock, &reads); FD_SET(connect_sock, &excepts); printf("connected client: %d\n", connect_sock); } else { str_len = recv(reads.fd_array[i], buf, BUF_SIZE-1, 0); if(str_len==0) { FD_CLR(reads.fd_array[i], &reads); FD_CLR(reads.fd_array[i], &excepts); closesocket(reads.fd_array[i]); printf("closed client: %d\n", reads.fd_array[i]); } else { buf[str_len] = 0; puts(buf); } } } } } closesocket(listen_sock); WSACleanup(); return 0; } void ErrorHanding(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }
cl /EHsc oob_recv_win.c /Fe:recvWin.exe /link ws2_32.lib recvWin 9190