2023.05.19 网络编程day5

1. select服务器

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define ERR_MSG(msg) do{\
	printf("line:%d\n", __LINE__);\
	perror(msg);\
} while(0)
#define IP "192.168.9.83"
#define PORT 6666

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

	//允许端口快速被复用
	int reuse = 1;
	if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
	{
		ERR_MSG("setsockopt");
		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(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind success\n");

	//监听
	if(listen(sfd, 128) < 0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success\n");

	//创建读集合
	//fd_set是一个结构体,成员只有一个long类型的数组,需要清空数组
	fd_set readfds, tempfds;
	FD_ZERO(&readfds);  //清空集合	
	FD_ZERO(&tempfds);  //清空集合	
	FD_SET(0, &readfds);
	FD_SET(sfd, &readfds);

	int s_res = 0;
	ssize_t res = 0;
	char buf[128] = "";	
	struct sockaddr_in cin;
	int addrlen = sizeof(cin);
	int newfd = -1;
	int maxfd = sfd;
	int sndfd = -1;
	struct sockaddr_in rcvcin[1024];

	while(1)
	{	
		tempfds = readfds;
		s_res = select(maxfd+1, &tempfds, NULL, NULL, NULL);
		if(s_res < 0)
		{
			ERR_MSG("select");
			return -1;
		}
		else if(s_res == 0)
		{
			printf("超时\n");
			break;
		}

		//当集合中的文件描述符准备就绪后,集合中会只剩下就绪的文件描述符
		for(int i=0; i<=maxfd; i++)
		{		
			//如果文件描述符不在集合中,则跳过
			if(!FD_ISSET(i, &tempfds))
				continue;

			if(i == 0)
			{
				//触发键盘输入事件
				bzero(buf, sizeof(buf));
				res = scanf("%d %s", &sndfd, buf);
				//吸收缓冲区所有垃圾字符,防止一直触发
				while(getchar() != '\n'); 
				//判断输入格式是否正确
				if(res != 2)
				{
					printf("输入格式有误,正确格式:fd string\n");
					continue;
				}

				//判断文件描述符是否存在
				if(sndfd<0 | sndfd>1023 | !FD_ISSET(sndfd, &readfds))
				{
					printf("输入的文件描述符不合法\n");
					continue;
				}

				if(send(sndfd, buf, sizeof(buf), 0) < 0)
				{
					ERR_MSG("send");
					return -1;
				}
				printf("发送成功\n");
			}
			else if(i == sfd)
			{
				//触发客户端连接事件
				newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);
				if(newfd < 0)
				{
					ERR_MSG("accept");
					return -1;
				}
				printf("[%s:%d] 客户端连接成功 newfd=%d\n", inet_ntoa(cin.sin_addr), cin.sin_port, newfd);

				FD_SET(newfd, &readfds);
				maxfd = maxfd>newfd ? maxfd : newfd;
				rcvcin[newfd] = cin;
			}
			else
			{
				//触发客户端交互事件
				bzero(buf, sizeof(buf));
				res = recv(i, buf, sizeof(buf), 0);
				if(res < 0)
				{
					ERR_MSG("recv");
					return -1;
				}
				else if(res == 0)
				{
					printf("[%s:%d] 客户端下线 newfd=%d\n", inet_ntoa(rcvcin[i].sin_addr), rcvcin[i].sin_port, i);
					close(i);   									  //关闭与该服务器通信的文件描述符
					FD_CLR(i, &readfds);  							  //将该文件描述符从集合中删除
					while(!FD_ISSET(maxfd, &readfds) && maxfd-->=0);  //重新确定maxfd
					continue;
				}
				printf("[%s:%d] newfd=%d: %s\n", inet_ntoa(rcvcin[i].sin_addr), rcvcin[i].sin_port, i, buf);
			}
		}
	}

	if(close(sfd) < 0)
	{
		ERR_MSG("close");
		return -1;
	}
	return 0;
}

2.select客户端

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define ERR_MSG(msg) do{\
	printf("line:%d\n", __LINE__);\
	perror(msg);\
} while(0)
#define IP "192.168.9.83"
#define PORT 6666

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

	//绑定,可省略,如果不绑定,操作系统会默认绑定一个可用IP和随机端口

	//连接服务器
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT);
	sin.sin_addr.s_addr = inet_addr(IP);
	if(connect(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("connect");
		return -1;
	}
	printf("connect success\n");
	
	fd_set readfds, tempfds;
	FD_ZERO(&readfds);
	FD_ZERO(&tempfds);
	FD_SET(0, &readfds);
	FD_SET(sfd, &readfds);
	char buf[128] = "";
	ssize_t res = 0;
	int s_res = 0;
	while(1)
	{
		//当集合中有文件描述符准备就绪后,会只保留就绪的文件描述符
		//所以每次进来需要重新赋值
		tempfds = readfds;
		s_res = select(sfd+1, &tempfds, NULL, NULL, NULL);
		if(s_res < 0)
		{
			ERR_MSG("select");
			return -1;
		}
		else if(s_res == 0)
		{
			printf("超时\n");
			return -1;
		}

		if(FD_ISSET(0, &tempfds))
		{
			bzero(buf, sizeof(buf));
			fgets(buf, sizeof(buf), stdin);
			buf[strlen(buf)-1] = 0;
			if(send(sfd, buf, sizeof(buf), 0) < 0)
			{
				ERR_MSG("send");
				return -1;
			}
		}
		if(FD_ISSET(sfd, &tempfds))
		{
			bzero(buf, sizeof(buf));
			res = recv(sfd, buf, sizeof(buf), 0);
			if(res < 0)
			{
				ERR_MSG("recv");
				return -1;
			}
			else if(res == 0)
			{
				printf("服务器下线\n");
				return -1;
			}
			printf("[%s: %d]: %s\n", IP, PORT, buf);
		}
	}

	if(close(sfd) < 0)
	{
		ERR_MSG("close");
		return -1;
	}
	return 0;
}

