服务器模型:循环服务器(TCP)和并发服务器(TCP)

        网络通信中,服务器端会面对需要处理多个客户端的情况,由于客户端的请求不定时,可能同时请求;总体来说,服务器端一般采用循环服务器模型和并发服务器模型来处理。

循环服务器模型:服务器端依次处理每个客户端,直到当前客户端所有请求处理完成,再处理下一个客户端。

并发服务器模型:在服务器端采用多任务机制(多进程或者多线程),分别为每一个客户端创造一个任务来处理。

两者对比
循环服务器模型并发服务器模型
优点简单服务器并发处理能力强,客户端不用等待时间过长
缺点单一处理,容易造成其他客户端等待时间过长需要避免僵死模式

  TCP循环服务器模型:(工作流程)/(特点分析)

  1. 服务器端从连接请求队列中提取请求,建立连接并返回新的已经连接套接字;
  2. 服务器端通过已经连接的套接字循环接受数据,处理并发送给客户端直到客户端关闭;
  3. 服务器关闭已连接套接字,返回步骤(1)处理下一个客户端;
  4. -------------------------------------------------------------------------------------------------------------------
  5. 服务器端采用循环嵌套来实现:外层循环依次提取客户端的请求队列并建立TCP连接,内层循环接受处理连接客户端的所有数据,直到客户端关闭;
  6. 如果当前客户端没有处理结束,其他客户端必须一直等待;

 流程:

socket(...); 
bind(...); 
listen(...);
while(1) 
{ 
    accept(...); 
    if(fork() == 0) 
    { 
        while(1) 
        { 
            recv(...); 
            process(...); 
            send(...); 
        }
            close(...); 
            exit(...); 
    }    
    close(...); 
}

 代码实现:

TCP循环服务器:

#include <stdio.h>
#include "net.h"
#include <string.h>
//编写一子函数,监听socket对象
//参数1:IP地址  char * ip
//参数2:端口号  int port
//返回值:失败返回-1,成功返回socket对象
int tcp_server(char * ip,int port);
int tcp_server(char * ip,int port)
{

	//1.创建Tcp套接字
	//2.绑定自己的IP地址和端口号
	//3.设置监听是否有人连接
	//创建并打开套接字
	int sockFd = socket(AF_INET,SOCK_STREAM,0);
	if(sockFd < 0)
	{
		perror("socket error");
		return -1;
	}
	//绑定服务器地址信息
	struct sockaddr_in servAddr = {0};
	servAddr.sin_family = AF_INET;
	servAddr.sin_port = htons(port);
	servAddr.sin_addr.s_addr = inet_addr(ip);
	int ret = bind(sockFd,(struct sockaddr *)&servAddr,sizeof(servAddr));
	if(ret < 0)
	{
		perror("bind error");
		return -1;
	}
	printf("bind OK\n");
	//创建监听队列
	ret = listen(sockFd,5);
	if(ret < 0)
	{
		perror("listen error");
		close(sockFd);
		return -1;
	}
	printf("listen OK\n");
	return sockFd;
}

//编写一子函数,实现客户端和服务器端的通信
//参数:连接好的socket对象
//返回值:通信结束返回-1,继续返回0
int tcp_com(int sockFd);
int tcp_com(int sockFd)
{
	char buf[N]={'\0'};
	char cmd[N]={'\0'};
	char filename[N]={'\0'};
	//接受客户端发过来的消息
	recv(sockFd,buf,sizeof(buf),0); //第一次接受"hello"
	printf("buf=%s\n",buf);
	if(strncmp(buf,"quit",4)==0)
	{
		printf("通信结束");
		return -1;

	}
	parseStr(buf,cmd,filename);	
	printf("cmd=%s\n",cmd);
	printf("filename=%s\n",filename);

	if(strncmp(cmd,"put",3)==0)
	{
		//接受文件(下载文件 download)
		download(sockFd,filename);
	}else if(strncmp(cmd,"get",3)==0)
	{
		//发送文件(上传文件 upload)
	}else
	{
		printf("指令有误\n");
	}

	return 0;
}

