socket的IO模型
- 非阻塞IO处理
- 阻塞IO处理
- IO复用的IO方式
- 异步IO
- 信号驱动式IO模型
可参考:https://www.cnblogs.com/LittleHann/p/3897910.html 讲解的比较i详细
概念
- 单个句柄占用一个输入 输出 造成资源过剩,使用文件集合占用一个输入输出
方法
- select
- poll
- epoll
select IO 的复用手段
select流程
- 从用户空间将fd_set拷贝到内核空间
- 注册回调函数
- 调用其对应的poll方法
- poll方法会返回一个描述读写是否就绪的mask掩码,根据这个mask掩码给fd_set赋值。
- 如果遍历完所有的fd都没有返回一个可读写的mask掩码,就会让select的进程进入休眠模式,直到发现可读写的资源后,重新唤醒等待队列上休眠的进程。如果在规定时间内都没有唤醒休眠进程,那么进程会被唤醒重新获得CPU,再去遍历一次fd。
- 将fd_set从内核空间拷贝到用户空间
优缺点:
缺点:两次拷贝耗时、轮询所有fd耗时,支持的文件描述符太小
优点:跨平台支持
select的使用
主要是对select的对象 struct fd_set的使用
1.创建对象
struct fd_set rfds;
2.设置对象
初始化 FD_ZERO(&rfds);
加入句柄 FD_SET(fd, &rfds);
移除句柄 FD_CLR(int fd, fd_set *set);
3.使用对象
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
fd_set *readfds, fd_set *writefds,fd_set *exceptfds 区分文件的读写错误分区 使用时可只设置一个 因为内核在实现时会把三个合并起来
int nfds 文件集合中最大的fd+1
struct timeval *timeout
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
判断由哪个fd引起的返回 int FD_ISSET(int fd, fd_set *set);
使用方法
首先进行select 等待
- 注意: 每次重新发起select时,需要对fd_set重新添加
- 注意: 将放入到select中的句柄都设置为非阻塞状态
select使用demo
tcp_sy_chat_server.c 服务端
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>
#define MAXBUF 1024
int main(int argc, char **argv)
{
int sockfd, new_fd;
socklen_t len;
struct sockaddr_in my_addr, their_addr;
unsigned int myport, lisnum;
char buf[MAXBUF + 1];
fd_set rfds;
struct timeval tv;
int retval, maxfd = -1;
if (argv[2])
myport = atoi(argv[2]);
else
myport = 7838;
if (argv[3])
lisnum = atoi(argv[3]);
else
lisnum = 2;
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = PF_INET;
my_addr.sin_port = htons(myport);
if (argv[1])
my_addr.sin_addr.s_addr = inet_addr(argv[1]);
else
my_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) {
perror("bind");
exit(EXIT_FAILURE);
}
if (listen(sockfd, lisnum) == -1) {
perror("listen");
exit(EXIT_FAILURE);
}
while (1)
{
printf ("\n----wait for new connect\n");
len = sizeof(struct sockaddr);
if ((new_fd =accept(sockfd, (struct sockaddr *) &their_addr,&len)) == -1) {
perror("accept");
exit(errno);
} else
printf("server: got connection from %s, port %d, socket %d\n",
inet_ntoa(their_addr.sin_addr),ntohs(their_addr.sin_port), new_fd);
while (1)
{
FD_ZERO(&rfds);
FD_SET(0, &rfds);
FD_SET(new_fd, &rfds);
maxfd = new_fd;
tv.tv_sec = 100;
tv.tv_usec = 0;
retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
if (retval == -1)
{
perror("select");
exit(EXIT_FAILURE);
}
else if (retval == 0)
{
continue;
}
else
{
if (FD_ISSET(0, &rfds))
{
bzero(buf, MAXBUF + 1);
fgets(buf, MAXBUF, stdin);
if (!strncasecmp(buf, "quit", 4)) {
printf("i will quit!\n");
break;
}
len = send(new_fd, buf, strlen(buf) - 1, 0);
if (len > 0)
printf ("send successful,%d byte send!\n",len);
else {
printf("send failure!");
break;
}
}
if (FD_ISSET(new_fd, &rfds))
{
bzero(buf, MAXBUF + 1);
len = recv(new_fd, buf, MAXBUF, 0);
if (len > 0)
printf ("recv success :'%s',%dbyte recv\n", buf, len);
else
{
if (len < 0)
printf("recv failure\n");
else
{
printf("the ohter one end ,quit\n");
break;
}
}
}
}
}
close(new_fd);
printf("need othe connecdt (no->quit)");
fflush(stdout);
bzero(buf, MAXBUF + 1);
fgets(buf, MAXBUF, stdin);
if (!strncasecmp(buf, "no", 2))
{
printf("quit!\n");
break;
}
}
close(sockfd);
return 0;
}
tcp_sy_chat_client.c 服务端
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#define MAXBUF 1024
int main(int argc, char **argv)
{
int sockfd, len;
struct sockaddr_in dest;
char buffer[MAXBUF + 1];
fd_set rfds;
struct timeval tv;
int retval, maxfd = -1;
if (argc != 3)
{
printf("argv format errno,pls:\n\t\t%s IP port\n",argv[0], argv[0]);
exit(EXIT_FAILURE);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Socket");
exit(EXIT_FAILURE);
}
bzero(&dest, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(atoi(argv[2]));
if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0)
{
perror(argv[1]);
exit(EXIT_FAILURE);
}
if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0)
{
perror("Connect ");
exit(EXIT_FAILURE);
}
printf("\nget ready pls chat\n");
while (1)
{
FD_ZERO(&rfds);
FD_SET(0, &rfds);
FD_SET(sockfd, &rfds);
maxfd = sockfd;
tv.tv_sec = 100;
tv.tv_usec = 0;
retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
if (retval == -1)
{
printf("select %s", strerror(errno));
break;
}
else if (retval == 0)
continue;
else
{
if (FD_ISSET(sockfd, &rfds))
{
bzero(buffer, MAXBUF + 1);
len = recv(sockfd, buffer, MAXBUF, 0);
if (len > 0)
printf ("recv message:'%s',%d byte recv\n",buffer, len);
else
{
if (len < 0)
printf ("message recv failure\n");
else
{
printf("the othe quit ,quit\n");
break;
}
}
}
if (FD_ISSET(0, &rfds))
{
bzero(buffer, MAXBUF + 1);
fgets(buffer, MAXBUF, stdin);
if (!strncasecmp(buffer, "quit", 4)) {
printf("i will quit\n");
break;
}
len = send(sockfd, buffer, strlen(buffer) - 1, 0);
if (len < 0) {
printf ("message send failure");
break;
} else
printf
("send success,%d byte send\n",len);
}
}
}
close(sockfd);
return 0;
}
编译
gcc tcp_sy_chat_server.c -o tcp_sy_chat_server
gcc tcp_sy_chat_client.c -o tcp_sy_chat_client
运行
./tcp_sy_chat_server 127.0.0.1
./tcp_sy_chat_client 127.0.0.1 7838