多进程、多线程服务器程序

来源:互联网 发布:资管业务 知乎 编辑:程序博客网 时间:2024/06/06 20:08

版本一:

客户端和服务器端可以进行通信,但是只能有一个客户端。

1.服务器端程序tcp_server.c

#include<stdio.h>#include<string.h>#include<stdlib.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>static void Usage(const char *proc){//提示客户端运行时的形式ip+端口号    printf("%s[local_ip][local_port]\n",proc);}int startup(const char *_ip,int _port){    //建立服务器端套接字socket    int sock=socket(AF_INET,SOCK_STREAM,0);    if(sock<0)    {        perror("socket");        exit(2);    }    struct sockaddr_in local;    local.sin_family=AF_INET;    local.sin_port=htons(_port);    local.sin_addr.s_addr=inet_addr(_ip);     //将套接字绑定到网络服务器的地址上    if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)    {        perror("bind");        exit(3);    }     //建立监听队列    if(listen(sock,5)<0)    {        perror("listen");        exit(4);    }    return sock;}int main(int argc,char *argv[]){    if(argc!=3)    {        Usage(argv[0]);        return 1;     }     int listen_sock=startup(argv[1],atoi(argv[2]));    while(1)    {        struct sockaddr_in client;        socklen_t len=sizeof(client);        //等待客户端连接请求到达        int new_fd=accept(listen_sock,(struct sockaddr*)&client,&len);        if(new_fd<0)        {           perror("accept");           continue;        }         //客户端连接成功        printf("get a new client,%s:%d\n",inet_ntoa(client.sin_addr),ntohs(\        client.sin_port));        while(1)        {           char buf[1024];           //接收客户端发送的数据           ssize_t s=read(new_fd,buf,sizeof(buf)-1);           if(s>0)           {               buf[s]=0;               printf("client: %s\n",buf);               write(new_fd,buf,sizeof(buf)-1);           }           else           {//客户端的数据被读完,即连接断开               printf("read done...,break\n");               break;           }        }    }}

2.客户端程序 tcp_client.c

#include<stdio.h>#include<string.h>#include<stdlib.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>static void Usage(const char *proc){//提示客户端运行时的形式ip+端口号    printf("%s[server_ip][server_port]\n",proc);}int main(int argc,char *argv[]){    if(argc!=3)    {        Usage(argv[0]);        return 1;     }    //建立客户端套接字    int sock=socket(AF_INET,SOCK_STREAM,0);    if(sock<0)    {        perror("socket");        return 2;    }    //服务器端的网络地址结构    struct sockaddr_in remote;    remote.sin_family=AF_INET;    remote.sin_port=htons(atoi(argv[2]));    remote.sin_addr.s_addr=inet_addr(argv[1]);    //与远程服务器连接    if(connect(sock,(struct sockaddr*)&remote,sizeof(remote))<0)    {        perror("connect");        return 3;    }           while(1)    {        char buf[1024];        printf("Please Enter#");        //在屏幕上输入数据        fflush(stdout);        ssize_t s=read(0,buf,sizeof(buf)-1);        if(s>0)        {            buf[s-1]=0;            write(sock,buf,strlen(buf));            ssize_t _s=read(sock,buf,sizeof(buf)-1);            if(_s>0)            {                buf[_s]=0;                printf("Server echo#%s\n",buf);            }        }    }}

①先运行服务器端,当客户端连接上服务器端时,服务器端会给出提示:
这里写图片描述

②客户端向服务器端发送数据,若服务器端接收到数据时,会回显在客户端的屏幕上
这里写图片描述

③服务器端接收到客户端的数据,当客户端断开连接时,服务器端显示数据已读完
这里写图片描述

版本二:

多进程服务器:
修改服务器,让服务器端可以被多个客户端连接,实现多用户、多进程

tcp_server.c

