day37:网编day4,多点通信和并发服务器

一、广播接收方:

#include 

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

#define BRD_IP "192.168.114.255"
#define BRD_PORT 8888

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

	//填充地址信息结构体给bind用
	struct sockaddr_in sin;
	sin.sin_family = AF_INET; 	//必须填AF_INET
	sin.sin_port = htons(BRD_PORT); 	//广播端口号
	sin.sin_addr.s_addr = inet_addr(BRD_IP); 	//广播IP
	//bind绑定地址信息
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind success[%s:%d]\n",BRD_IP,BRD_PORT);
	
	socklen_t addrlen = sizeof(sin);
	char buf[128]="";
	ssize_t len;
	while(1)
	{
		if((len = recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen)) < 0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}
		printf("recvfrom success %s\n",buf);
	}
	close(sfd);
	return 0;
}

二、广播发送方:

#include 

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

#define BRD_IP "192.168.114.255"
#define BRD_PORT 8888

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int cfd = -1;
	if((cfd = socket(AF_INET,SOCK_DGRAM,0)) < 0)
	{
		ERR_MSG("socket");
	}
	printf("socket success cfd=%d\n",cfd);
	
	int broad = 1;
	if(setsockopt(cfd,SOL_SOCKET,SO_BROADCAST,(int*)&broad,sizeof(broad)) < 0)
	{
		ERR_MSG("setsockpot");
		return-1;
	}
	printf("setsockpot success\n");
	//非必须绑定
	//填充地址信息结构体给sendto
	struct sockaddr_in sin;
	sin.sin_family = AF_INET; 	//必须填AF_INET
	sin.sin_port = htons(BRD_PORT); 	//广播端口号
	sin.sin_addr.s_addr = inet_addr(BRD_IP); 	//广播IP
	printf("bind success[%s:%d]\n",BRD_IP,BRD_PORT);
	
	char buf[128]="";
	ssize_t len;
	while(1)
	{
		//清空buf
		bzero(buf,sizeof(buf));

		//从终端输入数据保存到buf中
		fscanf(stdin,"%s",buf);
		while(getchar()!=10);

		if(sendto(cfd,buf,strlen(buf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
		{
			ERR_MSG("sendto");
			return -1;
		}
		printf("sendto success\n");
	}
	close(cfd);
	return 0;
}

三、组播接收方:

#include 

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

#define GRP_IP "224.1.2.3" 	//组播IP
#define IP "192.168.114.94" //本机IP
#define GRP_PORT 8888

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

	//填充结构体,来完成组播连接
	struct ip_mreqn grp;
	grp.imr_multiaddr.s_addr = inet_addr(GRP_IP); //组播IP
	grp.imr_address.s_addr = inet_addr(IP); //本机IP
	grp.imr_ifindex = 0; //自动

	//设置连接组播
	if(setsockopt(sfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&grp,sizeof(grp)) < 0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	printf("setsockopt success\n");

	//填充地址信息结构体给bind用
	struct sockaddr_in sin;
	sin.sin_family = AF_INET; 	//必须填AF_INET
	sin.sin_port = htons(GRP_PORT); 	//组播端口号
	sin.sin_addr.s_addr = inet_addr(GRP_IP); 	//组播IP
	//bind绑定地址信息
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind success[%s:%d]\n",GRP_IP,GRP_PORT);
	
	socklen_t addrlen = sizeof(sin);
	char buf[128]="";
	ssize_t len;
	while(1)
	{
		if((len = recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen)) < 0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}
		printf("recvfrom success %s\n",buf);
	}
	close(sfd);
	return 0;
}

四、组播发送方:

#include 

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

#define BRD_IP "224.1.2.3"
#define BRD_PORT 8888

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int cfd = -1;
	if((cfd = socket(AF_INET,SOCK_DGRAM,0)) < 0)
	{
		ERR_MSG("socket");
	}
	printf("socket success cfd=%d\n",cfd);
	
	int broad = 1;
	if(setsockopt(cfd,SOL_SOCKET,SO_BROADCAST,(int*)&broad,sizeof(broad)) < 0)
	{
		ERR_MSG("setsockpot");
		return-1;
	}
	printf("setsockpot success\n");
	//非必须绑定
	//填充地址信息结构体给sendto
	struct sockaddr_in sin;
	sin.sin_family = AF_INET; 	//必须填AF_INET
	sin.sin_port = htons(BRD_PORT); 	//广播端口号
	sin.sin_addr.s_addr = inet_addr(BRD_IP); 	//广播IP
	printf("bind success[%s:%d]\n",BRD_IP,BRD_PORT);
	
	char buf[128]="";
	ssize_t len;
	while(1)
	{
		//清空buf
		bzero(buf,sizeof(buf));

		//从终端输入数据保存到buf中
		fscanf(stdin,"%s",buf);
		while(getchar()!=10);

		if(sendto(cfd,buf,strlen(buf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
		{
			ERR_MSG("sendto");
			return -1;
		}
		printf("sendto success\n");
	}
	close(cfd);
	return 0;
}

五、TCP多线程并发服务器:

#include 

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

#define IP "192.168.114.94"
#define PORT 8888

int del_cli_msg(int newfd,struct sockaddr_in cin);
void handler(int num)
{
	if(num == SIGCHLD)
	{
		while(waitpid(-1,NULL,WNOHANG) > 0);
	}
}
int main(int argc, const char *argv[])
{
	if(signal(SIGCHLD,handler) == SIG_ERR)
	{
		ERR_MSG("signal");
		return -1;
	}
	printf("signal success\n");
	//创建流式套接字
	int sfd = -1;
	if((sfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("socket success 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使用

	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT); 	//服务器端口号
	sin.sin_addr.s_addr = inet_addr(IP); 	//服务器IP
	//bind必须绑定IP和端口号
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind success [%s:%d]\n",IP,PORT);
	//listen把sfd切换成监听模式
	if(listen(sfd,128) < 0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success sfd = %d\n",sfd);
	struct sockaddr_in cin;
	socklen_t addrlen=sizeof(cin);
	int newfd =-1;
	pid_t pid;
	while(1)
	{
		//accept接收客户端发送过来的地址信息,创建一个newfd与其通讯accept
		//父进程专门负责连接
		if((newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen)) < 0)
		{
			ERR_MSG("accept");
			return -1;
		}
		printf("[%s:%d]客户端连接成功 newfd = %d\n",\
				inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
		pid = fork();
		if(0 == pid)
		{
			close(sfd);
			//子进程专门负责交互
			del_cli_msg(newfd,sin);
			exit(0);
		}
		close(newfd);
	}
	close(sfd);
	return 0;
}
int del_cli_msg(int newfd,struct sockaddr_in cin)
{
	char buf[128] ="";
	ssize_t len = 0;
	while(1)
	{
		bzero(buf,sizeof(buf));
		len = recv(newfd,buf,sizeof(buf),0);
		if(len< 0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}
		else if(len == 0)
		{
			printf("[%s:%d]客户端已下线 newfd = %d\n",\
					inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
			break;
		}
		printf("recv success [%s:%d],newfd=%d buf=%s\n",\
				inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd,buf);
		/*
		bzero(buf,sizeof(buf));
		printf("请输入数据发送给客户端");
		fscanf(stdin,"%s",buf);
		while(getchar()!=10);
		*/
		strcat(buf,">-<");
		if(send(newfd,buf,strlen(buf),0) < 0)
		{
			ERR_MSG("send");
			return -1;
		}
		printf("send success buf = %s\n",buf);
	}
}

六、TCP多线程并发服务器:

#include 

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

#define IP "192.168.114.94"
#define PORT 8888
struct Data
{
	int newfd;
	struct sockaddr_in cin;
};
void *del_cli_msg(void *arg);
int main(int argc, const char *argv[])
{
	printf("signal success\n");
	//创建流式套接字
	int sfd = -1;
	if((sfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("socket success 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使用

	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT); 	//服务器端口号
	sin.sin_addr.s_addr = inet_addr(IP); 	//服务器IP
	//bind必须绑定IP和端口号
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind success [%s:%d]\n",IP,PORT);
	//listen把sfd切换成监听模式
	if(listen(sfd,128) < 0)
	{
		ERR_MSG("listen");
		return -1;
	}
	printf("listen success sfd = %d\n",sfd);
	struct sockaddr_in cin;
	socklen_t addrlen=sizeof(cin);
	int newfd =-1;
	pthread_t tid;
	while(1)
	{
		//accept接收客户端发送过来的地址信息,创建一个newfd与其通讯accept
		//父进程专门负责连接
		if((newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen)) < 0)
		{
			ERR_MSG("accept");
			return -1;
		}
		printf("[%s:%d]客户端连接成功 newfd = %d\n",\
				inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
		struct Data data;
		data.newfd = newfd;
		data.cin = cin;
		if(pthread_create(&tid,NULL,del_cli_msg,&data))
		{
			printf("pthread_create error\n");
			return -1;
		}
		printf("create success\n");
		pthread_detach(tid);
	}
	close(sfd);
	return 0;
}
void *del_cli_msg(void *arg)
{
	int newfd = ((struct Data*)arg)->newfd;
	struct sockaddr_in cin = ((struct Data*)arg)->cin;
	char buf[128] ="";
	ssize_t len = 0;
	while(1)
	{
		bzero(buf,sizeof(buf));
		len = recv(newfd,buf,sizeof(buf),0);
		if(len< 0)
		{
			ERR_MSG("recvfrom");
			break;
		}
		else if(len == 0)
		{
			printf("[%s:%d]客户端已下线 newfd = %d\n",\
					inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
			break;
		}
		printf("recv success [%s:%d],newfd=%d buf=%s\n",\
				inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd,buf);
		/*
		bzero(buf,sizeof(buf));
		printf("请输入数据发送给客户端");
		fscanf(stdin,"%s",buf);
		while(getchar()!=10);
		*/
		strcat(buf,">-<");
		if(send(newfd,buf,strlen(buf),0) < 0)
		{
			ERR_MSG("send");
			break;
		}
		//printf("send success buf = %s\n",buf);
	}
}

七、多线程并发服务器为什么不能将newfd定义成全局?

不能,因为如果newfd设成全局变量,而多线程之间共享0-3G用户空间,临界资源,所以会导致newfd变化后,原先线程进行交互。

八、基于UDP的TFTP文件传输,完成下载和上传功能,制作的是客户端,与tftp服务器进行交互

#include 

#define ERR(s) do\
{\
	fprintf(stderr,"__%d__",__LINE__);\
	perror(s);\
}while(0)
#define PORT 69
#define IP "192.168.114.103"

int do_download(int cfd,struct sockaddr_in sin);
int do_upload(int cfd,struct sockaddr_in sin);
int main(int argc, const char *argv[])
{

	//创建报式套接字socket
	int cfd;
	if((cfd = socket(AF_INET,SOCK_DGRAM,0)) < 0)
	{
		ERR("socket");
		return -1;
	}
	printf("socket success\n");

	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT);
	sin.sin_addr.s_addr = inet_addr(IP);
	char choose = 0;
	while(1)
	{
		system("clear");
		printf("--------------------------------\n");
		printf("-------------1下载--------------\n");
		printf("-------------2上传--------------\n");
		printf("-------------3退出--------------\n");
		printf("--------------------------------\n");
		printf("请输入>>>");
		scanf("%c",&choose);
		while(getchar()!=10); 	//吸收垃圾字符
		switch(choose)
		{
		case '1':
			do_download(cfd,sin);
			break;
		case '2':
			do_upload(cfd,sin);
			break;
		case '3':
			goto END;
			break;
		default :
			printf("输入错误,请重新输入\n");
		}
		printf("请输入任意字符清屏\n");
		while(getchar()!=10);
	}
END:
	//关闭套接字
	close(cfd);
	return 0;
}

int do_download(int cfd,struct sockaddr_in sin)
{
	/*
	//组下载请求包
	char buf[516] = "";
	char f_n[20] = "";
	//操作码
	unsigned short *ptr1 = (unsigned short*)buf;
	 *ptr1 = htons(1); 	//组操作码
	//文件名
	char *ptr2 = (char *)(ptr1+1);
	strcpy(ptr2,f_n);
	//模式
	char *ptr3 = ptr2 + strlen(f_n) + 1;
	strcpy(ptr3,"octet");

	int size = 2+strlen(ptr2)+1+strlen(ptr3)+1;
	*/
	char buf[516] = "";
	char f_n[20] = "";
	int num=1;
	printf("请输入文件名>>> ");
	//终端输入要下载的文件名
	fgets(f_n,sizeof(f_n),stdin);
	f_n[strlen(f_n)-1] = '\0';
	int fd;
	if((fd = open(f_n,O_RDONLY|O_CREAT|O_TRUNC,0664)) == -1)
	{
		ERR("open");
		return -1;
	}
	//用sprintf拼接
	int size = sprintf(buf,"%c%c%s%c%s%c",0,1,f_n,0,"octet",0);
	//发送下载请求sendto
	if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin)) < 0 )
	{
		ERR("sendto askpackage");
		return -1;
	}
	printf("sendto askpackage success\n");
	ssize_t len = -1;
	struct sockaddr_in addrin;
	socklen_t addrlen=sizeof(addrin);
	while(1)
	{
		//清空buf
		bzero(buf,sizeof(buf));
		//接收数据recvfrom,接收地址信息
		if((len = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&addrin,&addrlen)) < 0)
		{
			ERR("recvfrom datapackage");
			return -1;
		}
		unsigned short ptr2=ntohs(*(unsigned short*)(buf+2));
		printf("%hu\n",ptr2);	
		//当操作码为5时
		if(5 == buf[1])
		{
			fprintf(stderr, "错误码:%d,错误信息:%s\n", ntohs(*(unsigned short*)(buf+2)), buf+4);			
			break;
		}
		//当操作码为3,并且块编号正确时
		else if(3 == buf[1] && num == ptr2)
		{
			printf("%d\n",fd);
			//跳过前四个字节,写入文件

			if(write(fd,buf+4,len-4)<0)
			{
				ERR("write");
				return -1;
			}

			//组ACK包
			buf[1] = 4;	
			//发送ACK包,sendto
			if(sendto(cfd,buf,4,0,(struct sockaddr*)&addrin,addrlen) < 0)
			{
				ERR("sendto ACKpackage");
				return -1;
			}
			num++;
			//判断数据是否小于512个字节,若小于则下载完成
			if(len < 516)
			{
				printf("download success\n");
				break;
			}
		}
	}
	close(fd);
	return 0;
}

int do_upload(int cfd,struct sockaddr_in sin)
{
	char f_n[20]="";
	printf("请输入传输文件名>>>");
	fscanf(stdin,"%s",f_n);
	while(getchar()!=10);

	//打开文件
	int srcfd = -1;
	if((srcfd = open(f_n,O_RDONLY)) < 0)
	{
		if(errno == ENOENT)
		{
			printf("文件不存在,请重新输入>>>");
			return -2;
		}
		else
		{
			ERR("open srcfile");
			return -1;
		}
	}

	char buf[516]="";
	//用sprintf拼接读写请求封包
	int size=sprintf(buf,"%c%c%s%c%s%c",0,2,f_n,0,"octet",0);
	if(sendto(cfd,buf,size,0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		ERR("sendto askpackage");
		return -1;
	}
	printf("sendto askpackage success\n");


	//记录块编号
	int num=0;
	unsigned short* ptr = (unsigned short *)(buf+2);
	//记录我要传过去数据的大小
	ssize_t len = -1;
	socklen_t addrlen = sizeof(sin);
	while(1)
	{
		bzero(buf,sizeof(buf));
		//接受ACK
		if(recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen) < 0)
		{
			ERR("recvfrom ACKpackage");
			return -1;
		}
		printf("recvfrom ACKpackage success\n");
		//当传过来的是ACK并且对应的块编号是需要的块编号时
		if(buf[1] == 4)
		{
			if(*ptr == htons(num))
			{
				num++;
				//把操作码改为3,向服务器发送数据
				buf[1] = 3;
				//*(unsigned short *)(buf+2)= htons(num);
				*ptr = htons(num);
				//从文件中读取数据,保存到buf中
				len = read(srcfd,buf+4,sizeof(buf)-4);
				if(len < 0)
				{
					ERR("read srcfd");
					return -1;
				}
				else if(0 == len)
				{
					printf("文件上传完毕\n");
					break;
				}
				//向服务器发送打包好的数据包
				if(sendto(cfd,buf,len+4,0,(struct sockaddr*)&sin,addrlen) < 0)
				{
					ERR("sendto datapackage");
					return -1;
				}
				printf("sendto datapackage success\n");
			}else{
				printf("网络不好,上传数据失败\n");
				break;
			}
		}
		else if(buf[1] == 5)
		{
			fprintf(stderr,"错误码:%d,错误信息:%s\n",\
					ntohs(*(unsigned short*)(buf+2)),buf+4);
			break;
		}
	}

	return 0;
}

你可能感兴趣的:(服务器,c语言,tcp/ip,网络)