c语言 - IO多路复用

练习

1.使用select,实现TCP,socket服务器,客户端交互流程

tcp_ser_select.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <pthread.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/time.h>

#define ERROR_MSG(msg) do{\
	fprintf(stderr, "line:%d: %s %s", __LINE__, __FILE__, __func__);\
	perror(msg);\
}while(0)

#define SUCCESS_MSG(msg) do {\
	printf("%s, line:%d \n", msg, __LINE__);\
}while(0)

#define PORT 6666
#define IP "192.168.50.83"

int main(int argc, const char *argv[]) {
	
	int sfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sfd < 0) {
		ERROR_MSG(" socket: ");
		return -1;
	}

	int reuse = 1;
	if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
		ERROR_MSG("reuse");
		return -1;
	}

	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT);
	sin.sin_addr.s_addr = inet_addr(IP);
	socklen_t slen = sizeof(sin);

	if (bind(sfd, (struct sockaddr *)&sin, slen) < 0) {
		ERROR_MSG("bind");
		return -1;
	}

	if (listen(sfd, 100) < 0) {
		ERROR_MSG("listen");
		return -1;
	}

	fd_set readfds, tempfds;
	FD_ZERO(&readfds);
	FD_ZERO(&tempfds);
	
	FD_SET(0, &readfds);
	FD_SET(sfd, &readfds);
	
	int maxfd = sfd;
	int res = -1;
	char buf[126] = "";
	int newfd = -1;

	struct sockaddr_in cin;
	socklen_t clen = sizeof(cin);
	struct sockaddr_in cinArr[1024-3];

	ssize_t r_res;

	while(1) {
		bzero(buf, sizeof(buf));
		tempfds = readfds;
		res = select(maxfd+1, &tempfds, NULL, NULL, NULL);
		if (res < 0) {
			ERROR_MSG("select");
			return -1;
		} else if (res == 0) {
			printf("超时了 \n");
			continue;
		} 
		for (int i=0; i<=maxfd; i++) {
			if (!FD_ISSET(i, &tempfds)) {
				continue;
			}
			if (0 == i) {
				fgets(buf, sizeof(buf), stdin);
				buf[strlen(buf)-1] = 0;
			} else if (sfd == i) {
				newfd = accept(sfd, (struct sockaddr*)&cin, &clen);
				if (newfd < 0) {
					ERROR_MSG("accept");
					return -1;
				}
				FD_SET(newfd, &readfds);
				cinArr[newfd-3] = cin;
				fprintf(stderr, "[%s:%d]连接成功 \n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));
				maxfd = newfd > maxfd ? newfd : maxfd;
			} else {
				printf("11111 \n");
				bzero(buf, sizeof(buf));
				r_res = recv(i, buf, sizeof(buf), 0);
				if (r_res < 0) {
					ERROR_MSG("recv");
					continue;
				} else if (r_res == 0) {
					printf("[%s:%d]下线了 \n", inet_ntoa(cinArr[i-3].sin_addr), ntohs(cinArr[i-3].sin_port));
					
					close(i);
					FD_CLR(i, &readfds);
					int j=maxfd;
					while(!FD_ISSET(j, &readfds) && j-->0);
					maxfd = j;
					continue;
				} else {
					printf("[%s:%d]:%s \n", inet_ntoa(cinArr[i-3].sin_addr), ntohs(cinArr[i-3].sin_port), buf);
				}
			}
		}
	}



	return 0;
}

tcp_cli_select.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <pthread.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/time.h>

#define ERROR_MSG(msg) do{\
	fprintf(stderr, "line:%d: %s %s", __LINE__, __FILE__, __func__);\
	perror(msg);\
}while(0)

#define SUCCESS_MSG(msg) do {\
	printf("%s, line:%d \n", msg, __LINE__);\
}while(0)

#define PORT 6666
#define IP "192.168.50.83"

