嵌入式学习——网络编程(IO多路复用、IO模型)——day34

IO模型

1 阻塞IO

        1.效率高,当没有IO事件时,任务被挂起,不占用CPU资源
        2.多个阻塞IO放在一起,会导致相互受到影响

        示例程序:

        1. 头文件

#ifndef __HEAD_H__
#define __HEAD_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#endif

        2. makefile

all:client server

client:client.c
	gcc $^ -o $@
server:server.c
	gcc $^ -o $@
.PHONY:
clean:
	rm client server

        3. client.c

#include "head.h"

int main(int argc, char const *argv[])
{
	int ret = 0;
	int sockfd = 0;
	int cnt = 0;
	ssize_t nsize = 0;
	char tmpbuff[4096] = {0};
	struct sockaddr_in seraddr;

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
	{
		perror("fail to socket");
		return -1;
	}

	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(50000);
	seraddr.sin_addr.s_addr = inet_addr("192.168.0.161");
	ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
	if (-1 == ret)
	{
		perror("fail to connect");
		return -1;
	}

	while (1)
	{
		sprintf(tmpbuff, "hello world ---- %d", cnt);
		cnt++;

		nsize = send(sockfd, tmpbuff, strlen(tmpbuff), 0);
		if (-1 == nsize)
		{
			perror("fail to send");
			return -1;
		}
		
		memset(tmpbuff, 0, sizeof(tmpbuff));
		nsize = recv(sockfd, tmpbuff, sizeof(tmpbuff), 0);
		if (-1 == nsize)
		{
			perror("fail to recv");
			return -1;
		}
		else if (0 == nsize)
		{
			printf("服务端断开连接!\n");
			break;
		}

		printf("RECV:%s\n", tmpbuff);
		
		sleep(1);
	}
	
	close(sockfd);

	return 0;
}

        4. server.c

#include "head.h"

int main(int argc, char const *argv[])
{
	int ret = 0;
	int confd = 0;
	int sockfd = 0;
	ssize_t nsize = 0;
	char tmpbuff[4096] = {0};
	struct sockaddr_in seraddr;

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
	{
		perror("fail to socket");
		return -1;
	}

	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(50000);
	seraddr.sin_addr.s_addr = inet_addr("192.168.0.161");
	ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
	if (-1 == ret)
	{
		perror("fail to bind");
		return -1;
	}

	ret = listen(sockfd, 10);
	if (-1 == ret)
	{
		perror("fail to listen");
		return -1;
	}

	while (1)
	{
		confd = accept(sockfd, NULL, NULL);
		if (-1 == confd)
		{
			perror("fail to accept");
			return -1;
		}

		while (1)
		{
			memset(tmpbuff, 0, sizeof(tmpbuff));
			nsize = recv(confd, tmpbuff, sizeof(tmpbuff), 0);
			if (-1 == nsize)
			{
				perror("fail to recv");
				return -1;
			}
			else if (0 == nsize)
			{
				printf("客户端退出!\n");
				break;
			}

			printf("RECV:%s\n", tmpbuff);

			sprintf(tmpbuff, "%s ---- echo", tmpbuff);
			nsize = send(confd, tmpbuff, strlen(tmpbuff), 0);
			if (-1 == nsize)
			{
				perror("fail to send");
				return -1;
			}
		}
		
		close(confd);
	}
	
	close(sockfd);

	return 0;
}

2 非阻塞IO——(fcntl、NONBLOCK)

        1.效率低,当没有IO事件时,CPU一直轮询判断是否有IO事件发生
        2.可以解决多个阻塞导致程序编写顺序性的问题

        示例程序:

        1. 头文件

#ifndef __HEAD_H__
#define __HEAD_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#endif

        2. makefile

all:send recv

send:send.c
	gcc $^ -o $@
recv:recv.c
	gcc $^ -o $@
.PHONY:
clean:
	rm send recv

        3. client.c

#include "head.h"

int main(int argc, char const *argv[])
{
	int fd = 0;
	char tmpbuff[4096] = {0};

	mkfifo("/tmp/myfifo", 0777);

	fd = open("/tmp/myfifo", O_WRONLY);
	if (-1 == fd)
	{
		perror("fail to open");
		return -1;
	}

	while (1)
	{
		gets(tmpbuff);
		write(fd, tmpbuff, strlen(tmpbuff));
	}

	close(fd);

	return 0;
}

        4. server.c

#include "head.h"

int main(int argc, char const *argv[])
{
	int fd = 0;
	int flags = 0;
	ssize_t nret = 0;
	char *pret = NULL;
	char stdbuff[4096] = {0};
	char fifobuff[4096] = {0};

	mkfifo("/tmp/myfifo", 0777);

	fd = open("/tmp/myfifo", O_RDONLY);
	if (-1 == fd)
	{
		perror("fail to open");
		return -1;
	}

	flags = fcntl(fd, F_GETFL);
	flags |= O_NONBLOCK;
	fcntl(fd, F_SETFL, flags);

	flags = fcntl(0, F_GETFL);
	flags |= O_NONBLOCK;
	fcntl(0, F_SETFL, flags);

	while (1)
	{
		memset(fifobuff, 0, sizeof(fifobuff));
		nret = read(fd, fifobuff, sizeof(fifobuff));
		if (nret > 0)
		{
			printf("FIFO:%s\n", fifobuff);
		}

		memset(stdbuff, 0, sizeof(stdbuff));
		pret = gets(stdbuff);
		if (pret != NULL)
		{
			printf("STDIN:%s\n", stdbuff);	
		}	
	}

	close(fd);

	return 0;
}

