作业6.23

多线程并发服务器

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


#define PORT 8888
#define IP "192.168.124.109"

#define ERR_MSG(msg) do{\
	fprintf(stderr,"line:%d",__LINE__);\
	fprintf(stderr,"func:%s",__func__);\
	perror(msg);\
}while(0)
void* tx(void* arg);
struct fsxx{
	int newfd;
	struct sockaddr_in cin;
};

int main(int argc, const char *argv[])
{
	//创建流式套节奏
	int sfd=socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0){
		ERR_MSG("socket");
		return -1;
	}
	printf("流式套节奏创建完毕 sfd=%d\n",sfd);


	//允许端口快速被复用
	int reuse = 1;
	if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	printf("允许端口快速重用\n");



	//填充服务器地址信息结构体给bind函数使用
	//真实的地址信息结构体根据地址族制度,AF_INET-->>man 7 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);//本机IP,ifconfig,必须是桥接模式

	//绑定服务器地址信息,必须绑定
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0){
		ERR_MSG("bing");
		return -1;
	}
	//将套节奏设置为被动监听状态
	if(listen(sfd,128)<0){
		ERR_MSG("listen");
		return -1;
	}
	printf("监听成功\n");
	//获取连接成功的客户端信息,生成一个新的套节奏文件描述符
	struct sockaddr_in cin;
	socklen_t addrlen=sizeof(cin);

	while(1){
		int newfd=accept(sfd,(struct sockaddr*)&cin,&addrlen);
		if(newfd<0){
			ERR_MSG("accpet");
			return -1;
		}
		printf("[%s:%d] newfd=%d 客户端链接成功\n",\
				inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
		//创建分子线程
		pthread_t tid;
		struct fsxx cssz;
		cssz.newfd=newfd;
		cssz.cin=cin;
		if(pthread_create(&tid,NULL,tx,(void*)&cssz)!=0){
			ERR_MSG("pthread_create");
		}
		pthread_detach(tid);

	}
	//关闭文件描述符
	if(close(sfd)<0){
		ERR_MSG("close");
		return -1;
	}
	return 0;
}

void* tx(void* arg){
	int newfd=((struct fsxx*)arg)->newfd;
	struct sockaddr_in cin=((struct fsxx*)arg)->cin;
	char buf[128]="";
	ssize_t res;
	char buf2[128]="";
	ssize_t res2;
	while(1){
		bzero(buf,sizeof(buf));
		//接收
		res=recv(newfd,buf,sizeof(buf),0);
		if(res<0){
			ERR_MSG("recv");
			break;
		}else if(res==0){
			printf("[%s:%d]客户端下线\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
			break;
		}
		printf("[%s:%d]newfd=%d: %s\n",\
				inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd,buf);
		//发送
		res2=send(newfd,buf,sizeof(buf),0);
		if(res2<0){
			ERR_MSG("send");
			break;
		}
		printf("发送成功\n");
	}
	pthread_exit(NULL);
}

服务器

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

#define PORT 8888
#define IP "192.168.1.8"

#define ERR_MSG(msg) do{\
	fprintf(stderr,"line:%d",__LINE__);\
	fprintf(stderr,"func:%s",__func__);\
	perror(msg);\
}while(0)
void jpjs(fd_set readfds);
void khlj(int sfd,struct sockaddr_in cins[],fd_set *readfds,int *maxfd);
void khjh(int i,struct sockaddr_in cins[],fd_set *readfds,int *maxfd);


int main(int argc, const char *argv[])
{
	//创建流式套节奏
	int sfd=socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0){
		ERR_MSG("socket");
		return -1;
	}
	printf("流式套节奏创建完毕 sfd=%d\n",sfd);


	//允许端口快速被复用
	int reuse = 1;
	if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	printf("允许端口快速重用\n");



	//填充服务器地址信息结构体给bind函数使用
	//真实的地址信息结构体根据地址族制度,AF_INET-->>man 7 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);//本机IP,ifconfig,必须是桥接模式

	//绑定服务器地址信息,必须绑定
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0){
		ERR_MSG("bing");
		return -1;
	}
	//将套节奏设置为被动监听状态
	if(listen(sfd,128)<0){
		ERR_MSG("listen");
		return -1;
	}
	printf("监听成功\n");

	//创建读集合
	//-->fd_set本质上是一个结构体,其中只有一个整数数组
	//若不清空,会是一堆随机值,可能会随机到有效的文件描述符编号
	//但是该文件描述符标号不要检测,从而导致select异常解除阻塞
	fd_set readfds,tmpfds;
	FD_ZERO(&readfds);//清空

	//将需要的文件描述符添加到集合中
	FD_SET(0,&readfds);//添加
	FD_SET(sfd,&readfds);
	int maxfd=sfd;//集合中最大的文件描述符

	int s_res;
	struct sockaddr_in cins[1024];


	while(1){
		tmpfds=readfds;
		//让内核监测集合中的文件描述符是否准备就绪
		s_res=select(maxfd+1,&tmpfds,NULL,NULL,NULL);
		if(s_res<0){
			ERR_MSG("select");
			return -1;
		}else if(s_res==0){
			printf("time out....\n");
			break;
		}
		//能运行到当前位置,则代表集合中有文件描述符准备就绪
		//判断集合中那个文件描述符准备就绪,执行对应的处理函数

		/*
		 * 集合中会剩下产生事件的文件描述符:
		 * 0好准备就绪,则集合会只剩下0
		 * sfd准备就绪,则集合会只剩下sfd
		 * 若0和sfd都准备就绪,则集合中会剩下0和sfd
		 * 所以只要判断集合中剩下那个文件描述符,就代表该文件描述符准备就绪
		 */
		for(int i=0;i<=maxfd;i++){
			if(FD_ISSET(i,&tmpfds)==0){
				continue;
			}
			if(i==0){
				//键盘输入
				printf("触发键盘输入事件\n");
				jpjs(readfds);
			}else if(i==sfd){
				//客户端链接
				printf("触发客户端连接事件\n");
				khlj(sfd,cins,&readfds,&maxfd);
			}else{
				printf("触发客户端交互事件\n");
				khjh(i,cins,&readfds,&maxfd);
			}
		}
	}


	//关闭文件描述符
	if(close(sfd)<0){
		ERR_MSG("close");
		return -1;
	}
	return 0;
}