int main(int argc, const char *argv[]) {
	
	int cfd = socket(AF_INET, SOCK_STREAM, 0);
	if (cfd < 0) {
		ERROR_MSG("socket");
		return -1;
	}
	
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT);
	sin.sin_addr.s_addr = inet_addr(IP);

	if (connect(cfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
		ERROR_MSG("connect");
		return -1;
	}

	fd_set readfds, tempfds;
	FD_ZERO(&readfds);
	FD_SET(0, &readfds);
	FD_SET(cfd, &readfds);
	

	int res = -1;
	int r_res = -1;

	char buf[126] = "";
	while(1) {
		tempfds = readfds;
		res = select(cfd+1, &tempfds, NULL, NULL, NULL);
		if (res < 0) {
			ERROR_MSG("select");
			return -1;
		} else if (res == 0) {
			printf("超时了 \n");
			continue;
		}
		
		if (FD_ISSET(0, &tempfds)) {
			bzero(buf, sizeof(buf));
			fgets(buf, sizeof(buf), stdin);
			buf[strlen(buf)-1] = 0;
			if (send(cfd, buf, strlen(buf), 0) < 0) {
				ERROR_MSG("send");
				continue;
			}
		}
		if (FD_ISSET(cfd, &tempfds)) {
			bzero(buf, sizeof(buf));
			r_res = recv(cfd, buf, sizeof(buf), 0);
			if (r_res < 0) {
				ERROR_MSG("recv");
				return -1;
			} else if (r_res == 0) {
				printf("服务器端下线 \n");
				return -1;
			} else {
				printf("%s \n", buf);
			}
		}
	}

	close(cfd);
	return 0;
}

结果展示:
在这里插入图片描述

2.使用poll,实现TCP,socket服务器,客户端交互流程

服务器端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <pthread.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <poll.h>

#define ERROR_MSG(msg) do{\
	fprintf(stderr, "line:%d: %s %s", __LINE__, __FILE__, __func__);\
	perror(msg);\
}while(0)

#define SUCCESS_MSG(msg) do {\
	printf("%s, line:%d \n", msg, __LINE__);\
}while(0)

#define PORT 6666
#define IP "192.168.50.83"

int main(int argc, const char *argv[]) {
	
	int sfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sfd < 0) {
		ERROR_MSG(" socket: ");
		return -1;
	}

	int reuse = 1;
	if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
		ERROR_MSG("reuse");
		return -1;
	}

	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT);
	sin.sin_addr.s_addr = inet_addr(IP);
	socklen_t slen = sizeof(sin);

	if (bind(sfd, (struct sockaddr *)&sin, slen) < 0) {
		ERROR_MSG("bind");
		return -1;
	}

	if (listen(sfd, 100) < 0) {
		ERROR_MSG("listen");
		return -1;
	}

	struct pollfd fds[1024-2];
	int fdslen = 2;
	
	fds[0].fd = 0;
	fds[0].events = POLLIN;

	fds[1].fd = sfd;
	fds[1].events = POLLIN;
	
	int res = -1;
	char buf[126] = "";
	int newfd = -1;

	struct sockaddr_in cin;
	socklen_t clen = sizeof(cin);
	struct sockaddr_in cinArr[1024-3];

	ssize_t r_res;

	struct pollfd tempfd;

	while(1) {
		bzero(buf, sizeof(buf));
		res = poll(fds, fdslen, -1);
		if (res < 0) {
			ERROR_MSG("select");
			return -1;
		} else if (res == 0) {
			printf("超时了 \n");
			continue;
		} 
		for (int i=0; i<=fdslen; i++) {
			if (fds[0].revents && POLLIN) {
				fgets(buf, sizeof(buf), stdin);
				buf[strlen(buf)-1] = 0;
			} else if (fds[1].revents && POLLIN) {
				newfd = accept(sfd, (struct sockaddr*)&cin, &clen);
				if (newfd < 0) {
					ERROR_MSG("accept");
					return -1;
				}
				tempfd.fd = newfd;
				tempfd.events = POLLIN;
				fds[newfd-2] = tempfd;
				fdslen++;
				cinArr[newfd-3] = cin;
				fprintf(stderr, "[%s:%d]连接成功 \n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));
			} else {
				bzero(buf, sizeof(buf));
				r_res = recv(i, buf, sizeof(buf), 0);
				if (r_res < 0) {
					ERROR_MSG("recv");
					continue;
				} else if (r_res == 0) {
					printf("[%s:%d]下线了 \n", inet_ntoa(cinArr[i-3].sin_addr), ntohs(cinArr[i-3].sin_port));

					close(fds[i].fd);
					for (int j=i; j<fdslen-1; j++) {
						fds[j] = fds[j+1];
					}
					fdslen--;
					continue;
				} else {
					printf("[%s:%d]:%s \n", inet_ntoa(cinArr[i-3].sin_addr), ntohs(cinArr[i-3].sin_port), buf);
				}
			}

		}
	}



	return 0;
}