#include<stdio.h>#include<string.h>#include<stdlib.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>static void Usage(const char *proc){    printf("%s[local_ip][local_port]\n",proc);}int startup(const char *_ip,int _port){    int sock=socket(AF_INET,SOCK_STREAM,0);    if(sock<0)    {        perror("socket");        exit(2);    }    struct sockaddr_in local;    local.sin_family=AF_INET;    local.sin_port=htons(_port);    local.sin_addr.s_addr=inet_addr(_ip);    if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)    {        perror("bind");        exit(3);    }    if(listen(sock,5)<0)    {        perror("listen");        exit(4);    }    return sock;}int main(int argc,char *argv[]){    if(argc!=3)    {        Usage(argv[0]);        return 1;     }    //返回监听套接字    int listen_sock=startup(argv[1],atoi(argv[2]));    while(1)    {        struct sockaddr_in client;        socklen_t len=sizeof(client);        int new_fd=accept(listen_sock,(struct sockaddr*)&client,&len);        if(new_fd<0)        {           perror("accept");           continue;        }        printf("get a new client,%s:%d\n",inet_ntoa(client.sin_addr),ntohs(\        client.sin_port));        //创建子进程        pid_t id=fork();        if(id<0)        {             perror("fork");            close(new_fd);        }        else if(id==0)        {//child            close(listen_sock);            if(fork()>0)            {//子进程退出,子进程的子进程变为孤儿进程,被1号进程回收,让子进程的子进程继续运行程序,父进程不需要再回收子进程                exit(0);            }            while(1)            {                char buf[1024];                ssize_t s=read(new_fd,buf,sizeof(buf)-1);                if(s>0)                {                    buf[s]=0;                    printf("client: %s\n",buf);                    write(new_fd,buf,sizeof(buf)-1);                }                else                {                    printf("read done...,break\n");                    return;                }            }            close(new_fd);            }        else        {//father            close(new_fd);        }    }}

版本三:

多线程服务器:
编译时需要链接线程的库 -lpthread

#include<stdio.h>#include<string.h>#include<stdlib.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>static void Usage(const char *proc){//提示客户端运行时的形式ip+端口号    printf("%s[local_ip][local_port]\n",proc);}int startup(const char *_ip,int _port){    //建立服务器套接字    int sock=socket(AF_INET,SOCK_STREAM,0);    if(sock<0)    {        perror("socket");        exit(2);    }    struct sockaddr_in local;    local.sin_family=AF_INET;    local.sin_port=htons(_port);    local.sin_addr.s_addr=inet_addr(_ip);    //将套接字绑定到网络服务器的地址上    if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)    {        perror("bind");        exit(3);    }    //建立监听队列    if(listen(sock,5)<0)    {        perror("listen");        exit(4);    }    return sock;}void *handlerRequest(void *arg){//处理客户端数据    int new_fd=(int)arg;     while(1)     {          char buf[1024];          ssize_t s=read(new_fd,buf,sizeof(buf)-1);          if(s>0)          {              buf[s]=0;              printf("client: %s\n",buf);              write(new_fd,buf,sizeof(buf)-1);          }          else          {              printf("read done...,break\n");              break;          }      }}int main(int argc,char *argv[]){    if(argc!=3)    {        Usage(argv[0]);        return 1;     }    int listen_sock=startup(argv[1],atoi(argv[2]));    while(1)    {        struct sockaddr_in client;        socklen_t len=sizeof(client);        //等待客户端的请求连接        int new_fd=accept(listen_sock,(struct sockaddr*)&client,&len);        if(new_fd<0)        {           perror("accept");           continue;        }        printf("get a new client,%s:%d\n",inet_ntoa(client.sin_addr),ntohs(\        client.sin_port));        pthread_t id;        //创建线程        pthread_create(&id,NULL,handlerRequest,(void*)new_fd);        //线程运行结束后释放资源        pthread_detach(id);    }}

bind错误:

当服务器和客户端连接成功后,服务器不小心按下ctrl-c退出后,再次连接时,会显示一个bind错误,bind:Address already in use。原因: 该错误是由于TCP 套接字状态 TIME_WAIT 引起,TIME_WAIT状态在套接字关闭后约保留 2 到 4 分钟。当TIME_WAIT 状态退出之后,套接字被删除,此时该地址才能被重新绑定而不出问题。解决方法:①等待2~4分钟重新连接;        ②给套接字应用 SO_REUSEADDR 套接字选项,以便端口可以马上重用        ③换一个端口使用;

如图所示:
这里写图片描述

阅读全文
0 0
原创粉丝点击