五种I/O模型:
1、阻塞式I/O:最流行的I/O模型,默认情况下所有套接字都是阻塞的,此模式对系统友好
2、非阻塞式I/O:不会进行休眠等待资源,返回一个错误继续运行后面程序,
内核会循环判断有没有相关资源,若没有则报错,较浪费内核资源,此模式对人比较友好
3、I/O复用:同时阻塞多个I/O,只要发现一个触发,都会执行后面的程序
4、信号驱动式I/O:内核在描述符就绪时会发送SIGIO子女好通知
5、给内核传递描述符、缓冲区指针、缓冲区大小和文件偏移。通知内核完成并返回通知
以上模型(除异步I/O外)在数据传输过程(将数据从内核空间复制到用户空间)中均为阻塞;例如:read在读取过程中为阻塞过程
select()函数:
原型:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *excsptfds, struct timeout);
参数:
nfds:最大文件描述符加1
readfds:文件描述符读表
writefds:文件描述符写表
exceptfds:文件描述符异常处理表
timeout:超时设置,-1为不设置定时,以微秒为单位
一般程序员都会使用这个函数进行定时(比较好用)
此函数使用流程较为繁琐,且最大记录只有1024,推荐使用poll()函数
使用select()创建服务器
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#define BUF_N 1024
#define LISTEN_MAX 5
#define PORT 8888
#define SERVER_IP "192.168.113.129"
#define DEF_VAL 0
#define ERR_VAL -1
#define ERR_LOG(val) do{perror(val);exit(EXIT_FAILURE);}while(0);
int main(int argc, const char * argv[])
{
int ret = 0;
int sockfd = socket(AF_INET, SOCK_STREAM, DEF_VAL);
if (ERR_VAL == sockfd)
{
ERR_LOG("socket");
}
printf("====create socket successful sockfd: %d\n",sockfd);
struct sockaddr_in caddr;
memset(&caddr, 0, sizeof(caddr));
socklen_t caddr_len = sizeof(caddr);
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORT);
saddr.sin_addr.s_addr = INADDR_ANY;
ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
if (ERR_VAL == ret)
{
ERR_LOG("bind");
}
printf("====bind sucessful\n");
ret = listen(sockfd, LISTEN_MAX);
if (ERR_VAL == ret)
{
ERR_LOG("listen");
}
printf("====listen successful\n");
printf("wait client connect...\n");
fd_set fds, tmp;
FD_ZERO(&fds);
FD_SET(sockfd, &fds);
int nfds = sockfd + 1;
tmp = fds;
struct timeval t = {5, 0};
int connfd = 0;
char dst[20] = {0};
while(1)
{
fds = tmp;
t.tv_sec = 5;
t.tv_usec = 0;
int ret = select(nfds, &fds, NULL, NULL, &t);
if (-1 == ret)
{
ERR_LOG("select");
}
else if (0 == ret)
{
printf("Timeout!\n");
continue;
}
for (int i = 3; i < nfds; i++)
{
if (!FD_ISSET(i, &fds))
{
continue;
}
if (i == sockfd)
{
connfd = accept(sockfd, (struct sockaddr *)&caddr, &caddr_len);
if (ERR_VAL == connfd)
{
ERR_LOG("accept");
}
printf("connfd: %d\n", connfd);
printf("====accept successful %s : %d\n",
inet_ntop(AF_INET, &caddr.sin_addr, dst, sizeof(dst)),
ntohs(caddr.sin_port));
FD_SET(connfd, &tmp);
if (connfd + 1 != nfds)
{
nfds = connfd + 1;
}
}
else
{
char buf[BUF_N] = {0};
int len = read(i, buf, sizeof(buf));
if (len <= DEF_VAL)
{
printf("client exit...\n");
close(i);
FD_CLR(i, &tmp);
if(i + 1 == nfds)
{
--nfds;
}
continue;
}
write(i, buf, strlen(buf));
printf("client send massage: %s", buf);
}
}
}
close(sockfd);
return 0;
}
poll()函数:
原型:int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数:
fds:需要监听的所有文件描述符结构体的数组
nfds:数组内有效最大个数
timeout:超时时间,-1为不设置超时,以毫秒为单位计时
使用poll()创建服务器
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <poll.h>
#define BUF_N 1024
#define LISTEN_MAX 5
#define PORT 8888
#define SERVER_IP "192.168.113.129"
#define DEF_VAL 0
#define ERR_VAL -1
#define ERR_LOG(val) do{perror(val);exit(EXIT_FAILURE);}while(0);
int main(int argc, const char * argv[])
{
int ret = 0;
int sockfd = socket(AF_INET, SOCK_STREAM, DEF_VAL);
if (ERR_VAL == sockfd)
{
ERR_LOG("socket");
}
printf("====create socket successful sockfd: %d\n",sockfd);
struct sockaddr_in caddr;
memset(&caddr, 0, sizeof(caddr));
socklen_t caddr_len = sizeof(caddr);
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORT);
saddr.sin_addr.s_addr = INADDR_ANY;
ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
if (ERR_VAL == ret)
{
ERR_LOG("bind");
}
printf("====bind sucessful\n");
ret = listen(sockfd, LISTEN_MAX);
if (ERR_VAL == ret)
{
ERR_LOG("listen");
}
printf("====listen successful\n");
printf("wait client connect...\n");
struct pollfd fds[20];
fds[0].fd = sockfd;
fds[0].events = POLLIN;
int nfds = 1;
int connfd = 0;
char dst[20] = {0};
while(1)
{
int ret = poll(fds, nfds, -1);
if (-1 == ret)
{
ERR_LOG("poll");
}
for (int i = 0; i < nfds; i++)
{
if (!(fds[i].revents & POLLIN))
{
continue;
}
if (fds[i].fd == sockfd)
{
connfd = accept(sockfd, (struct sockaddr *)&caddr, &caddr_len);
if (ERR_VAL == connfd)
{
ERR_LOG("accept");
}
printf("connfd: %d\n", connfd);
printf("====accept successful %s : %d\n",
inet_ntop(AF_INET, &caddr.sin_addr, dst, sizeof(dst)),
ntohs(caddr.sin_port));
fds[nfds].fd = connfd;
fds[nfds].events = POLLIN;
nfds++;
printf("nfds: %d\tconnfd: %d\n", nfds, connfd);
}
else
{
char buf[BUF_N] = {0};
int len = read(fds[i].fd, buf, sizeof(buf));
if (len <= DEF_VAL)
{
printf("client exit...\n");
close(fds[i].fd);
fds[i] = fds[nfds - 1];
fds[nfds].fd = -1;
nfds--;
continue;
}
write(fds[i].fd, buf, strlen(buf));
printf("client send massage: %s", buf);
}
}
}
close(sockfd);
return 0;
}
使用poll()创建客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <poll.h>
#define BUF_N 1024
#define DEF_VAL 0
#define ERR_VAL -1
#define ERR_LOG(val) do{perror(val);exit(EXIT_FAILURE);}while(0);
int main(int argc, const char * argv[])
{
int ret = 0;
if (argc != 3)
{
fprintf(stderr, "Usage %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
int port = atoi(argv[2]);
int sockfd = socket(AF_INET, SOCK_STREAM, DEF_VAL);
if (ERR_VAL == sockfd)
{
ERR_LOG("socket");
}
printf("====create socket successful sockfd: %d\n",sockfd);
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
ret = inet_pton(AF_INET, argv[1] ,&saddr.sin_addr);
if (ERR_VAL == ret)
{
ERR_LOG("inet_pton");
}
ret = connect(sockfd, (const struct sockaddr *) &saddr, sizeof(saddr));
if (ERR_VAL == ret)
{
close(sockfd);
ERR_LOG("connect");
}
printf("====connect successful\n");
struct pollfd fds[2];
fds[0].fd = STDIN_FILENO;
fds[0].events = POLLIN;
fds[1].fd = sockfd;
fds[1].events = POLLIN;
int nfds = 2;
char buf[BUF_N] = {0};
while(1)
{
int ret = poll(fds, nfds, -1);
if (ERR_VAL == ret)
{
ERR_LOG("poll");
}
if (fds[0].events & POLLIN)
{
read(STDIN_FILENO, buf, BUF_N);
ret = send(sockfd, buf, strlen(buf), 0);
if (ERR_VAL == ret)
{
ERR_LOG("send");
}
}
if (fds[1].events & POLLIN)
{
memset(buf, 0, sizeof(buf));
ret = recv(sockfd, buf, sizeof(buf), 0);
if (ERR_VAL == ret)
{
ERR_LOG("recv");
}
printf("from server massage: %s", buf);
memset(buf, 0, sizeof(buf));
}
}
close(sockfd);
return 0;
}