Day6(网络编程)

使用select实现TCP随时收发的客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <poll.h>

#define PORT 4444
#define IP "192.168.31.72"

#define ERR_MSG(msg) do{\
	fprintf(stderr, "line:%d ", __LINE__);\
	perror(msg);\
	return -1;\
}while(0)

int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sfd < 0)
	{
		ERR_MSG("socket");
	}
	printf("socket creat 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);

	socklen_t addrlen = sizeof(sin);
	//使用connect连接服务器
	if(-1 == connect(sfd, (struct sockaddr *)&sin, addrlen))
	{
		ERR_MSG("connect");
	}

	fd_set readfds, tempfds; 	//设置两个集合
	FD_ZERO(&readfds); 	//清空集合
	FD_ZERO(&tempfds); 	//清空集合

	//将需要的文件描述表填入集合中
	FD_SET(0, &readfds);
	FD_SET(sfd, &readfds);
	
	char buf[128] = "";
	ssize_t res = -1;
	int s_res = 0;
	int maxfd = sfd;

	while(1)
	{
		tempfds = readfds; 	//防止被覆盖集合

		s_res = select(maxfd+1, &tempfds, NULL, NULL, NULL);
		if(s_res < 0)
		{
			ERR_MSG("select");
		}
		else if(0 == s_res)
		{
			printf("time out...\n");
			break;
		}

		//运行到当前位置,代表有文件描述符产生事件
		//判断所有文件描述符实际产生的事件revents中是否有POLIN
		if(FD_ISSET(0, &tempfds))
		{
			//send发送文件
			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");
			}
		}

		if(FD_ISSET(sfd, &tempfds))
		{
			//recv接收文件
			bzero(buf, sizeof(buf));
			res = recv(sfd, buf, sizeof(buf), 0);
			if(res < 0)
			{
				ERR_MSG("recv");
			}
			else if(res == 0)
			{
				break;
			}
			printf("receve message: %s\n", buf);
			if(!strcmp("quit", buf))
			{
				break;
			}
		}
	}
	//关闭套接字
	close(sfd);

	return 0;
}

服务器结果:
ubuntu@ubuntu:06_IO$ ./ser 
socket creat success sfd = 3
[192.168.31.72 : 59406] newfd=4 客户端连接成功
[192.168.31.72 : 59406] newfd=4 接收文件:[nihao]
文件回复成功
sdf
输入格式不正确,请按照文件描述 发送内容 格式进行发送
4 nihao
文件发送成功
[192.168.31.72 : 59406] newfd=4 接收文件:[ai]
文件回复成功

客户端结果:
ubuntu@ubuntu:06_IO$ ./cli 
socket creat success sfd = 3
nihao
receve message: nihao---mwt
receve message: nihao
ai
receve message: ai---mwt

selec服务端
#include <stdio.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>

