提示:在前几篇文章的基础上,多进程编程终于来辣,被一些小事情和小错误耽误了不少的时间,属实是有点拖拉辣,🆗废话就到这,开始进入本篇正文
前言
这里主要写的是多进程的内容,如果有细节不理解,可以去看看前几篇文章或者针对不理解的地方再去查阅资料,或者与我私信也可以。
提示:以下是本篇文章正文内容,下面案例可供参考
一、多进程处理流程
附上一张流程图作一下简单的说明:
大致上看起来和迭代服务器(网络socket通信,前文有讲)的处理流程是很相似的,而它的不同之处在于,网络socket通信是属于“单”进程通讯,而真正的多进程通信是,一个服务器端,可以接收多个客户端的连接访问。
二、服务器端的示例代码
代码如下(示例):
#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>
#include <ctype.h>
void print_usage(char *program)
{
printf("%s usage: \n",program);
printf("-p(--port): sepcify server listen port.\n");
printf("-h(--Help): print this help information.\n");
return ;
}
int main(int argc,char **argv)
{
int sockfd = -1;
int rv = -1;
struct sockaddr_in servaddr;
struct sockaddr_in cliaddr;
socklen_t len;
int port = 0;
int clifd;
int ch;
int on = 1;
pid_t pid;
struct option opts[] = {
{"port",required_argument,NULL,'p'},
{"help",no_argument,NULL,'h'},
{NULL,0,NULL,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]);
printf("port is what: %d\n",port);
printf("process is excuting now!!!!!!!!!!!!1");
return 0;
}
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0)
{
printf("create socket failure: %s\n",strerror(errno));
return -1;
}
printf("create socket[%d] successfully!\n",sockfd);
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
//listen all the IP address on this host
//inet_aton("192.168.0.16",&servaddr.sin_addr);
//only listen specify IP address on this host
rv = bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
if( rv < 0 )
{
printf("Socket[%d] bind on port[%d] failureL %s\n",sockfd,port,strerror(errno));
return -2;
}
listen(sockfd,13);
printf("start to listen on port[%d]\n",port);
while(1)
{
printf("start accept new client incoming!!!\n");
clifd = accept(sockfd,(struct sockaddr *)&cliaddr,&len);
if(clifd < 0)
{
printf("accept new client failure: %s\n",strerror(errno));
continue;
}
printf("accept new client{%s:%d} succeddfully\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
pid = fork();
if(pid < 0)
{
printf("fork() create child process failure: %s\n",strerror(errno));
close(clifd);
continue;
}
else if( pid > 0 )
{
//parent process close client fd goes to accept new socket client again
close(clifd);
continue;
}
else if( 0 == pid )
{
char buf[1024];
int i;
printf("child process start to commuicate with socket client!!!\n");
close(sockfd);
//child process close the listen socket fd
while(1)
{
memset(buf,0,sizeof(buf));
rv = read(clifd,buf,sizeof(buf));
if(rv < 0)
{
printf("read data from sockfd[%d] failure: %s\n",clifd,strerror(errno));
close(clifd);
exit(0);
}
else if(rv == 0)
{
printf("socket[%d] get disconnet\n",clifd);
close(clifd);
exit(0);
}
else if(rv > 0)
{
printf("read %d bytes data from server: %s\n",rv,buf);
}
//convert letter from lowercase to uppercase
for(i = 0; i < rv; i++)
{
buf[i] = toupper(buf[i]);
}
rv = write(clifd,buf,rv);
if(rv < 0)
{
printf("write to client by sockfd[%d] failure: %s\n",clifd,strerror(errno));
exit(0);
}
}//child process loopp
}//child process strat
}
close(sockfd);
return 0;
三、客户端测试
在该处我使用了端口测试工具Tcp Test Tool,需要的可以和我私信。在服务器端运行的时候附上要监听的端口号。
在使用tcp test tool的时候,需要关闭防火墙,才能连接上,该程序可以开多个来模拟有多个客户端的效果,在本次测试中只需输入你的服务器端的ip地址和端口号就能在下面的框中输入信息并发送了,服务器端也会有相应的信息。如图:
四、代码中涉及到的一些函数
结构体option 的原型定义
代码如下(示例):
#define no_argument 0 //选项没有参数
#define required_argument 1 //选项需要参数
#define optional_argument 2 //选项参数可选
struct option {
const char *name; //不带短横线的选项名,前面没有短横线,如:help
int has_arg; //描述了选项是否有选项参数,其值可以是以上三个宏的任意一个值
int *flag; //用来决定,getopt_long()的返回值使什么
int val;
};
其中的*flag:
①指针为NULL:那么getopt_long()返回该结构体val字段中的数值;
②指针不为NULL: getopt_long()会使得它所指向的变量中填入val字段中的数值,并且getopt_long()返回0。
③如果flag不是NULL,但未发现长选项,那么它所指向的变量的数值不变。
setsockopt()函数
用于任意类型、任意状态套接字的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接字”层次上的选项。
函数原型:
Int setsockopt(int sockfd,int level, init optname,const void *optval, socklen_t optlen);
各参数的含义:
Sockfd:标识一个套接口的描述字;
Level:选项定义的层次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6;
Optname:需设置的选项;
Optval:指针,指向存放选项待设置的新值的缓冲区。
Optlen:optval缓冲区的长度
总结
本文先到这