int main(int argc,char * argv[])
{
	if(argc!=3)
	{
		printf("input appname ip port\n");
		return -1;
	}
	//1.监听socket对象

	int tcp_socket=tcp_server(argv[1],atoi(argv[2]));
	//2.接受连接
	struct sockaddr_in client;
	int len=sizeof(client);

	while(1)
	{
		int newfd=accept(tcp_socket,(struct sockaddr *)&client,&len);
		if(newfd<0)
		{
			perror("accept error");
			return -1;
		}
		printf("accept ok\n");

		printf("client ip=%s  port=%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));

		while(1)
		{
			//3.接受/发送消息
			int n=tcp_com(newfd);

			//客户端和服务器端进行通信
			if(n<0){
				//4.关闭套接字
				close(newfd);
				break;
			}
		}
	}
	close(tcp_socket);
}

 头文件:

#ifndef _NET_H
#define _NET_H
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#define N 20
int parseStr(char *buf,char * cmd,char * filename);
int download(int sockfd,char * filename);
#endif

 

 对应客户端:

#ifndef _NET_H
#define _NET_H
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define N 20
int parseStr(char *buf,char * cmd,char * filename);
int upload(int sockfd,char * filename);
#endif

#include <stdio.h>
#include "net.h"
#include <string.h>
//编写一子函数,连接服务器
//参数1:IP地址ecv(sockFd,buf,sizeof(buf),0);
//参数2:端口号
//返回值:连接成功返回连接好的socket对象,失败返回-1
int tcp_connect(char * ip,int port);
int tcp_connect(char * ip,int port)
{
	//1.创建Tcp套接字
	//2.设置对方的IP地址和端口号
	//3.请求连接


	//创建并打开套接字
	int sockFd = socket(AF_INET,SOCK_STREAM,0);
	if(sockFd < 0)
	{
		perror("socket error");
		return -1;
	}
	printf("socket OK\n");
	//连接服务器
	struct sockaddr_in servAddr = {0};
	servAddr.sin_family = AF_INET;
	servAddr.sin_port = htons(port);
	servAddr.sin_addr.s_addr = inet_addr(ip);
	int ret = connect(sockFd,(struct sockaddr *)&servAddr,sizeof(servAddr));
	if(ret < 0)
	{
		close(sockFd);
		return -1;
	}
	printf("connect OK\n");
	return  sockFd;
}


//编写一子函数,实现客户端和服务端的通信
//参数:连接好的socket对象
//返回值:通信结束返回-1,继续返回0
int tcp_com(int sockFd);
int tcp_com(int sockFd)
{
	printf("请输入指令");
	char buf[N]={'\0'};
	char cmd[N]={'\0'};
	char filename[N]={'\0'};

	fgets(buf,sizeof(buf),stdin);
	send(sockFd,buf,strlen(buf),0);
	parseStr(buf,cmd,filename);
	printf("cmd==%s\n",cmd);
	printf("filename=%s\n",filename);

	if(strncmp(cmd,"put",3)==0)
	{
		//上传文件upload()
		upload(sockFd,filename);

	}else if(strncmp(cmd,"get",3)==0)
	{
		//下载文件download()
	}else if(strncmp(cmd,"quit",4)==0)
	{
		printf("通信结束\n");
		return -1;
	}else 
	{
		printf("指令有误\n");
	}



	return 0;
}

int main(int argc,char * argv[])
{
	if(argc!=3)
	{
		printf("input appname ip  port\n");
		return -1;
	}
	//1.请求连接服务器  //atoi()将字符串转换成整数
	//		    atof()将字符串转换成float
	int tcp_socket=tcp_connect(argv[1],atoi(argv[2]));
	//2.发送/接受消息
	while(1){
		//客户端和服务端之间的通信
		int n=tcp_com(tcp_socket);
		//3.关闭套接字
		if(n<0){
			close(tcp_socket);
			break;
		}
	}
	//
	//
}

TCP并发服务器模型:(工作流程)/(特点分析)

  1. 服务器端父进程从连接请求队列中提取请求,建立连接并返回新的已创建套接字;
  2. 服务器端父进程创建子进程为客户端服务,客户端关闭连接时,子进程结束;
  3. 服务器端父进程关闭已连接套接字,返回步骤(1);
  4. --------------------------------------------------------------------------------------------------------
  5. 服务器端父进程一旦接收到客户端的连接请求,便建立好连接并创建新的子进程。这意味着每个客广端在服务器端有一个专门的子进程为其服务。
  6. 服务器端的多个子进程同时运行 (宏观上),处理多个容户端。
  7. 服务器端的父进程不具体处理每个客户端的数据请求。

 1.多进程----流程:

socket(...)
bind(...)
listen(...)
while(1)
{
    accept(...)
    if(fork(...)==0)
    {
        process(...);
        close(...);
        exit(...);
    }
    close(...);
}

 代码实现:

头文件:

#ifndef _NET_H
#define _NET_H
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <signal.h>
#define N 20
int parseStr(char *buf,char * cmd,char * filename);
int download(int sockfd,char * filename);
#endif

 TCP多进程并发服务器:

#include <stdio.h>
#include "net.h"
#include <string.h>
//编写一子函数,监听socket对象
//参数1:IP地址  char * ip
//参数2:端口号  int port
//返回值:失败返回-1,成功返回socket对象
int tcp_server(char * ip,int port);
int tcp_server(char * ip,int port)
{

	//1.创建Tcp套接字
	//2.绑定自己的IP地址和端口号
	//3.设置监听是否有人连接
	//创建并打开套接字
	int sockFd = socket(AF_INET,SOCK_STREAM,0);
	if(sockFd < 0)
	{
		perror("socket error");
		return -1;
	}
	//绑定服务器地址信息
	struct sockaddr_in servAddr = {0};
	servAddr.sin_family = AF_INET;
	servAddr.sin_port = htons(port);
	servAddr.sin_addr.s_addr = inet_addr(ip);
	int ret = bind(sockFd,(struct sockaddr *)&servAddr,sizeof(servAddr));
	if(ret < 0)
	{
		perror("bind error");
		return -1;
	}
	printf("bind OK\n");
	//创建监听队列
	ret = listen(sockFd,5);
	if(ret < 0)
	{
		perror("listen error");
		close(sockFd);
		return -1;
	}
	printf("listen OK\n");
	return sockFd;
}

//编写一子函数,实现客户端和服务器端的通信
//参数:连接好的socket对象
//返回值:通信结束返回-1,继续返回0
int tcp_com(int sockFd);
int tcp_com(int sockFd)
{
	char buf[N]={'\0'};
	char cmd[N]={'\0'};
	char filename[N]={'\0'};
	//接受客户端发过来的消息
	recv(sockFd,buf,sizeof(buf),0); //第一次接受"hello"
	printf("buf=%s\n",buf);
	if(strncmp(buf,"quit",4)==0)
	{
		printf("通信结束");
		return -1;

	}
	parseStr(buf,cmd,filename);	
	printf("cmd=%s\n",cmd);
	printf("filename=%s\n",filename);

	if(strncmp(cmd,"put",3)==0)
	{
		//接受文件(下载文件 download)
		download(sockFd,filename);
	}else if(strncmp(cmd,"get",3)==0)
	{
		//发送文件(上传文件 upload)
	}else
	{
		printf("指令有误\n");
	}

	return 0;
}

void handler(int sig)
{
	printf("子进程发生改变\n");
	while(waitpid(-1,NULL,WNOHANG)>0);
}

int main(int argc,char * argv[])
{
	if(argc!=3)
	{
		printf("input appname ip port\n");
		return -1;
	}
	signal(SIGCHLD,handler); //安装信号
	//1.监听socket对象

	int tcp_socket=tcp_server(argv[1],atoi(argv[2]));
	//2.接受连接
	struct sockaddr_in client;
	int len=sizeof(client);
	pid_t pid=0;
	while(1)
	{
		int newfd=accept(tcp_socket,(struct sockaddr *)&client,&len);
		if(newfd<0)
		{
			perror("accept error");
			return -1;
		}
		printf("accept ok\n");
		printf("client ip=%s  port=%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
		//创建子进程和客户端进行通信
		pid=fork();
		if(pid<0)
		{
			perror("fork error");
			return -1;
		}else if(pid==0) //子进程
		{
			while(1)  
			{
				//3.接受/发送消息
				int n=tcp_com(newfd);
				//客户端和服务器端进行通信
				if(n<0){
					//4.关闭套接字
					close(newfd);
					break;
				}
			}
			exit(0);//子进程提出
		}
		close(newfd);
	}
	close(tcp_socket);
}

2.多线程----流程 :

socket(...)
bind(...)
listen(...)
while(1)
{
    accept(...)
    if(pthread_create(...)== 0)
    {
        process(...);
        close(...);
        exit(...);
    }
    close(...);
}

代码实现:

头文件:

#ifndef _NET_H
#define _NET_H
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#define N 20
int parseStr(char *buf,char * cmd,char * filename);
int download(int sockfd,char * filename);
#endif

 TCP多线程服务器端:

#include <stdio.h>
#include "net.h"
#include <string.h>
//编写一子函数,监听socket对象
//参数1:IP地址  char * ip
//参数2:端口号  int port
//返回值:失败返回-1,成功返回socket对象
int tcp_server(char * ip,int port);
int tcp_server(char * ip,int port)
{

	//1.创建Tcp套接字
	//2.绑定自己的IP地址和端口号
	//3.设置监听是否有人连接
	//创建并打开套接字
	int sockFd = socket(AF_INET,SOCK_STREAM,0);
	if(sockFd < 0)
	{
		perror("socket error");
		return -1;
	}
	//绑定服务器地址信息
	struct sockaddr_in servAddr = {0};
	servAddr.sin_family = AF_INET;
	servAddr.sin_port = htons(port);
	servAddr.sin_addr.s_addr = inet_addr(ip);
	int ret = bind(sockFd,(struct sockaddr *)&servAddr,sizeof(servAddr));
	if(ret < 0)
	{
		perror("bind error");
		return -1;
	}
	printf("bind OK\n");
	//创建监听队列
	ret = listen(sockFd,5);
	if(ret < 0)
	{
		perror("listen error");
		close(sockFd);
		return -1;
	}
	printf("listen OK\n");
	return sockFd;
}

//编写一子函数,实现客户端和服务器端的通信
//参数:连接好的socket对象
//返回值:通信结束返回-1,继续返回0
int tcp_com(int sockFd);
int tcp_com(int sockFd)
{
	char buf[N]={'\0'};
	char cmd[N]={'\0'};
	char filename[N]={'\0'};
	//接受客户端发过来的消息
	recv(sockFd,buf,sizeof(buf),0); //第一次接受"hello"
	printf("buf=%s\n",buf);
	if(strncmp(buf,"quit",4)==0)
	{
		printf("通信结束");
		return -1;

	}
	parseStr(buf,cmd,filename);	
	printf("cmd=%s\n",cmd);
	printf("filename=%s\n",filename);

	if(strncmp(cmd,"put",3)==0)
	{
		//接受文件(下载文件 download)
		download(sockFd,filename);
	}else if(strncmp(cmd,"get",3)==0)
	{
		//发送文件(上传文件 upload)
	}else
	{
		printf("指令有误\n");
	}

	return 0;
}


//创建子线程
void * thread(void *arg)
{
	int * pfd=(int *)arg; //处理主线程给子线程传过来的值
	int newfd=*pfd;
	//参数:需要连接好的socket对象
		while(1)
		{
			//3.接受/发送消息
			int n=tcp_com(newfd);

			//客户端和服务器端进行通信
			if(n<0){
				//4.关闭套接字
				close(newfd);
				pthread_exit(0);//线程的退出
				break;
			}
		}

}
int main(int argc,char * argv[])
{
	if(argc!=3)
	{
		printf("input appname ip port\n");
		return -1;
	}
	//1.监听socket对象

	int tcp_socket=tcp_server(argv[1],atoi(argv[2]));
	//2.接受连接
	struct sockaddr_in client;
	int len=sizeof(client);
	
	pthread_t pid;
	while(1)
	{
		int newfd=accept(tcp_socket,(struct sockaddr *)&client,&len);
		if(newfd<0)
		{
			perror("accept error");
			return -1;
		}
		printf("accept ok\n");
		printf("client ip=%s  port=%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
		//创建子线程实现和客户端的通信
		//第四个参数:主线程给子线程传值
		pthread_create(&pid,NULL,thread,&newfd);

	}
	close(tcp_socket);
}

 对应客户端:

头文件:

#ifndef _NET_H
#define _NET_H
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define N 20
int parseStr(char *buf,char * cmd,char * filename);
int upload(int sockfd,char * filename);
#endif

#include <stdio.h>
#include "net.h"
#include <string.h>
//编写一子函数,连接服务器
//参数1:IP地址ecv(sockFd,buf,sizeof(buf),0);
//参数2:端口号
//返回值:连接成功返回连接好的socket对象,失败返回-1
int tcp_connect(char * ip,int port);
int tcp_connect(char * ip,int port)
{
	//1.创建Tcp套接字
	//2.设置对方的IP地址和端口号
	//3.请求连接


	//创建并打开套接字
	int sockFd = socket(AF_INET,SOCK_STREAM,0);
	if(sockFd < 0)
	{
		perror("socket error");
		return -1;
	}
	printf("socket OK\n");
	//连接服务器
	struct sockaddr_in servAddr = {0};
	servAddr.sin_family = AF_INET;
	servAddr.sin_port = htons(port);
	servAddr.sin_addr.s_addr = inet_addr(ip);
	int ret = connect(sockFd,(struct sockaddr *)&servAddr,sizeof(servAddr));
	if(ret < 0)
	{
		close(sockFd);
		return -1;
	}
	printf("connect OK\n");
	return  sockFd;
}


//编写一子函数,实现客户端和服务端的通信
//参数:连接好的socket对象
//返回值:通信结束返回-1,继续返回0
int tcp_com(int sockFd);
int tcp_com(int sockFd)
{
	printf("请输入指令");
	char buf[N]={'\0'};
	char cmd[N]={'\0'};
	char filename[N]={'\0'};

	fgets(buf,sizeof(buf),stdin);
	send(sockFd,buf,strlen(buf),0);
	parseStr(buf,cmd,filename);
	printf("cmd==%s\n",cmd);
	printf("filename=%s\n",filename);

	if(strncmp(cmd,"put",3)==0)
	{
		//上传文件upload()
		upload(sockFd,filename);

	}else if(strncmp(cmd,"get",3)==0)
	{
		//下载文件download()
	}else if(strncmp(cmd,"quit",4)==0)
	{
		printf("通信结束\n");
		return -1;
	}else 
	{
		printf("指令有误\n");
	}



	return 0;
}

int main(int argc,char * argv[])
{
	if(argc!=3)
	{
		printf("input appname ip  port\n");
		return -1;
	}
	//1.请求连接服务器  //atoi()将字符串转换成整数
	//		    atof()将字符串转换成float
	int tcp_socket=tcp_connect(argv[1],atoi(argv[2]));
	//2.发送/接受消息
	while(1){
		//客户端和服务端之间的通信
		int n=tcp_com(tcp_socket);
		//3.关闭套接字
		if(n<0){
			close(tcp_socket);
			break;
		}
	}
	//
	//
}

 (注:部分段落参考自《嵌入式-应用程序设计综合教程微课版》)

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

......……_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值