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

版本一:

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

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值