多进程并发服务器

本实验基于Linux平台下的C语言编程实现

1.服务器端程序设计步骤:

   1)使用socket()函数创建套接字;

   2)将创建的套接字绑定到指定的地址结构;

   3)listen()函数设置套接字为侦听模式,使服务器处于打开状态;

   4)接受客户端的连接请求,建立连接;

   5)接收、应答客户端的数据请求;

   6)终止连接。

2. 客户端程序设计步骤:

   1)使用socket()函数创建套接字;

   2)调用connect()函数建立一个与TCP服务器的连接;

   3)发送数据请求,接收服务器的数据应答;

   6)终止连接。

代码:

//客户端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define MAXDATASIZE 1000
#define PORT 1234
int main(int argc,char **argv){
    int listenfd,nbytes;
    struct sockaddr_in srvaddr;
    char buf[MAXDATASIZE];
    int i=1;
    //使用socket()函数创建套接字
    listenfd=socket(AF_INET,SOCK_STREAM,0); //listenfd侦听套接字,SOCK_STREAM流式套接字
    if(listenfd==-1){
        printf("create socket failed !\n");
        exit(1);
    }
    //指定服务器地址(本地socket地址采用默认值)
    bzero(&srvaddr,sizeof(srvaddr));  //初始化
    srvaddr.sin_family=AF_INET;  //IPv4
    srvaddr.sin_port=htons(PORT); //htons:host to net long
    //将字符串形式的IP地址转换成32网络字节序的IP地址
    if(inet_aton("127.0.0.1",srvaddr.sin_addr.s_addr)==-1){
        printf("addr convert error !\n");
        exit(1);
    }
    //调用connect()函数建立一个与TCP服务器的连接
    if(connect(listenfd,(struct sockaddr *)&srvaddr,sizeof(struct sockaddr))==-1){
        printf("connect error !\n");
        exit(1);
    }
    //接收服务器的数据应答
    //recv()函数前三个参数与read()函数一致,第四个参数为0时与read()相同
    if((nbytes=recv(listenfd,buf,MAXDATASIZE,0))==-1){
        printf("read error !\n");
        exit(1);
    }
    buf[nbytes]='\0';
    printf("Server Massage: %s\n",buf);
    printf("Input your name: ");  //提示输入客户姓名,用来区分不同的客户
    scanf("%s",buf);
    if((nbytes=send(listenfd,buf,strlen(buf),0))==-1){
        perror("Send error !\n");
        exit(1);
    }
    while(i){
        printf("Input message(max char:%d):",MAXDATASIZE);//输入要发给server的消息
        scanf("%s",buf);
        if(strlen(buf)<1)
            i=0;
        if((nbytes=send(listenfd,buf,strlen(buf),0))==-1){//发送消息
            perror("Send error !\n");
            exit(1);
        }
        if((nbytes=recv(listenfd,buf,MAXDATASIZE,0))==-1){//接收server的反馈消息
            perror("read error !\n");
            exit(1);
        }
        buf[nbytes]='\0';
        printf("Server message: %s\n",buf);
        printf("\n");
    }
    //关闭listenfd
    close(listenfd);
}

 

//服务器端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#define MAXDATASIZE 1000
#define PORT 1234
#define BACKLOG 5
void process_client(int connectfd,struct sockaddr_in client);
int main(int argc,char **argv){
    int listenfd,connectfd,nbytes,sin_size;
    char buf[MAXDATASIZE];
    int opt=SO_REUSEADDR;
    pid_t pid;
    struct sockaddr_in srvaddr,clientaddr;
    
    //创建网络端点
    listenfd=socket(AF_INET,SOCK_STREAM,0);//listenfd为侦听套接字,SOCK_STREAM流式
    if(listenfd==-1){
        printf("Create socket failed !\n");
        exit(1);
    }
    //填充地址
    bzero(&srvaddr,sizeof(srvaddr)); //初始化
    srvaddr.sin_family=AF_INET;  //IPv4
    srvaddr.sin_port=htons(PORT); //htons:host to net short
    //将字符串形式的IP地址转换成32网络字节序的IP地址
    if(inet_aton("127.0.0.1",srvaddr.sin_addr.s_addr)==-1){
        printf("addr convert error !\n");
        exit(1);
    }
    //绑定服务器地址和端口
    if(bind(listenfd,(struct sockaddr *)&srvaddr,sizeof(struct sockaddr))==-1){
        printf("Bind error !\n");
        exit(1);
    }
    //监听端口
    if(listen(listenfd,BACKLOG)==-1){
        printf("listen error !\n");
        exit(1);
    }
    while(1){
        //接收客户端的连接请求,建立连接
        if((connectfd=accept(listenfd,(struct sockaddr *)&clientaddr,&sin_size))==-1){
            perror("Accept error !\n");
            exit(-1);        
        }
        //下面是多进程并发的核心所在
        //用fork()函数创建进程
        if((pid=fork())>0){ //父进程处理过程
            close(connectfd); //父进程关闭已连接描述符
            continue;  //继续接受下一个客户的请求
        }
        else if(pid==0){  //子进程处理过程
       //子进程关闭监听描述符,然后调用process_client()函数处理该客户的请求
            close(listenfd); 
            process_client(connectfd,clientaddr);
            exit(1);  //终止子进程
        }
        else{  //调用失败处理
            perror("Fork error !\n");
            exit(0);  //退出
        }
    }
    close(listenfd);
}
void process_client(int connectfd,struct sockaddr_in client){
    char recvbuf[MAXDATASIZE];
    char client_name[MAXDATASIZE];
    int recvlen,i;
    //显示客户的IP,用inet_ntoa将客户地址转换为可显示的
    printf("You get a connetion from %s\n",inet_ntoa(client.sin_addr));
    send(connectfd,"Welcome to my server.\n",22,0);
    //接收客户的名字并显示
    recvlen=recv(connectfd,client_name,MAXDATASIZE,0);
    //对是否已经结束通话进行判断
    if(recvlen==0){
        close(connectfd);
        printf("Client disconnected.\n");    
        return;
    }
    else if(recvlen<0){
        close(connectfd);
        printf("Connect broked.\n");
        return;
    }
    client_name[recvlen]='\0';
    printf("Client name is %s.\n",client_name);
    //不断重复接收客户的数据,接收成功则显示
    while(recvlen=recv(connectfd,recvbuf,MAXDATASIZE,0)){
        recvbuf[recvlen]='\0';
        printf("Receive from client<%s> message: %s\n",client_name,recvbuf);
        recvbuf[recvlen]='\0';
        send(connectfd,recvbuf,strlen(recvbuf),0);
    }
    printf("Client: %s disconnected.\n",client_name);
    //关闭connectfd
    close(connectfd);
}

运行结果如下:

①开启服务器

服务器端:

 

②客户A连接服务器

客户端A:

 

服务器端:

 

③客户B也连接服务器

客户端B:

 

服务器端:

 

④客户B发送消息

客户端B发送hello!

 

服务器端收到hello!并显示是B发来的:

 

⑤客户A发送消息

客户端A发送thanks.

 

服务器端接收到thanks.并显示是来自A的消息:

 

⑥客户端A断开连接

客户端退出:

 

服务器端显示如下:

 

⑦客户端B断开连接

客户端退出:

 

服务器端显示如下:

 

 

 

 

posted @ 2020-05-25 10:55  霜见  阅读(647)  评论(0编辑  收藏  举报