Linux的一个应用优势是可用于设计各种高性能网络服务程序,高性能的一个特点就是实现并发访问处理,及同时为多个在线用户提供服务;多进程网络服务、多线程网络服务、线程池网络服务;
利用Linux系统中的父子进程关系为多用户提供并发服务,是一种比较流行的并发服务技术,其基本理念是:来一个用户,启动一个服务进程。若有新连接到来,则启动子进程与其交互,服务结束后,其子进程自动退出。
模型如下:
用一个整数的运算模拟多进程的网络服务。
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>
#define SERVER_PORT 9090
#define SERVER_IP "127.0.0.1"
#define LISTEN_QUEUE 5
#define BUFFER_SIZE 255
typedef enum{ADD,SUB,MUL,DIV,MOD, QUIT}OPER_TYPE;
typedef struct OperStruct{
int op1;
int op2;
OPER_TYPE oper;
}OperStruct;
#include"../utili.h"
void Process_Handler(int sockConn);
void Process_Handler(int sockConn){
OperStruct op;
int result;
while(1){
int res = recv(sockConn, &op, sizeof(op), 0);
if(res == -1){
printf("recv data fail.\n");
continue;
}
if(op.oper == ADD){
result = op.op1 + op.op2;
}else if(op.oper == SUB)
{
result = op.op1 - op.op2;
}else if(op.oper == MUL){
result = op.op1 * op.op2;
}else if(op.oper == DIV){
result = op.op1 / op.op2;
}else if(op.oper == QUIT){
break;
}
res = send(sockConn, &result, sizeof(result), 0);
if(res == -1){
printf("send data fail.\n");
continue;
}
}
close(sockConn);
}
int main(void){
int sockSer = socket(AF_INET, SOCK_STREAM, 0);
if(sockSer == -1){
perror("socket");
return -1;
}
struct sockaddr_in addrSer, addrCli;
addrSer.sin_family = AF_INET;
addrSer.sin_port = htons(SERVER_PORT);
addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);
socklen_t len = sizeof(struct sockaddr);
int res = bind(sockSer, (struct sockaddr*)&addrSer, len);
if(res == -1){
perror("bind");
close(sockSer);
return -1;
}
listen(sockSer, LISTEN_QUEUE);
int sockConn;
while(1){
printf("Server Wait Client Connect.......\n");
sockConn = accept(sockSer, (struct sockaddr*)&addrCli, &len);
if(sockConn == -1){
printf("Server Accept Client Connect Fail.\n");
continue;
}else{
printf("Server Accept Client Connect Success.\n");
printf("Client IP:>%s\n", inet_ntoa(addrCli.sin_addr));
printf("Client Port:>%d\n",ntohs(addrCli.sin_port));
}
pid_t pid;
pid = fork();
if(pid == 0){
close(sockSer);
Process_Handler(sockConn);
exit(0);
}else if(pid > 0){
close(sockConn);
continue;
}else{
printf("Create Process Fail.\n");
continue;
}
}
close(sockSer);
return 0;
}
#include"utili.h"
void InputData(OperStruct *pt);
void InputData(OperStruct *pt){
printf("please input op1 and op2 : ");
scanf("%d %d", &(pt->op1), &(pt->op2));
}
//Cli
int main(void){
int sockCli = socket(AF_INET, SOCK_STREAM, 0);
if(sockCli == -1){
perror("socket");
return -1;
}
struct sockaddr_in addrSer;
addrSer.sin_family = AF_INET;
addrSer.sin_port = htons(SERVER_PORT);
addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);
socklen_t len = sizeof(struct sockaddr);
int res = connect(sockCli, (struct sockaddr*)&addrSer, len);
if(res == -1){
perror("connect");
close(sockCli);
return -1;
}else{
printf("Client Connect Server Success.\n");
}
char cmd[2];
OperStruct op;
int result;
while(1){
printf("Please input operator : ");
scanf("%s",cmd);
if(strcmp(cmd, "+") == 0){
op.oper = ADD;
InputData(&op);
}else if(strcmp(cmd,"-") == 0){
op.oper = SUB;
InputData(&op);
}else if(strcmp(cmd,"*") == 0){
op.oper = MUL;
InputData(&op);
}else if(strcmp(cmd,"/") == 0){
op.oper = DIV;
InputData(&op);
}else if(strcmp(cmd, "quit") == 0){
op.oper = QUIT;
}else{
printf("Cmd invalid.\n");
}
res = send(sockCli, &op, sizeof(op), 0);
if(res == -1){
printf("send data fail.\n");
continue;
}
if(op.oper == QUIT)
break;
res = recv(sockCli, &result, sizeof(result), 0);
if(res == -1){
printf("recv data fail.\n");
continue;
}
printf("result = %d\n", result);
}
close(sockCli);
return 0;
}
服务器端:
客户1
客户2
- utili.h在ser.c的上一层目录,utili.h和cli.c是在同一层目录;
- 进程服务器:socker是引用计数器模型,close()是减少一个,并没有真正的关闭,每次创建一个进程都会给socker引用计数器加1;
- 缺点:
- 启动和关闭子进程带来很大的开销;
- 系统最多只能产生512个进程,也就是说最多只有512个客户,形成不了处理大型访问的情形;