#define PORT 4444
#define IP "192.168.31.72"

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

	//设置允许端口快速被重启
	int reuse = 1;
	if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
	{
		perror("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)
	{
		perror("bind");
		return -1;
	}
	//设置监听
	if(listen(sfd, 64) < 0)
	{
		perror("listen");
		return -1;
	}

	//填充集合体
	fd_set readfds, tempfds;
	FD_ZERO(&readfds);
	FD_ZERO(&tempfds);

	FD_SET(0, &readfds); 	//将文件描述符表填入集合体中
	FD_SET(sfd, &readfds);

	//定义保存连接客户端地址信息的结构体
	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);

	struct sockaddr_in SaveCin[1024-4];

	//定义变量
	int s_res = 0;
	char buf[32] = "";

	int maxfd = sfd;

	int sndfd = 0;
	ssize_t res = 0;
	int newfd = -1;
	
	//进入循环
	while(1)
	{
		//为了防止被覆盖使用一个暂存变量
		tempfds = readfds;
		//使用select进行阻塞,如果有文件描述符表准备好接触阻塞
		s_res = select(maxfd+1, &tempfds, NULL, NULL, NULL);
		if(s_res < 0)
		{
			perror("select");
			return -1;
		}
		printf("触发select\n");

		//判断是否满足文件描述表准备就绪,准备好就开始进行文件的收发
		for(int i=0; i<=maxfd; i++)
		{
			//文件描述符如果不在,则直接退出
			if(!FD_ISSET(i, &tempfds))
			{
				continue;
			}
			//当文件描述符等于0时,键盘触发
			if(0 == i)
			{
				printf("触发键盘\n");
				//从键盘获取数据
				res = scanf("%d %s", &sndfd, buf);
				while(getchar() != 10);

				if(res != 2)
				{
					fprintf(stderr, "请输入正确格式 文件描述符 内容\n");
					continue;
				}

				if(sndfd > 1024 || sndfd <= sfd || !FD_ISSET(sndfd, &readfds))
				{
					fprintf(stderr, "客户端不存在,请重新输入\n");
					continue;
				}

				//运行到这个位置则证明没有问题,可以开始发送
				if(send(sndfd, buf, sizeof(buf), 0) < 0)
				{
					perror("send");
					return -1;
				}
				printf("输送成功\n");
			}
			//文件描述符等于sfd时,使用accept函数创建newfd
			else if(sfd == i)
			{
				printf("触发客户端连接\n");

				newfd = accept(sfd, (struct sockaddr *)&cin,  &addrlen);
				if(newfd < 0)
				{
					perror("accept");
					return -1;
				}
				printf("客户端[%s:%d] newfd = %d连接成功\n", inet_ntoa(cin.sin_addr), \
						ntohs(cin.sin_port), newfd);
				//使用结构体存储多个连接客户端的信息
				SaveCin[newfd-4] = cin;
				//将新生成的文件描述符放入集合中
				FD_SET(newfd, &readfds);

				//更新maxfd
				maxfd = maxfd>newfd ? maxfd : newfd;
			}
			else
			{
				//是其他的newfd时开始进行收发
				printf("触发客户端操作\n");

				bzero(buf, sizeof(buf));
				res = recv(i, buf, sizeof(buf), 0);
				if(res < 0)
				{
					perror("recv");
					return -1;
				}
				else if(0 == res)
				{
					fprintf(stderr, "客户端[%s:%d] newfd = %d已下线\n", \
							inet_ntoa(SaveCin[i-4].sin_addr), \
						ntohs(SaveCin[i-4].sin_port), i);
					//关闭文件描述符
					close(i);
					//剔除文件描述符集合
					FD_CLR(i, &readfds);
					//更新文件描述符集合
					for(int j=maxfd; j>sfd; j--)
					{
						if(FD_ISSET(j, &readfds))
						{
							maxfd = j;
							break;
						}
					}
				}
				else
				{
					printf("客户端[%s:%d] newfd = %d 传输内容[%s]\n", \
							inet_ntoa(SaveCin[i-4].sin_addr), \
						ntohs(SaveCin[i-4].sin_port), i, buf);
				}

			}
		}
	}
	//关闭套接字
	close(sfd);

	return 0;
}

poll搭建TCP客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <poll.h>

#define PORT 4444
#define IP "192.168.31.72"

#define ERR_MSG(msg) do{\
	fprintf(stderr, "line:%d ", __LINE__);\
	perror(msg);\
	return -1;\
}while(0)

int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sfd < 0)
	{
		ERR_MSG("socket");
	}
	printf("socket creat 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);

	socklen_t addrlen = sizeof(sin);
	//使用connect连接服务器
	if(-1 == connect(sfd, (struct sockaddr *)&sin, addrlen))
	{
		ERR_MSG("connect");
	}

	//创建合集
	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 = -1;
	int p_res = 0;

	while(1)
	{
		p_res = poll(fds, 2, -1);
		if(p_res < 0)
		{
			ERR_MSG("poll");
		}
		else if(0 == p_res)
		{
			printf("time out...\n");
			break;
		}

		//运行到当前位置,代表有文件描述符产生事件
		//判断所有文件描述符实际产生的事件revents中是否有POLIN
		if(fds[0].revents & POLLIN)
		{
			//send发送文件
			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");
			}
		}

		if(fds[1].revents * POLLIN)
		{
			//recv接收文件
			bzero(buf, sizeof(buf));
			res = recv(sfd, buf, sizeof(buf), 0);
			if(res < 0)
			{
				ERR_MSG("recv");
			}
			else if(res == 0)
			{
				break;
			}
			printf("receve message: %s\n", buf);
			if(!strcmp("quit", buf))
			{
				break;
			}
		}
	}
	//关闭套接字
	close(sfd);

	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值