3.poll服务器

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

#define ERR_MSG(msg) do{\
	printf("line:%d\n", __LINE__);\
	perror(msg);\
} while(0)
#define IP "192.168.9.83"
#define PORT 6666

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

	//允许端口快速被复用
	int reuse = 1;
	if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
	{
		ERR_MSG("setsockopt");
		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(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind success\n");

	//监听
	if(listen(sfd, 128) < 0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success\n");

	struct pollfd fds[1024];
	nfds_t nfds = 2;
	fds[0].fd = 0;
	fds[0].events = POLLIN;
	fds[1].fd = sfd;
	fds[1].events = POLLIN;

	int p_res = 0;
	char buf[128] = "";
	ssize_t res = 0;
	int sndfd = -1;
	int newfd = -1;
	struct sockaddr_in rcvcin[1024];
	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);
	int flag=0;
	while(1)
	{
		p_res = poll(fds, nfds, -1);
		if(p_res < 0)
		{
			ERR_MSG("poll");
			return -1;
		}
		else if(p_res == 0)
		{
			printf("超时\n");
			return -1;
		}

		for(int i=0; i<=nfds; i++)
		{
			if(fds[i].revents & POLLIN)
			{
				if(i == 0)
				{
					//触发键盘输入事件
					bzero(buf, sizeof(buf));
					res = scanf("%d %s", &sndfd, buf);
					//吸收缓冲区所有垃圾字符,防止一直触发
					while(getchar() != '\n'); 
					//判断输入格式是否正确
					if(res != 2)
					{
						printf("输入格式有误,正确格式:fd string\n");
						continue;
					}
					
					//判断文件描述符是否存在
					flag = 0;
					for(int j=0; j<nfds; j++)
					{
						if(fds[j].fd == sndfd)
						{
							if(send(sndfd, buf, sizeof(buf), 0) < 0)
							{
								ERR_MSG("send");
								return -1;
							}
							printf("发送成功\n");
							flag = 1;
							continue;
						}
					}
					if(flag == 0)
						printf("输入的文件描述符不合法\n");
				}

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

					fds[nfds].fd = newfd;
					fds[nfds].events = POLLIN;
					nfds++;
					rcvcin[newfd] = cin;
				}

				else
				{
					//触发客户端交互事件
					bzero(buf, sizeof(buf));
					res = recv(fds[i].fd, buf, sizeof(buf), 0);
					if(res < 0)
					{
						ERR_MSG("recv");
						return -1;
					}
					else if(res == 0)
					{
						printf("[%s:%d] 客户端下线 newfd=%d\n", inet_ntoa(rcvcin[fds[i].fd].sin_addr), rcvcin[fds[i].fd].sin_port, fds[i].fd);
						//关闭与该服务器通信的文件描述符
						close(fds[i].fd);
						//将该文件描述符从集合中删除
						for(int j=i; j<nfds; j++)
							fds[i] = fds[i+1];
						nfds--;
						continue;
					}
					printf("[%s:%d] newfd=%d: %s\n", inet_ntoa(rcvcin[fds[i].fd].sin_addr), rcvcin[fds[i].fd].sin_port, fds[i].fd, buf);

				}
			}
		}
	}

	if(close(sfd) < 0)
	{
		ERR_MSG("close");
		return -1;
	}
	return 0;
}

4.poll客户端

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

#define ERR_MSG(msg) do{\
	printf("line:%d\n", __LINE__);\
	perror(msg);\
} while(0)
#define IP "192.168.9.83"
#define PORT 6666

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

	//绑定,可省略,如果不绑定,操作系统会默认绑定一个可用IP和随机端口

	//连接服务器
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT);
	sin.sin_addr.s_addr = inet_addr(IP);
	if(connect(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("connect");
		return -1;
	}
	printf("connect success\n");

	struct pollfd fds[2];
	fds[0].fd = 0;
	fds[0].events = POLLIN;
	fds[1].fd = sfd;
	fds[1].events = POLLIN;

	char buf[128] = "";
	ssize_t res = 0;
	int p_res = -1;
	while(1)
	{
		p_res = poll(fds, 2, -1);
		if(p_res < 0)
		{
			ERR_MSG("poll");
			return -1;
		}
		else if(p_res == 0)
		{
			printf("超时\n");
			return -1;
		}
		
		//触发终端输入事件,向服务器发送信息
		if(fds[0].revents & POLLIN)
		{
			bzero(buf, sizeof(buf));
			fgets(buf, sizeof(buf), stdin);
			buf[strlen(buf)-1] = 0;
			if(send(sfd, buf, sizeof(buf), 0) < 0)
			{
				ERR_MSG("send");
				return -1;
			}
		}

		//触发接收服务器信息事件
		if(fds[1].revents & POLLIN)
		{
			bzero(buf, sizeof(buf));
			res = recv(sfd, buf, sizeof(buf), 0);
			if(res < 0)
			{
				ERR_MSG("recv");
				return -1;
			}
			else if(res == 0)
			{
				printf("[%s:%d] 服务器下线\n", inet_ntoa(sin.sin_addr), sin.sin_port);
				break;
			}
			printf("[%s:%d] %s\n", inet_ntoa(sin.sin_addr), sin.sin_port, buf);
		}
	}
	if(close(sfd) < 0)
	{
		ERR_MSG("close");
		return -1;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值