3 异步IO——(信号)

        1.效率高,当有IO事件发生时,内核会向应用层发送SIGIO信号,此时可以操作IO事件
        2.只能操作一个文件描述符,多个文件描述符无法区分      

        示例程序:

        1. 头文件

#ifndef __HEAD_H__
#define __HEAD_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#endif

        2. makefile

all:send recv

send:send.c
	gcc $^ -o $@
recv:recv.c
	gcc $^ -o $@
.PHONY:
clean:
	rm send recv

        3. client.c

#include "head.h"

int main(int argc, char const *argv[])
{
	int fd = 0;
	char tmpbuff[4096] = {0};

	mkfifo("/tmp/myfifo", 0777);

	fd = open("/tmp/myfifo", O_WRONLY);
	if (-1 == fd)
	{
		perror("fail to open");
		return -1;
	}

	while (1)
	{
		gets(tmpbuff);
		write(fd, tmpbuff, strlen(tmpbuff));
	}

	close(fd);

	return 0;
}

        4. server.c

#include "head.h"

int fd = 0;

void handler(int signo)
{
	char fifobuff[4096] = {0};

	memset(fifobuff, 0, sizeof(fifobuff));
	read(fd, fifobuff, sizeof(fifobuff));
	printf("FIFO:%s\n", fifobuff);

	return;
}

int main(int argc, char const *argv[])
{
	int flags = 0;
	ssize_t nret = 0;
	char *pret = NULL;
	char stdbuff[4096] = {0};

	signal(SIGIO, handler);

	mkfifo("/tmp/myfifo", 0777);

	fd = open("/tmp/myfifo", O_RDONLY);
	if (-1 == fd)
	{
		perror("fail to open");
		return -1;
	}

	flags = fcntl(fd, F_GETFL);
	flags |= O_ASYNC;//设置异步信号中断
	fcntl(fd, F_SETFL, flags);
	fcntl(fd, F_SETOWN, getpid());

	while (1)
	{
		memset(stdbuff, 0, sizeof(stdbuff));
		gets(stdbuff);
		printf("STDIN:%s\n", stdbuff);		
	}

	close(fd);

	return 0;
}

4 多路复用IO——(高并发:多路IO(边沿触发)+线程)

4.1 select——谁产生事件了,谁留在文件描述符集合中,没产生的踢出去(用户层)

(1) select

          int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
          功能:
            监听IO事件
          参数:    
            nfds:最大文件描述符个数+1
            readfds:读文件描述符集合
            writefds:写文件描述符集合
            exceptfds:其余文件描述符集合
            timeout:最大允许阻塞等待的时间
                NULL:永远阻塞等待
          返回值:
            成功返回产生事件的个数
            失败返回-1 
            等待时间到达没有产生的事件返回0 
        
(2)void FD_CLR(int fd, fd_set *set);
        将fd文件描述符从set集合中清除
        

(3)int  FD_ISSET(int fd, fd_set *set);
        判断fd是否仍在set集合中

(4)void FD_SET(int fd, fd_set *set);
        将fd加入文件描述符集合set中

(5)void FD_ZERO(fd_set *set);
        将文件描述符集合清0 

        示例程序:

        1. 头文件

#ifndef __HEAD_H__
#define __HEAD_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#endif

        2. makefile

all:send recv

send:send.c
	gcc $^ -o $@
recv:recv.c
	gcc $^ -o $@
.PHONY:
clean:
	rm send recv

        3. client.c

#include "head.h"

int main(int argc, char const *argv[])
{
	int fd = 0;
	char tmpbuff[4096] = {0};

	mkfifo("/tmp/myfifo", 0777);

	fd = open("/tmp/myfifo", O_WRONLY);
	if (-1 == fd)
	{
		perror("fail to open");
		return -1;
	}

	while (1)
	{
		gets(tmpbuff);
		write(fd, tmpbuff, strlen(tmpbuff));
	}

	close(fd);

	return 0;
}

        4. server.c

#include "head.h"

