网络编程5——多进程并发服务器分析与实现/ 多线程并发服务器分析(实现在下篇)

  • 使用多进程并发服务器要考虑一下几点:
    1,父进程最大文件描述符个数(父进程中需要关闭(close)accept返回的新文件描述符
    2,系统内创建进程个数(与内存大小相关)
    3,进程创建过多是否降低整体服务性能(进程调度)

一、多进程并发服务器思路分析

1,socket()不调用系统的socket()函数了,去调用自己封装的Socket()函数 dao
1,Socket() :创建监听套接字lfd
2,Bind():绑定地址结构 Strucr sockaddr_in addr;
3,Listen(): 设置同时监听上限
4,Accept():每Accept一次就会返回一个新的套接字,并且有一个新进程(子进程)与之对应,所以如果一个服务器支持与多个客户端进行连接的话,就应用loop实现
while(1)
{
cfd = Accept();//cfd接收客户端连接请求,lfd用于监听
pid = fork();//当有一个Accept返回了,就可以创建子进程(是用于与客户端连接的子进程)
if(pid == 0)//如果pid是0就是子进程,子进程就做相应工作,这边就是读内容转换成大写字符再写回客户端
{
close(lfd);//子进程只需要cfd,所以关闭用于建立连接的套接字lfd
read();
toUpper();
write();
}else if(pid>0)//pid>0是父进程
{
close(cfd);//只需要lfd用于监听,关闭用于与客户端通信的套接字cfd
//父进程是lfd,就用持续监听是否有新的客户来连接,有就产生新的套接字用于连接进行通信,即父进程就是调用Accept()函数,因此直接跳回到Accept(),continue就ok
continue;
}
}
重要:☆父进程还要回收子进程☆ 信号捕捉函数
5,

  • 子进程:
    close(lfd)
    read()
    toUpper()
    write()
  • 父进程:
    close(cfd);
    注册信号捕捉函数:SIGCHLD
    在回调函数中,完成子进程回收
    while(waitpid());

二、多线程并发服务器思路分析

和多进程的区别:创建子进程fork()函数,创建子线程pthread_create()函数
前面都一样:
1,Socket() :
2,Bind():
3,Listen():
4,while(1)
{
cfd = Accpet(lfd, , );//cfd接收客户端连接请求,
pthread_create(&tid ,NULL ,tfn ,NULL );//启动子线程
pthread_detach(tid);//线程不能进行回收,可以用detach分离
}
5,子线程://与子进程做一样的事
void * tfn(void *arg)
{
close(lfd);//只用来连接 线程好像是不能关的,因为线程是同一份空间
read(cfd);
toUpper();
write(cfd);
}

三、多进程并发服务器的实现

1,创建lfd 接收socket,用自己封装的Socket函数就不用去检查返回值了
2,Bind(lfd,(强转)&地址结构,长度),服务器地址结构的定义与初始化,以及要记得添加相应头文件
3,listen()
4,循环中
4.1,accept():accept创建新套接字cfd,函数参数分别是lfd,客户端地址结构,客户端地址结构长度,长度的类型是socklen_t,要记一下的//用lfd创立连接返回cfd
4.2,创建子进程fork(),定义变量pid_t类型的pid = fork(),小于0报错,大于0是父进程,pid=0是子进程,子进程就可以关闭lfd,并可以开始处理(read,toupper,write)
4.3,read()要ret接收,从buf读,write写ret个到buf中,所以要定义ret,i,buf

5, 子进程回收 复习操作系统的信号之后补上//24.49开始
5,子进程回收:用信号捕捉函数进行子进程回收
5.1,

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include"wrap.h"
#DEFINE SRV_PORT 999

void catch_child(int signum)//捕捉到就可以去回收了
{
	while(waitpid(0,NULL, WNOHANG)) > 0);//第一个参数0无差别回收所有子进程,但不循环就只能一次回收一次,所以循环多次回收
	return;
}

int main(int argc, char *argv[])
{
	int lfd,cfd;
	pid_t pid;//进程
	struct sockaddr_in srv_addr,clt_addr;//地址结构初始化
	socklen_t clt_addr_len;
	char buf[BUFSIZ];//读写需要的缓冲区
	int ret, i;
	//memset(&srv_addr,0,sizeof(srv_addr));//将地址结构清零,之后再去赋值
	bzero(&srv_addr, sizeof(srv_addr));//memset一个作用,但是写法更简单,头文件
	//初始化地址结构
	srv_addr.sin_family = AF_INET;
	srv_addr.sin_port = htons(SRV_PORT);
	srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	lfd = Socket(AF_INET, SOCK_STREAM,0);//不用检查返回值了,wrap.h中已经写好了
	Bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
	Listen(lfd, 128);
	clt_addr_len = sizeof(clt_addr);
	while(1)
	{
		cfd = Accept(lfd,(struct sockaddr*)&clt_addr, &clt_addr_len);
		pid = fork();//创建子进程
		if(pid<0)
		{
			perr_exit("fork error");
		}else if(pid == 0)//是子进程
		{
			close(lfd);
			break;//跳出在外面操作
		}else{
		//信号用来回收子进程
		struct sigaction act;
		act.sa_handler(catch_child);
		sigempthset(&act.sa_mask);
		act.sa_flags = 0; 
		ret = sigaction(SIGCHLD,&act, NULL);//注册完
		if(ret != 0)
		{
			perr_exit("sigaction error");
		}
		close(cfd);
		continue;//继续去监听其他来连接的进程
		
		}
	}
	if(pid == 0)//子进程
	{
		for(;;){
		ret = Read(cfd, buf,sizeof(buf));
		if(ret == 0)
		{
			//检测到客户端关闭了,就可以关闭退出了
			close(cfd);
			exit(1);//子进程退出
		}
		for(i = 0; i<ret; ++i)
			buf[i] = toupper(buf[i]);//小写转大写结束
		write(cfd, buf, ret);
		write(STDOUT_FILENO, buf, ret);//写到桌面上
	}
	}
	
    return 0;
}

还没有回收

  信号回收部分:
    struct sigaction act;
	act.sa_handler(catch_child);
	sigempthset(&act.sa_mask);
	act.sa_flags = 0; 
	ret = sigaction(SIGCHLD,&act, NULL);//注册


	 void catch_child(int signum)//捕捉到就可以去回收了
	{
		while(waitpid(0,NULL, WNOHANG)) > 0);
		return;
	}

多进程服务器测试ip地址调整

客户端服务端至少得ping通才能通信哇,要在一个局域网网段中??(桥接模式)

服务器程序上传外网服务器,并访问

网络设置为NAT模式
scp -r 当前目录 目标位置

你可能感兴趣的:(网络编程,网络,socket,linux)