网络程序设计——4.利用多进程和多线程实现服务器端的并发处理

 

一、实验要求

    在TCP文件传输代码的基础上,利用单线程进程并发模型和多线程并发模型实现服务器端的并发处理。

二、实验分析

  •     多线程与多进程相比,使用多线程相比多进程有以下两个优点:更高的效率和共享存储器,效率的提高源于上下文切换次数的减少。
  •     但是采用多线程的方式也有缺点:一个线程的动作可能会对同一个进程内的其他线程产生影响、并且多线程缺乏健壮性。
  •     具体使用哪种服务器还要根据不同情况进行选择,实验实现多线程和多进程的服务器。

三、实验步骤

  1. 单线程进程并发模型:

    实验代码:

//tcp_server.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include   /*for errno*/
#define BUFFER_SIZE 1024
#define QLEN        32
void get_filename(char *filepath,char *filename);

int process_conn_server(int sd)
{
    ssize_t size = 0;
    char buffer[BUFFER_SIZE];
    FILE *stream;
    char filepath[100];

    strcpy(buffer,"please enter a path!\n");
    send(sd,buffer,BUFFER_SIZE,0);
    int length = 0;
    memset(filepath,'\0',sizeof(filepath));

    length = recv(sd,filepath,100,0);
    if(length < 0){
        printf("recv error!\n");
    }
    else
    {
        char filename[100] = {'\0'};
        get_filename(filepath,filename);
        printf("server: filename:%s\n",filename);

        if( (stream=fopen(filename, "w")) == NULL){
            printf("server:open file error!\n");
            return;
        }
        while(1){/*读取文件并写入文件流*/
            size = recv(sd, buffer, BUFFER_SIZE,0);
            printf("server:size:%d\n",size);
            if(size <= 0){
                break;
            }
            int write_len=fwrite(buffer, sizeof(char), size, stream);
        }
        printf("recv finished!\n");
        fclose(stream);
    }
    return 0;
}

int main(int argc, char *argv[]){
    char *service = "8888";
    struct sockaddr_in client;  //the address of a client
    unsigned int addr_len;      // the length of client's address
    int msock;                  //master server socket
    int ssock;                  //slave server socket
    switch(argc)
    {
    case 1:
        break;
    case 2:
        service = argv[1];
        break;
    default:
        errexit("usage: TCPfiletransfer [port]\n");
    }

    msock = passiveTCP(service, QLEN);

    printf("waiting...\n");
    /*显示核是sigchld信号*/
    //(void) signal(SIGCHLD, reaper);
    if(signal(SIGCHLD, SIG_IGN) == SIG_ERR){
            perror("signal error");
            return EXIT_SUCCESS;
        }
    while(1){
        socklen_t addr_len = sizeof(struct sockaddr);
        if( (ssock = accept(msock, (struct sockaddr*)&client, &addr_len)) < 0)
        {
            if(errno == EINTR)
                continue;
            errexit("accept: %s\n", strerror(errno));
        }
        printf("server:accept\n");

        switch(fork()){
        case 0:     /*child*/
            close(msock);/*在子进程中关闭服务器的监听*/
            exit(process_conn_server(ssock));
        default:    /*parent*/
            close(ssock);
            break;
        case -1:
            errexit("fork: %s\n", strerror(errno));
        }
    }
    return 0;
}
//passiveTCP.c
int passivesock(const char *service, const char *transport,
               int qlen);

int passiveTCP(const char *service, int qlen)
{
    return passivesock(service, "tcp", qlen);
}
//passivesock,c
//created by gregory
#include 
#include 
#include 

#include 
#include 
#include 
#include 

int errexit(const char *format, ...);

unsigned short portbase = 0;

