C 高性能大并发TCP服务器,TCP多进程并发服务器(c)

其他关联文章@丶4ut15m:

TCP网络编程(c)

UDP网络编程(c)

多线程并发服务器(c)

IO复用(c)

多进程并发服务器

多进程并发服务器整个流程和单进程差别不太大,主要区别在交互部分.

服务器

->创建套接字

->绑定地址结构

->监听套接字

->等待并接受连接请求

->主进程创建子进程后执行上一步,子进程与客户端进行交互

->关闭套接字

父进程和子进程都会执行fork(创建子进程)后面的代码,为了区别父进程和子进程通过使用if语句和pid来控制想要父进程和子进程分别执行的代码.

详细内容见我的作业三和代码

95bbd0b4bb6ac4c0d349dc6a78655012.png

下面给个实例

服务器:

服务器等待接收客户的连接请求,连接成功则显示客户地址;

接收客户端的名称并显示;

然后接收来自该客户的字符串,对接收的字符串按分组进行加密(分组长度为个人学号,密钥为个人序号,分组不够补0);

将加密后的字符串发送给客户端;

继续等待接收该客户的信息,直到客户关闭连接;

要求服务器具有同时处理多个客户请求的能力。

客户端:

客户首先与相应的服务器建立连接;

接着接收用户输入的客户端名称,并将其发送给服务器;

接收用户输入的字符串并发送给服务器;

接收服务器发回的加密后的字符串并显示。

继续等待用户输入字符串;

若用户输入的是quit,则关闭连接并退出。

使用多进程实现

服务器代码如下

#include //基本输入输出

#include //exit函数所在头文件

#include //套接字函数所在头文件

#include //memset函数所在头文件

#include //inet_ntoa函数所在头文件

#include //close等函数所在头文件

#define PORT 3333

char miwen[100];//定义全局变量,用以存放密文

int response(int connectfd,struct sockaddr_in client);

void encode(char data[],int snum[]);

int main(){

int listenfd,connectfd;//定义监听套接字和已连接套接字

int miyao[] = {2,0,1,7,1,2,2,1,1,9};

char data[100];

pid_t chld_pid;//存放子进程pid

struct sockaddr_in server,client;//定义地址结构

socklen_t clientlen;

if((listenfd = socket(AF_INET,SOCK_STREAM,0)) == -1){

//创建套接字

perror("Sockfd() error!\n");

exit(0);

}

//地址重用

int opt = SO_REUSEADDR;

setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

//bind操作前的预处理

memset(&server,'\0',sizeof(server));

server.sin_family = AF_INET;//配置协议族为IPv4

server.sin_port = htons(PORT);//配置端口

server.sin_addr.s_addr = htonl(INADDR_ANY);//设置监听地址

if(bind(listenfd,(struct sockaddr *)&server,sizeof(server)) == -1){

//绑定套接字与地址结构

perror("Bind error!\n");

exit(0);

}

if(listen(listenfd,5) == -1){

//监听,并且设置最大连接数为5

perror("Listen error!\n");

exit(0);

}

while(1){

//while循环接收连接

clientlen = sizeof(client);

if((connectfd = accept(listenfd,(struct sockaddr *)&client,&clientlen)) == -1){

perror("Connect error!\n");

exit(0);

}

printf("Got a connect from %s:%d\n",inet_ntoa(client.sin_addr),htons(client.sin_port));

//调用响应函数

response(connectfd,client);//接收用户输入的名字并打印

/*

*fork会分别给父进程和子进程返回一个值

*对父进程会返回子进程的pid(一个大于0的整数)

*对子进程会返回0

*所以可以通过if判断pid的值来分辨子进程与父进程

*/

chld_pid = fork();//创建子进程

if(chld_pid == 0){

//说明是子进程

close(listenfd);//子进程不需要监听套接字,直接关闭防止后方主进程关闭套接字失败

while(1){

recv(connectfd,data,100,0);

if(!strcmp(data,"quit")){

break;

}

memset(&miwen,'\0',sizeof(miwen));

encode(data,miyao);

send(connectfd,miwen,strlen(miwen),0);

}

close(connectfd);

exit(0);//处理完事件之后关闭子进程,记得上方注释所说的关闭已连接套接字connectfd

}

else if(chld_pid > 0){

//说明是父进程

close(connectfd);//父进程不需要对客户端的请求做事情,直接关闭已连接套接字

continue;//主进程执行下一次循环,即等待新的连接

}

else {

perror("Fork error!\n");

exit(0);

}

}

close(listenfd);

return 0;

}