void jpjs(fd_set readfds){	
	int sndfd;
	char buf[128]="";
	int res=scanf("%d %s",&sndfd,buf);
	while(getchar()!='\n');
	if(res!=2){
		printf("请输入正确格式");
		return;
	}
	if(sndfd<=3||FD_ISSET(sndfd,&readfds)==0){
		printf("sndfd=%d 错误,请输入合法的文件描述符\n",sndfd);
		return;
	}
	if(send(sndfd,buf,sizeof(buf),0)<0){
		ERR_MSG("send");
		return;
	}
	return;

}

void khlj(int sfd,struct sockaddr_in cins[],fd_set *readfds,int *maxfd){
	struct sockaddr_in cin;
	socklen_t len=sizeof(cin);
	int newfd=accept(sfd,(struct sockaddr*)&cin,&len);
	if(newfd<0){
		ERR_MSG("accpet");
		return ;
	}
	printf("[%s:%d] newfd=%d 客户端链接成功\n",\
			inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
	cins[newfd]=cin;
	FD_SET(newfd,readfds);
	*maxfd=*maxfd > newfd ? *maxfd:newfd;
}

void khjh(int i,struct sockaddr_in cins[],fd_set *readfds,int *maxfd){
	char buf[128]="";
	ssize_t res;
	bzero(buf,sizeof(buf));
	//接收
	res=recv(i,buf,sizeof(buf),0);
	if(res<0){
		ERR_MSG("recv");
		return ;
	}else if(res==0){
		printf("[%s:%d]客户端下线\n",inet_ntoa(cins[i].sin_addr),ntohs(cins[i].sin_port));
		close(i);
		FD_CLR(i,readfds);
		while(FD_ISSET(*maxfd,readfds)==0 && *maxfd-->0);
		return ;
	}
	printf("[%s:%d]newfd=%d: %s\n",\
			inet_ntoa(cins[i].sin_addr),ntohs(cins[i].sin_port),i,buf);
	//发送
	if(send(i,buf,sizeof(buf),0)<0){
		ERR_MSG("send");
		return ;
	}
	printf("发送成功\n");
}

客户端

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

#define PORT   8888
#define IP    "192.168.1.8"
#define ERR_MSG(msg) do{\
	fprintf(stderr,"line:%d",__LINE__);\
	perror(msg);\
}while(0)
int main(int argc, const char *argv[])
{
	//创建流式套接字
	int sfd=socket(AF_INET,SOCK_STREAM,0);
	if(sfd<0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("流式套接字创建完毕 sfd=%d\n",sfd);
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;                      //夸主机通信填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("连接服务器成功[%s:%d]成功\n",IP,PORT);
	fd_set readfds ,temfds;
	FD_ZERO(&readfds);
	FD_SET(0,&readfds);
	FD_SET(sfd,&readfds);
	int res,s_res;
	int maxfd=sfd;
	char buf[128]="";
	while(1)
	{

		temfds=readfds;
		s_res=select(maxfd+1,&temfds,NULL,NULL,NULL);
		if(s_res<0)
		{
			ERR_MSG("select");
			return -1;
		}
		else if(s_res==0)
		{
			printf("time out......\n");
			break;
		}
		for(int i=0;i<=maxfd;i++){
			if(FD_ISSET(i,&temfds)==0)
			{
				continue;
			}
			if(i==0){
				printf("触发发送事情\n");
				bzero(buf,sizeof(buf));
				int sr=scanf("%s",buf);
				if(send(sfd,buf,sizeof(buf),0)<0)
				{
					ERR_MSG("send");
					return -1;
				}
			}else if(i==sfd)
			{
				printf("触发接收事件\n");
				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");
					break;
				}
				printf("[%s:%d]   newfd=%d:%s\n",IP,PORT,sfd,buf);
			}
		}
	}
	if(close(sfd)<0)
	{
		ERR_MSG("close");
		return -1;
	}
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值