int passivesock(const char *service, const char *transport, int qlen)
{
    struct hostent  *phe;
    struct servent  *pse;
    struct protoent *ppe;
    struct sockaddr_in  sin;
    int     s, type;


    memset(&sin,0,sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;

/*map port*/
    if ( pse = getservbyname(service, transport) )
		sin.sin_port = htons(ntohs((unsigned short)pse->s_port)
			+ portbase);
    else if ( (sin.sin_port = htons((unsigned short)atoi(service))) == 0)
		errexit("can't get \"%s\" service entry\n", service);
/*map protocol*/
    if ( (ppe = getprotobyname(transport)) == 0)
		errexit("can't get \"%s\" protocol entry\n", transport);
/*use protocol to chose a socket type*/
    if(strcmp(transport,"udp") == 0)
        type = SOCK_DGRAM;
    else
        type = SOCK_STREAM;
/*allocate a socket*/
    s = socket(PF_INET, type, ppe->p_proto);
    if (s < 0)
		errexit("can't create socket: %s\n", strerror(errno));

/*加入此代码是为了避免再次打开服务器程序出现bind error的错误*/
    int on = 1;
    int ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

/*bind the socket*/
    if(bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
        errexit("can't bind to %s port: %s\n", service,
                    strerror(errno));
    if(type == SOCK_STREAM && listen(s, qlen) < 0)
        errexit("can't listen on %s port: %s\n", service,
                    strerror(errno));
    return s;
}
//errexit.c
#include 
#include 
#include 

int errexit(const char *format, ...)
{
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    exit(1);
}

   

    有关connectTCP的代码,见第二个实验的博客:2.利用TCP/UDP完成文件传输的设计和实现(下)

//tcp_client.c
#include 
#include 
#include 
#include 
#include 
#include 
#include   /*for errno*/

#define BUFFER_SIZE 1024


void process_conn_client(int s)
{
    ssize_t size = 0;
    char buffer[BUFFER_SIZE];
    FILE *stream;

    int length = 0;
    char filepath[100] = {'\0'};
    size = read(s, buffer, BUFFER_SIZE);
    printf("%s",buffer);

    scanf("%s",filepath);
    write(s,filepath,100);

    if( (stream = fopen(filepath,"r")) == NULL) {
            printf("client:open file error!\n");
            return;
    }
    printf("sending!\n");
    while(1){
        size = fread(buffer,sizeof(char),BUFFER_SIZE,stream);
        if(size <= 0){
            break;
        }
        write(s,buffer,size);
    }
    printf("send finished!\n");
    fclose(stream);
}

int main(int argc, char* argv[]){
    char *host = "localhost";
    char *service = "8888";
    switch(argc)
    {
    case 1:
        host = "localhost";
        break;
    case 3:
        service = argv[2];
    case 2:
        host = argv[1];
        break;
    default:
        fprintf(stderr, "usage: TCPfiletransfer [ host [port]]\n");
        exit(1);
    }

    int socket = connectTCP(host,service);
    process_conn_client(socket);
    close(socket);
}

编译: 

 网络程序设计——4.利用多进程和多线程实现服务器端的并发处理_第1张图片

 

打开服务器端:

网络程序设计——4.利用多进程和多线程实现服务器端的并发处理_第2张图片

 

打开客户端:

网络程序设计——4.利用多进程和多线程实现服务器端的并发处理_第3张图片

        2.多线程并发模型:

        服务器代码:

//tcp_server_threads.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include   /*for errno*/
#include 

#define BUFFER_SIZE 1024
#define QLEN        32
void get_filename(char *filepath,char *filename);

int process_conn_server(int sd)
{
    ssize_t size = 0;
    char buffer[BUFFER_SIZE];
    FILE *stream;
    char filepath[100];

    strcpy(buffer,"please enter a path!\n");
    send(sd,buffer,BUFFER_SIZE,0);
    int length = 0;
    memset(filepath,'\0',sizeof(filepath));

    length = recv(sd,filepath,100,0);
    if(length < 0){
        printf("recv error!\n");
    }
    else
    {
        time_t start,end;
        time(&start);
        char filename[100] = {'\0'};
        get_filename(filepath,filename);
        printf("server: filename:%s\n",filename);

        if( (stream=fopen(filename, "w")) == NULL){
            printf("server:open file error!\n");
            return;
        }
        while(1){/*读取文件并写入文件流*/
            size = recv(sd, buffer, BUFFER_SIZE,0);
            if(size <= 0){
                break;
            }
            int write_len=fwrite(buffer, sizeof(char), size, stream);
        }
        time(&end);
        int use_time = end - start;
        printf("recv finished!\n");
        printf("time:%d\n",use_time);
        fclose(stream);
    }
    return 0;
}

int main(int argc, char *argv[]){
    char *service = "8888";
    struct sockaddr_in client;  //the address of a client
    unsigned int addr_len;      // the length of client's address
    int msock;                  //master server socket
    int ssock;                  //slave server socket
    switch(argc)
    {
    case 1:
        break;
    case 2:
        service = argv[1];
        break;
    default:
        errexit("usage: TCPfiletransfer [port]\n");
    }

    msock = passiveTCP(service, QLEN);

    printf("waiting...\n");
    /*显示核是sigchld信号*/
    //(void) signal(SIGCHLD, reaper);
    if(signal(SIGCHLD, SIG_IGN) == SIG_ERR){
            perror("signal error");
            return EXIT_SUCCESS;
        }
    pthread_t th;
    pthread_attr_t ta;
    pthread_attr_init(&ta);
    pthread_attr_setdetachstate(&ta, PTHREAD_CREATE_DETACHED);
    while(1)
    {
        addr_len = sizeof(client);
        if( (ssock = accept(msock, (struct sockaddr*)&client, &addr_len)) < 0)
        {
            if(errno == EINTR)
                continue;
            errexit("accept: %s\n", strerror(errno));
        }
        if(pthread_create(&th, &ta, (void * (*)(void *))process_conn_server,(void *)ssock) < 0)
        {
            errexit("pthread_create: %s\n", strerror(errno));;
        }
    }
    return 0;
}

运行服务器:

网络程序设计——4.利用多进程和多线程实现服务器端的并发处理_第4张图片

运行客户端:

网络程序设计——4.利用多进程和多线程实现服务器端的并发处理_第5张图片

你可能感兴趣的:(socket)