套接字超时
1、调用alarm,指定超时时产生SIGALRM信号,捕捉该信号。注意信号处理在多线程上处理有困难,建议在单进程、单线程的程序使用信号
2、通过select,通过设置select内置超时。
3、使用SO_RCVTIMEO和SO_SNDTIMEO套接字选项。
1、2中方法可以用于各种描述符,不仅仅套接字描述符,而第3种只能用于套接字;第1种可以用于connect函数,2、3不能;第3种,一旦超时,send或recv会返回EWOULDBLOCK。
recvmsg和sendmsg函数
msg_name获取的对端地址信息,msg_control填充的是本端地址信息。
第3个参数flag取值如下表所示:
msghdr中msg_flags取值如下表所示:
总结:在这些flag中,MSG_DONTWAIT表示本次读取实现异步,MSG_PEEK读取数据,但是同时并不将其从接收队列中减少。
sendmsg 和 recvmsg例子
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> /*for struct sockaddr_in*/
#define SVR_IP "127.0.0.1"
#define SVR_PORT 1234
#define BUFSIZE 255
int main()
{
int ret = 0;
int sockfd = 0;
struct sockaddr_in svr_addr;
memset(&svr_addr, 0, sizeof(struct sockaddr_in));
// create client socket fd
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("socket failed");
exit(1);
}
// connet to server
svr_addr.sin_family = AF_INET;
svr_addr.sin_port = htons(SVR_PORT);
svr_addr.sin_addr.s_addr = inet_addr(SVR_IP);
ret = connect(sockfd, (struct sockaddr *)&svr_addr, sizeof(struct sockaddr_in));
if (ret == -1) {
perror("connect failed");
exit(1);
}
int i = 0;
for (i = 0; i < 150; i++)
{
char * msghead = "msg header...";
char * msgbody = "msg body...";
struct msghdr msg;
struct iovec iov[2];
memset(&msg, 0, sizeof(msg));
iov[0].iov_base = msghead;
iov[0].iov_len = strlen(msghead);
printf("send header msg: %s\n", msghead);
iov[1].iov_base = msgbody;
iov[1].iov_len = strlen(msgbody);
printf("send body msg: %s\n", msgbody);
msg.msg_iov = iov;
msg.msg_iovlen = 2;
if (sendmsg(sockfd, &msg, 0) < 0) {
perror("sendmsg()");
}
char recv_buf[BUFSIZE] = {0};
int recv_len = recv(sockfd, recv_buf, sizeof(recv_buf), 0);
printf("client recv msg: %s%d\n", recv_buf, recv_len);
sleep(1);
}
close(sockfd);
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/queue.h>
#define MYPORT 1234 // the port users will be connecting to
#define BACKLOG 512 // how many pending connections queue will hold
#define BUF_SIZE 1024
#define MAX_PATH_LEN 255
#ifndef bool
#define bool int
#endif
#define FALSE 0
#define TRUE 1
int main(void)
{
int ret = 0;
int udp_fd = 0; // udp socket fd
struct sockaddr_in server_addr; // server address information
memset(&server_addr, 0, sizeof(struct sockaddr_in));
udp_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == udp_fd)
{
perror("socket");
return -1;
}
bzero(&server_addr, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET; // host byte order
server_addr.sin_port = htons(MYPORT); // short, network byte order
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // automatically fill with my IP
memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero));
ret = bind(udp_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (-1 == ret)
{
perror("bind");
return -1;
}
while (1)
{
struct sockaddr_in cli_addr;
memset(&cli_addr, 0, sizeof(struct sockaddr_in));
char msghead[13] = {0};
char msgbody[11] = {0};
struct iovec iov[2];
iov[0].iov_base = msghead;
iov[0].iov_len = 13;
iov[1].iov_base = msgbody;
iov[1].iov_len = 11;
struct msghdr msg;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &cli_addr;
msg.msg_namelen = sizeof(struct sockaddr_in);
msg.msg_iov = iov;
msg.msg_iovlen = 2;
if (recvmsg(udp_fd, &msg, 0) < 0) {
perror("sendmsg()");
}
printf("recv header msg: %s\n", msghead);
printf("recv body msg: %s\n", msgbody);
sendto(udp_fd, msgbody, 11, 0, (struct sockaddr *)&cli_addr, sizeof(struct sockaddr_in));
#if 0
char msg[BUF_SIZE] = {0};
struct sockaddr_in cliaddr;
socklen_t len = sizeof(struct sockaddr_in);
ssize_t n = recvfrom(udp_fd, msg, BUF_SIZE, 0, (struct sockaddr *)&cliaddr, &len);
printf("udp recv: %s\n", msg);
printf("udp send: %s\n", msg);
sendto(udp_fd, msg, n, 0, (struct sockaddr *)&cliaddr, len);
#endif
}
}
辅助数据:
msg_controllen是整个辅助数据的总长度,在msg_control执行的内存,会有填充数据,系统为了对于用户屏蔽填充数据,提供了如下宏:
使用方法如下:
其中CMSG_LEN返回实际数据长度,不包括填充数据,CMSG_SPACE则包括填充数据。