高级IO多路复用select简单创建TCP服务器

#include<myhead.h>

#define ERR(msg) do{\
	fprintf(stderr, "__%d__:", __LINE__);\
	perror(msg);\
}while(0);

#define PORT 1314
#define IP "192.168.114.56"

int stdin_put()
{
	ssize_t ret = 0;
	char buf[128] = "";

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

	//加\n是为了刷新缓冲区
	printf("%s\n", buf);

	return 0;
}

int Cli_connect(int sfd, struct sockaddr_in Cli_addr[], fd_set* preadfds, int* pmaxfd)
{
	int newfd = -1;
	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);

	newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);
	if(newfd < 0)
	{
		ERR("accept()");
		return -1;
	}
	printf("[%s:%d]客户端连接成功 newfd=%d\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);

	Cli_addr[newfd] = cin;
	FD_SET(newfd, preadfds);
	*pmaxfd = *pmaxfd>newfd ? *pmaxfd : newfd;

	return 0;
}

int Cli_intreact(int fd, struct sockaddr_in Cli_addr[], fd_set* preadfds, int* pmaxfd)
{
	char buf[128] = "";
	bzero(buf, sizeof(buf));

	//接收
	ssize_t res = recv(fd, buf, sizeof(buf), 0);
	if(res < 0)
	{
		ERR("recv()");
		return -1;
	}
	else if(0 == res)
	{
		printf("[%s:%d]客户端下线 newfd=%d\n", inet_ntoa(Cli_addr[fd].sin_addr), ntohs(Cli_addr[fd].sin_port), fd);

		//关闭文件描述符
		close(fd);
		FD_CLR(fd, preadfds);

		while(FD_ISSET(*pmaxfd, preadfds) == 0 && (*pmaxfd)-- >= 0);
		
		return 0;
	}
	printf("[%s:%d] newfd=%d : %s\n", inet_ntoa(Cli_addr[fd].sin_addr), ntohs(Cli_addr[fd].sin_port), fd, buf);

	//发送
	strcat(buf, "*_*");
	if(send(fd, buf, sizeof(buf), 0) < 0)
	{
		ERR("send()");
		return -1;
	}
	printf("发送成功\n");

	return 0;
}

int main(int argc, const char *argv[])
{
	//创建套接字
	int sfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sfd < 0)
	{
		ERR("socket()");
		exit(1);
	}
	printf("socket success sfd=%d\n", sfd);

	//允许端口被快速复用成功
	int reuse = 1;
	if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
	{
		ERR("setsockopt()");
		exit(1);
	}
	printf("允许端口被快速复用成功\n");
	
	//绑定套接字
	struct sockaddr_in sin = {AF_INET, htons(PORT), .sin_addr.s_addr = inet_addr(IP)};
	socklen_t addrlen = sizeof(sin);
	if(bind(sfd, (struct sockaddr*)&sin, addrlen) < 0)
	{
		ERR("bind()");
		exit(1);
	}
	printf("bind success\n");

	//设置为监听状态
	if(listen(sfd, 128) < 0)
	{
		ERR("listen()");
		exit(1);
	}
	printf("listen success\n");
	
	//创建一个读集合
	fd_set readfds, tempfds;
	FD_ZERO(&readfds);
	FD_SET(0, &readfds);
	FD_SET(sfd, &readfds);

	int maxfd = sfd;
	struct sockaddr_in Cli_addr[1024];
	int fds;

	while(1)
	{
		tempfds = readfds;
		//执行IO多路复用
		fds = select(maxfd+1, &tempfds, NULL, NULL, NULL);
		if(fds < 0)
		{
			ERR("select()");
			exit(1);
		}

		for(int fd=0; fd<=maxfd; fd++)
		{
			if(FD_ISSET(fd, &tempfds) == 0)
				continue;

			if(0 == fd)
			{
				//触发终端输入事件
				printf("触发终端输入事件\n");
				stdin_put();
			}
			else if(sfd == fd)
			{
				//触发客户端连接事件
				printf("触发客户端连接事件\n");
				Cli_connect(sfd, Cli_addr, &readfds, &maxfd);
			}
			else
			{
				//触发客户端交互事件
				printf("触发客户端交互事件\n");
				Cli_intreact(fd, Cli_addr, &readfds, &maxfd);
			}
		}
	}
	
	if(close(sfd) < 0)
	{
		ERR("close()");
		exit(1);
	}
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值