int main(int argc, char const *argv[])
{
	int fd = 0;
	int flags = 0;
	ssize_t nret = 0;
	char *pret = NULL;
	char stdbuff[4096] = {0};
	char fifobuff[4096] = {0};
	fd_set rdfds;
	fd_set tmpfds;
	int maxfd = 0;
	int nready = 0;

	mkfifo("/tmp/myfifo", 0777);

	fd = open("/tmp/myfifo", O_RDONLY);
	if (-1 == fd)
	{
		perror("fail to open");
		return -1;
	}

	FD_ZERO(&rdfds);
	FD_SET(fd, &rdfds);
	FD_SET(0, &rdfds);
	maxfd = fd;
	tmpfds = rdfds;

	while (1)
	{
		tmpfds = rdfds;
		nready = select(maxfd+1, &tmpfds, NULL, NULL, NULL);
		if (-1 == nready)
		{
			perror("fail to nready");
			return -1;
		}

		if (FD_ISSET(fd, &tmpfds))
		{
			memset(fifobuff, 0, sizeof(fifobuff));
			read(fd, fifobuff, sizeof(fifobuff));
			printf("FIFO:%s\n", fifobuff);
		}
		
		if (FD_ISSET(0, &tmpfds))
		{
			memset(stdbuff, 0, sizeof(stdbuff));
			gets(stdbuff);
			printf("STDIN:%s\n", stdbuff);
		}
	}

	return 0;
}

4.3 epoll——(内核层)

(1)epoll_create

                int epoll_create(int size);

                    功能:
                        创建一个用来监听事件的内核事件表
                    参数:
                        size:事件表中事件个数
                    返回值:
                        成功返回文件描述符
                        失败返回-1 

(2)epoll_ctl

            int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
            功能:
                管理内核事件表
            参数:
                epfd:事件表文件描述符
                op:
                    EPOLL_CTL_ADD:添加
                    EPOLL_CTL_DEL:删除
                fd:文件描述符
                event:事件信息
                    typedef union epoll_data {
                       void        *ptr;
                       int          fd;
                       uint32_t     u32;
                       uint64_t     u64;
                    } epoll_data_t;

                    struct epoll_event {
                       uint32_t     events;      /* Epoll events */
                       epoll_data_t data;        /* User data variable */
                    };
                    
                    EPOLLIN        读事件
                    EPOLLOUT    写事件
                    EPOLLET        边沿触发(默认为水平触发)
                    
            返回值:
                成功返回0 
                失败返回-1 

(3)epoll_wait

            int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);
            功能:
                等待事件发生
            参数:
                epfd:事件表文件描述符
                events:存放产生事件的数组 
                maxevents:最多存放事件的个数
                timeout:超时时间
            返回值:
                成功返回产生事件的个数
                失败返回-1 
                时间到达没有事件产生返回0 

        示例程序:

        1. 头文件

#ifndef __HEAD_H__
#define __HEAD_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>

#endif

        2. makefile

all:send recv

send:send.c
	gcc $^ -o $@
recv:recv.c
	gcc $^ -o $@
.PHONY:
clean:
	rm send recv

        3. recv.c

#include "head.h"

int main(int argc, char const *argv[])
{
	int fd = 0;
	int flags = 0;
	ssize_t nret = 0;
	char *pret = NULL;
	char stdbuff[4096] = {0};
	char fifobuff[4096] = {0};
	int epfd = 0;
	int ret = 0;
	int nready = 0;
	struct epoll_event env;
	struct epoll_event retenv[2];
	int i = 0;

	mkfifo("/tmp/myfifo", 0777);

	fd = open("/tmp/myfifo", O_RDONLY);
	if (-1 == fd)
	{
		perror("fail to open");
		return -1;
	}

	epfd = epoll_create(2);
	if (-1 == epfd)
	{
		perror("fail to epoll_create");
		return -1;
	}

	env.events = EPOLLIN;
	env.data.fd = fd;
	ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &env);
	if (-1 == ret)
	{
		perror("fail to epoll_ctl");
		return -1;
	}

	env.events = EPOLLIN;
	env.data.fd = 0;
	ret = epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &env);
	if (-1 == ret)
	{
		perror("fail to epoll_ctl");
		return -1;
	}

	while (1)
	{
		nready = epoll_wait(epfd, retenv, 2, -1);
		if (-1 == nready)
		{
			perror("fail to epoll_wait");
			return -1;
		}

		for (i = 0; i < nready; i++)
		{
			if (retenv[i].data.fd == fd)
			{
				memset(fifobuff, 0, sizeof(fifobuff));
				read(fd, fifobuff, sizeof(fifobuff));
				printf("FIFO:%s\n", fifobuff);
			}
			else if (retenv[i].data.fd == 0)
			{
				memset(stdbuff, 0, sizeof(stdbuff));
				gets(stdbuff);
				printf("STDIN:%s\n", stdbuff);
			}
		}
	}

	close(epfd);

	return 0;
}

        4. send.c

#include "head.h"

int main(int argc, char const *argv[])
{
	int fd = 0;
	char tmpbuff[4096] = {0};

	mkfifo("/tmp/myfifo", 0777);

	fd = open("/tmp/myfifo", O_WRONLY);
	if (-1 == fd)
	{
		perror("fail to open");
		return -1;
	}

	while (1)
	{
		gets(tmpbuff);
		write(fd, tmpbuff, strlen(tmpbuff));
	}

	close(fd);

	return 0;
}

        

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值