套接字超时
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则包括填充数据。