客户端

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>

#define ERR_MSG(msg) do{\
    fprintf(stderr, "line:%d: %s %s\n", __LINE__, __FILE__, __func__);\
    perror(msg);\
}while(0)

#define IP "192.168.2.184"  //ifconfig出来的本机IP
#define PORT 6666           //1024~49151,网络字节序

int main(int argc, const char *argv[])
{
    //创建流式套接字
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    if(cfd < 0)
    {
        ERR_MSG("socket");
        return -1;
    }


    //绑定客户端自身的地址信息结构体,----》非必须绑定
    //若不绑定,则操作系统会自动帮cli绑定IP和端口
    //IP:客户端运行环境的IP地址
    //port:默认从49151~65535范围动态选择


    //填充服务器的地址信息结构体,给connect函数使用
    //客户端想连哪个服务器,就填哪个服务器绑定的IP和端口
    struct sockaddr_in sin;
    sin.sin_family      = AF_INET;          //必须填AF_INET;
    sin.sin_port        = htons(PORT);      //1024~49151,网络字节序
    sin.sin_addr.s_addr = inet_addr(IP);    //ifconfig出来的本机IP


    //连接服务器
    if(connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
    {
        ERR_MSG("connect");
        return -1;
    }
    printf("connect success __%d__\n", __LINE__);


    //创建集合,让poll监测                                                               
    struct pollfd fds[2];

    fds[0].fd = 0;              //监测0号文件描述符
    fds[0].events = POLLIN;     //监测读事件

    fds[1].fd = cfd;            //监测cfd
    fds[1].events = POLLIN;     //读事件


    char buf[128] = "";
    ssize_t res = 0;
    int p_res = 0;
    while(1)
    {
        //阻塞方式让内核监测文件描述符
        p_res = poll(fds, 2, -1);
        if(p_res < 0)
        {
            ERR_MSG("poll");
            return -1;
        }
        else if(0 == p_res)
        {
            printf("time out....\n");
            break;
        }

        //能运行到当前位置,则代表文件描述符触发事件了;
        //需要遍历访问集合中每个文件描述符的revents中是否有POLLIN事件。
        //需要从revents中通过按位与的方式,提取出POLLIN的那一位是否为1 

        if((fds[0].revents & POLLIN) != 0)
        {
            fprintf(stderr, "触发键盘输入事件>>> ");

            bzero(buf, sizeof(buf));
            fgets(buf, sizeof(buf), stdin);
            buf[strlen(buf)-1] = 0;

            if(send(cfd, buf, sizeof(buf), 0) < 0)
            {
                ERR_MSG("send");
                return -1;
            }
            printf("send success __%d__\n", __LINE__);

        }

        if(fds[1].revents & POLLIN)
        {
            fprintf(stderr, "触发服务器交互事件>>> ");
            bzero(buf, sizeof(buf));
            //接收
            res = recv(cfd, buf, sizeof(buf), 0);
            if(res < 0)
            {
                ERR_MSG("recv");
                return -1;
            }
            else if(0 == res)
            {
                printf("服务器下线__%d__\n", __LINE__);
                break;
            }

            printf(":%s __%d__\n", buf, __LINE__);
        }

    }

    //关闭套接字文件描述符
    close(cfd);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值