send与recv
原型:
ssize_t send(int sockfd, const void *buf,size_t nbytes, int flags);
ssize_t recv(int sockfd, const void *buf,size_t nbytes, int flags);
最后一个参数可取值
MSG_OOB 发送/接收带外数据(out-of-banddata);
MSG_PEEK 返回来的数据并不会在系统内删除,如果再调用,recv()会返回相同的数据内容;
MSG_WAITALL 强迫收到地三个参数所指定的大小的数据后才返回,除非有错误或者信号产生;
MSG_DONTWAIT 调用I/O函数时不阻塞
MSG_NISIGNAL 此操作不愿被SIGPIPE信号中断
注:MSG_OOB用于发送”带外数据”紧急消息,实际上TCP不存在真正意义上的”带外数据”,真正意义上的带外数据是通过单独的通信路径告诉传输的消息,但是TCP不另外提供这种通道。只是使用紧急模式传输,其意义在于督促数据接收对象尽快处理数据,TCP的”顺序传输”特性依然成立
示例:
服务器端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <signal.h>
#include <fcntl.h>
#define BUF_SIZE 30
void error_handling(const char *message);
void urg_handler(int signo);
int acpt_sock;
int recv_sock;
int main(int argc, const char * argv[])
{
struct sockaddr_in recv_adr, serv_adr;
int str_len, state;
socklen_t serv_adr_sz;
struct sigaction act;
char buf[BUF_SIZE];
if( 2 !=argc )
{
printf("Usage: %s <port> \n", argv[0]);
exit(1);
}
act.sa_handler = urg_handler; //windows下使用select处理,并且OOB数据属于异常
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
acpt_sock = socket(PF_INET, SOCK_STREAM, 0);
memset(&recv_adr, 0, sizeof(recv_adr));
recv_adr.sin_family = AF_INET;
recv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
recv_adr.sin_port = htons(atoi(argv[1]));
if( -1 == bind(acpt_sock, (struct sockaddr *) &recv_adr, sizeof(recv_adr)) )
error_handling("bind() error");
if( -1 == listen(acpt_sock, 5) )
error_handling("listen() error");
serv_adr_sz = sizeof(serv_adr);
recv_sock = accept(acpt_sock, (struct sockaddr *)&serv_adr, &serv_adr_sz);
//文件描述符recv_sock所指向的套接字触发的SIGURG信号由进程ID为getpid()的返回值的进程处理,特别是多进程的时候需要设置由谁处理
fcntl(recv_sock, F_SETOWN, getpid());
state = sigaction(SIGURG, &act, 0); //收到MSG_OOB紧急消息时,操作系统将产生SIGURG,并调用注册的处理函数
while (0 != (str_len = recv(recv_sock, buf, sizeof(buf), 0)) )//循环接收普通消息
{
if( -1 == str_len )
continue;
buf[str_len] = 0;
puts(buf);
}
close(recv_sock);
close(acpt_sock);
return 0;
}
void urg_handler(int signo)
{
int str_len;
char buf[BUF_SIZE];
str_len = recv(recv_sock, buf, sizeof(buf) - 1, MSG_OOB); //接收MSG_OOB紧急消息
buf[str_len] = 0;
printf("Urgent message : %s \n", buf);
}
void error_handling(const char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
客户端
#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(const char *message);
int main(int argc, const char * argv[]) {
int sock;
struct sockaddr_in recv_adr;
if( 3 != argc )
{
printf("Usage: %s <IP> <port> \n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_STREAM, 0);
if( -1 == sock )
error_handling("socket() error");
memset(&recv_adr, 0, sizeof(recv_adr));
recv_adr.sin_family = AF_INET;
recv_adr.sin_addr.s_addr = inet_addr(argv[1]);
recv_adr.sin_port = htons(atoi(argv[2]));
if( -1 == connect(sock, (struct sockaddr *) &recv_adr, sizeof(recv_adr)) )
error_handling("connect() error");
write(sock, "123", strlen("123")); //发送普通消息
send(sock, "4", strlen("4"), MSG_OOB); //发送紧急消息
write(sock, "567", strlen("567"));
send(sock, "890", strlen("890"), MSG_OOB);
close(sock);
return 0;
}
void error_handling(const char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
writev与readv
通过函数writev可以将多个buf中的数据整合后写入套接字的输出缓冲区一并发送,调用readv函数可以使用多个buf分别接收输入缓冲区中的数据,因此适当使用这两个函数会减少I/O函数调用次数,而且在没开启Nagle算法时,使用函数writev相比较于使用函数write很可能会减少数据包个数,故使用这两个函数有助于提高数据通信效率
writev函数
ssize_t writev(int filedes, const structiovec *iov, int iovcnt);//成功返回发送字节数,失败返回-1
参数一: 数据传递目标的文件描述符,可以是套接字、文件或标准输出描述符
参数二: 指向iovec结构体数组的指针,结构体中包含待发送数据的地址和待发送数据大小
参数三: 第二个参数中结构体数组的长度
结构体iovec
struct iovec
{
void *iov_base; /* Starting address*/
size_t iov_len; /* Length in bytes */
};
示例:
#include <stdio.h>
#include <sys/uio.h>
int main(int argc, const 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); //1为标准输出
puts("");
printf("Write bytes: %d \n", str_len);
return 0;
}
readv函数
ssize_t readv(int filedes, const structiovec *iov, int iovcnt); //成功返回接受字节数,失败返回-1
示例:
#include <stdio.h>
#include <sys/uio.h>
#define BUF_SIZE 100
int main(int argc, const char * argv[])
{
struct iovec vec[2];
char buf1[BUF_SIZE] = { 0 };
char buf2[BUF_SIZE] = { 0 };
int str_len;
vec[0].iov_base = buf1; //意图存储数据的位置0
vec[0].iov_len = 5; //位置0意图存储的最大字节数为5,剩余数据由下一个位置(位置1)存储
vec[1].iov_base = buf2; //位置1
vec[1].iov_len = BUF_SIZE;
str_len = readv(0, vec, 2); //0位标准输入
printf("Read bytes: %d \n", str_len);
printf("First message: %s \n", buf1);
printf("Second message: %s \n", buf2);
return 0;
}