阻塞/非阻塞 主要针对Server端
同步IO和异步IO的区别就在于:数据访问的时候进程是否阻塞!
阻塞IO和非阻塞IO的区别就在于:应用程序的调用是否立即返回!
select的阻塞 与 是否设置非阻塞I/O是 没有关系的。
阻塞
阻塞调用是指调用结果返回之前,当前线程会被挂起(线程进入非可执行状态,在这个状态下,cpu不会给线程分配时间片,即线程暂停运行)。
函数只有在得到结果之后才会返回。
非阻塞
非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
设置socket为异步的三种方式
1、创建socket的时候,指定socket是异步的
int sockfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
2、使用fcntl函数:
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
3、使用ioctl函数:
int IOFLAG = 1;
ioctl(sockfd, FIONBIO, &IOFLAG); //1:非阻塞 0:阻塞
/* 函数名 :int fcntl(int fd, int cmd); 参 数 :int fd -- 文件描述符 参 数 :int cmd -- 控制值 参 数 :flock *lock -- (参数) 返回值 :【成功】返回依赖于cmd的值 【错误】返回-1,错误原因存于errno. 说 明 :fcntl针对文件描述符提供控制。 */ int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg); int fcntl(int fd, int cmd, struct flock *lock);
/* 函数名 :int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout); 参 数 :int maxfd -- maxfd是要监视的最大的文件描述符值+1; 参 数 : fd_set *rdset -- 读文件描述符的集合 参 数 : fd_set *exset -- 可写文件描述符集合 参 数 : fd_set *wrset -- 异常文件描述符集合 参 数 : struct timeval *timeout --超时描述,在一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。 返回值 : 说 明 :select函数用于在非阻塞中,当一个套接字或一组套接字有信号时通知你,系统提供select函数来实现多路复用输入/输出模型。 */ int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout); FD_ZERO(fd_set *fdset); --将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。 FD_SET(fd_set *fdset); --用于在文件描述符集合中增加一个新的文件描述符。 FD_CLR(fd_set *fdset); --用于在文件描述符集合中删除一个文件描述符。 FD_ISSET(int fd,fd_set *fdset); --用于测试指定的文件描述符是否在该集合中。
// 例子 非阻塞 Tcp Client
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <sys/select.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main() { unsigned short port = 8000; char *server_ip = "127.0.0.1"; int sockfd=socket(AF_INET, SOCK_STREAM, 0); //默认非阻塞模式 //int sockfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);// 创建时设置非阻塞模式 if (sockfd<0) { perror("socket error"); exit(-1); } //fcntl设置为非阻塞模式 //fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK); //ioctl设置为非阻塞模式 int IOFLAG = 1; ioctl(sockfd, FIONBIO, &IOFLAG); //1:非阻塞 0:阻塞 struct sockaddr_in server_addr; bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family=AF_INET; server_addr.sin_port=htons(port); inet_pton(AF_INET, server_ip, &server_addr.sin_addr); int ret = 0; int err_log=connect(sockfd,(struct sockaddr*)&server_addr, sizeof(server_addr)); if (0 == err_log) { printf("socket connect succeed. "); ret = 1; } else {
printf("不要将进程睡眠,而是返回一个错误 err_log = %d ",err_log);
printf("please get the connect result by select(). "); int times = 0; while (times++ < 5 && ret == 0) { fd_set rfds, wfds; struct timeval tv; FD_ZERO(&rfds);
FD_ZERO(&wfds); FD_SET(sockfd, &rfds); FD_SET(sockfd, &wfds); tv.tv_sec = 10; tv.tv_usec = 0; int selres = select(sockfd + 1, &rfds, &wfds, NULL, &tv); switch (selres) { case -1: printf("select error "); break; case 0: printf("select time out "); break; default: if (FD_ISSET(sockfd, &rfds) || FD_ISSET(sockfd, &wfds)) { connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in)); int err = errno; if (err == EISCONN) { printf("connect finished. "); ret = 1; } else { printf("FD_ISSET(sock_fd, &rfds): %d ", FD_ISSET(sockfd, &rfds)); printf("FD_ISSET(sock_fd, &wfds): %d ", FD_ISSET(sockfd, &wfds)); } } else { printf("other error. "); } } } } if (ret == 1) { char send_buf[512] = "hello"; char recv_buf[512] = ""; if (send(sockfd, send_buf, sizeof(send_buf), 0) > 0) { printf("send "); } } close(sockfd); return 0; }
结果: