其他关联文章@丶4ut15m:
TCP网络编程(c)
UDP网络编程(c)
多线程并发服务器(c)
IO复用(c)
多进程并发服务器整个流程和单进程差别不太大,主要区别在交互部分.
服务器
->创建套接字
->绑定地址结构
->监听套接字
->等待并接受连接请求
->主进程创建子进程后执行上一步,子进程与客户端进行交互
->关闭套接字
父进程和子进程都会执行fork(创建子进程)后面的代码,为了区别父进程和子进程通过使用if语句和pid来控制想要父进程和子进程分别执行的代码.
详细内容见我的作业三和代码
下面给个实例
服务器:
服务器等待接收客户的连接请求,连接成功则显示客户地址;
接收客户端的名称并显示;
然后接收来自该客户的字符串,对接收的字符串按分组进行加密(分组长度为个人学号,密钥为个人序号,分组不够补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<(10-strlen(data)%10);i++){
miwen[strlen(data)+i] += 48;
miwen[strlen(data)+i] += snum[j];
j++;
if(j %10 == 0){
//如果j%10等于0,就说明已经加密完一轮,该让密钥回到第一个值的位置了
j = 0;
}
}
}
}
客户端代码如下
#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);
}