Linux&Apue(0.2.1):多进程并发服务器的编程

(一)多进程并发服务器基础知识

(1) 多进程并发服务器流程图

在这里插入图片描述

(2)涉及函数

2.1 getopt_long()函数

getopt_long()函数:长选项的命令行解析。
目的:就是可以指定任意IP,PORT,避免经常在文件中修改。

#include <getopt.h>		//头文件包含
int getopt_long(int argc, char * const argv[],const char *optstring, const struct option *longopts,int *longindex);

参数:
argc和argv[]:用来接收main函数的参数。
optstring:一个字符串,表示可以接受的参数。例如,“a:b:cd”,表示可以接受的参数是a,b,c,d,其中,a和b参数后面跟有更多的参数值。(例如:-a host -b name)。
longopts:一个结构的实例。
longindex:表示当前长参数在longopts中的索引值。
示例

getopt_long(argc,argv,"p:h",opts,NULL)

2.1.1 option结构体

struct option {
const char *name; 
int has_arg;
int *flag;
int val;
}

参数:
name:表示的是长参数名
has_arg(3个值):
①no_argument(或者是0),表示该参数后面不跟参数值
②required_argument(或者是1),表示该参数后面一定要跟个参数值
③optional_argument(或者是2),表示该参数后面可以跟,也可以不跟参数值
flag:用来决定,getopt_long()的返回值到底是什么。如果flag是null(通常情况),则函数会返回与该项option匹配的val值;如果flag不是NULL,则将val值赋予flag所指向的内存,并且返回值设置为0。
val:与flag联合决定返回值
示例

struct option	opts[]={
		{"port",required_argument,NULL,'p'},
		{"help",no_argument,NULL,'h'},
		{0,0,0,0}
	};

2.2 setsockopt()函数

setsockopt()函数::用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。

#include <sys/types.h>		//包含的头文件
#include <sys/socket.h>		//包含的头文件
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
参数功能
sockfd指定套接字的描述符
level选项定义的层次。例如:SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6等
optname设置的选项名
optval指针,指向存放选项待设置的新值的缓冲区(布尔型/整型或结构选项)
optlenoptval缓冲区长度

setsockopt有多种用法,这里我只讲与下面编程有关的用法。
示例:

int		on=1;
setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));

目的:当我们运行了程序,并退出后,端口并不会立即释放(会经理TIME_WAIT过程),此时使用setsockopt()则可以实现端口的重用。这样就不会出现经常看到的Address already in use现象了。
setsockopt设置sockfd的用法可以参考下面的博客:
https://blog.csdn.net/godleading/article/de8102814tails/

(二)多进程并发服务器的编程

(1) 用getopt_long实现长选项命令行解析

#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<getopt.h>

#define	MSG_STR	"Hello socket process world \n"
//help printf
void print_usage(char  *progname)
{
	printf("%s usage:\n ",progname);
	printf("-p(--port):sepcify server listen port.\n");
	printf("-h(--help):print this help information.\n");
	return ;
}
int main(int argc,char **argv)
{
	int		sock_fd=-1;
	int		cli_fd=-1;
	int		rv=-1;
	struct		sockaddr_in	servaddr;
	struct		sockaddr_in	cliaddr;
	socklen_t	len;
	pid_t		pid;
	int		ch;
	int		on=1;
	int		port=0;

	//write a option to give help
	struct option	opts[]={
		{"port",required_argument,NULL,'p'},
		{"help",no_argument,NULL,'h'},
		{0,0,0,0}
	};
	
	while((ch=getopt_long(argc,argv,"p:h",opts,NULL))!=-1)  
	{
		switch(ch)
		{
			case'p':
				port=atoi(optarg);
				break;       //不能少,要跳出循环
			case'h':
				print_usage(argv[0]);
				return 0;
		}
	}
	if(!port)
	{
		print_usage(argv[0]);
		return 0;
	}

简析:这里简单来说就是做了提供帮助,指定端口的功能。

(2) server创建的四步骤

	//1.socket 
	sock_fd=socket(AF_INET,SOCK_STREAM,0);
	if(socket<0)
	{
		printf("Crate socket failure:%s\n",strerror(errno));
		return -1;
	}
	else
	{
		printf("Create socket[%d],successfully!\n",sock_fd);
	}
	
	setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));//
	
	//2.bind
	memset(&servaddr,0,sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_port=htons(port);
	servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

	if(rv=bind(sock_fd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0);
	{
		printf("Socket[%d] bind on port [%d] failure:%s\n",sock_fd,port,strerror(errno));
		return -2;
	}
	//3.listen
	listen(sock_fd,13);	//队列
	printf("Start to listen on port [%d]\n",port);

	while(1)
	{
		printf("Start accept new client incoming..\n");
		//4.accept
		cli_fd=accept(sock_fd,(struct sockaddr *)&cliaddr,&len);
		if(cli_fd<0)
		{
			printf("Accept new client failure:%s\n",strerror(errno));
			return -3;
		}
		else
		{
			printf("Accept new client [%s:%d] suceesssfuly\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
		}

(3) 当接收客服端连接后创建子进程并进行操作

//子进程:fork()  
		pid=fork();
		if(pid<0)	//创建子进程失败
		{
			printf("fork() crate child process failure :%s\n",strerror(errno));
			close(cli_fd);
			continue;
		}
		else if(pid>0)		//返回给父进程
		{
			close(cli_fd);
			continue;
		}
		else if(0==pid)		//创建子进程中,并且进行I/O操作读取客户端cli_fd
		{
			char	buf[1024];

			close(sock_fd);   //

			printf("Child process start to commuicate with socket client..\n");

			//IO操作读写操作
			memset(buf,0,sizeof(buf));
			rv=read(cli_fd,buf,sizeof(buf));
			if(rv<0)
			{
				printf("read data from client sockfd[%d] failure:%s\n",cli_fd,strerror(errno));
				close(cli_fd);
				exit(0);		//退出
			}
			else if(rv==0)
			{
				printf("Socket[%d] get disconnected\n",cli_fd);
				close(cli_fd);
				exit(0);
			}
			else if(rv>0)
			{
				printf("read %d bytes data from server:%s\n",rv,buf);
			}
				
			rv=write(cli_fd,MSG_STR,strlen(MSG_STR));
			if(rv<0)
			{
				printf("write to client by sockfd[%d] failure:%d\n",sock_fd,strerror(errno));
				close(cli_fd);
				exit(0);
			}

			sleep(1);    //一般不这样处理

			printf("close client socket[%d] and child process exit\n",cli_fd);
			close(cli_fd);
			exit(0);
		}
	}
	close(sock_fd);
	return 0;
}

(4) 没有客户端连接运行的结果

./socket_process_server -p 22

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值