//定义响应函数用以打印客户端名称

int response(int connectfd,struct sockaddr_in client){

int num;

char client_name[20];

memset(&client_name,'\0',sizeof(client_name));

if((num = recv(connectfd,client_name,20,0) == -1)){

//接收客户端名字

perror("Recv error!\n");

exit(0);

}

client_name[num-1] ='\0';//将最后一个字符置为空字符,用以作为字符串结尾

printf("Client name is %s\n",client_name);

return 0;

}

//根据学号进行字符串加密

void encode(char data[],int snum[]){

int i,j;

j = 0;

//密钥长度为学号长度,能整除该长度就说明不需要在字符串后面补零可以直接加密

for(i = 0; i= 'a' && data[i] <='z') || (data[i] >= 'A' && data[i] <='Z')){

//字符加密与数字加密需要分开,因为字符移位可能会越过字母的区域,需要另做操作

if(((data[i]+snum[j]) > 'z') && (data[i] >= 'a') && (data[i] <= 'z')){

//越界情况一

miwen[i] = data[i] + snum[j]-26;

}

else if(((data[i]+snum[j])>'Z') && (data[i] >= 'A') && (data[i] <= 'Z')){

//越界情况二

miwen[i] = data[i] + snum[j] -26;

}

else{

//这是正常情况执行的代码

miwen[i] = data[i] + snum[j];

}

//每次加密结束,j增加移至下一个密钥

j++;

if(j %10 == 0){

//如果j%10等于0,就说明已经加密完一轮,该让密钥回到第一个值的位置了

j = 0;

}

}

}

if(strlen(data) % 10 != 0){

//如果不能整除则需要填补。

for(i=0;i

客户端代码如下

#include //基本输入输出

#include //sockaddr_in结构体所在头文件

#include //socket函数所在头文件

#include //hostent结构体所在头文件

#include //memset等函数所在头文件

#include //close等函数所在头文件

#include //exit等函数所在头文件

#define PORT 3333

int main(int argc,char *args[]){

int sockfd,num;//定义套接字描述符

struct sockaddr_in server;

struct hostent *he;

socklen_t server_len;

char data[100];

if(argc != 2){

printf("Usage:%s \n",args[0]);

exit(0);

}

//创建套接字

if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){

perror("Socket error!\n");

exit(0);

}

//获取服务器信息

if((he = gethostbyname(args[1])) == NULL){

perror("Gethostbyname error!\n");

exit(0);

}

memset(&server,'\0',sizeof(server));

//配置服务器地址结构

server.sin_family = AF_INET;//配置协议族为IPv4

server.sin_port = htons(PORT);//配置服务器端口

server.sin_addr = *((struct in_addr *)he->h_addr);//服务器地址

server_len = sizeof(server);

//而后可以进行连接

if((connect(sockfd,(struct sockaddr *)&server,sizeof(server))) ==-1){

perror("Connect error!\n");

exit(0);

}

printf("Plz enter your name:");

memset(&data,'\0',sizeof(data));//每次输入信息前先清空缓存,防止前次数据影响后面输入的数据

scanf("%s",&data);

if(send(sockfd,data,strlen(data),0) ==-1){

//send函数原型send(int sockfd,const char FAR *datafer,int dataferlen,int flags)

//第二个参数为要发送的数据,第三个参数为发送的大小也即是长度,第四个参数通常置0代表其功能与write相同

printf("Send error!\n");

exit(1);

}

//连接之后便可开始数据的发送与接收

while(1){

memset(&data,'\0',sizeof(data));

printf("Now,you can enter something what you want to encode.\n");

scanf("%s",&data);

send(sockfd,data,sizeof(data),0);

if(!strcmp(data,"quit")){

break;

}

else{

memset(&data,'\0',sizeof(data));

if((recv(sockfd,data,100,0) ==-1)){

//recv函数原型recv(int sockfd,char *datafer,int dataferlen,int flags)

//第二个参数为将接收的数据暂存的位置,第三个参数为接收的数据大小,第四个参数通常置0代表其功能与read相同

printf("Recv error!\n");

exit(1);

}

printf("The code message:%s\n",data);

}

}

printf("Bye~\n");

close(sockfd);

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值