• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Linux socket编程多进程/多线程TCP并发服务器模型

武飞扬头像
徐艺桐
帮助1

一、循环服务器(while)【不常用】

  1. 一次只能处理一个客户端的请求,等这个客户端退出后,才能处理下一个客户端。
  2. 缺点:循环服务器所处理的客户端不能有耗时操作。

模型

  1.  
    sfd = socket();
  2.  
    bind();
  3.  
    listen();
  4.  
    while(1)
  5.  
    {
  6.  
    newfd = accept();
  7.  
    while(1)
  8.  
    {
  9.  
    recv();
  10.  
    send();
  11.  
    }
  12.  
    close(newfd);
  13.  
    }
  14.  
    close(sfd);

源码

  1.  
    #include <stdio.h>
  2.  
    #include <sys/socket.h>
  3.  
    #include <sys/types.h>
  4.  
    #include <arpa/inet.h>
  5.  
    #include <string.h>
  6.  
    #include <netinet/in.h>
  7.  
    #include <netinet/ip.h>
  8.  
    #include <head.h>
  9.  
     
  10.  
    #define PORT 6666 //1024-49151
  11.  
    #define IP "192.168.122.80" //ifconfig查看本机ip
  12.  
     
  13.  
    int main(int argc, const char *argv[])
  14.  
    {
  15.  
    //创建流式套接字
  16.  
    int sfd = socket(AF_INET,SOCK_STREAM,0);
  17.  
    if(sfd < 0)
  18.  
    {
  19.  
    ERR_MSG("socket");
  20.  
    return -1;
  21.  
    }
  22.  
    printf("sfd= %d\n",sfd);
  23.  
     
  24.  
    //填充地址信息结构体,真实的地址信息结构体根据地址族制定
  25.  
    //AF_INET: man 7 ip
  26.  
    struct sockaddr_in sin;
  27.  
    sin.sin_family = AF_INET; //必须填AF_INET
  28.  
    sin.sin_port = htons(PORT); //端口号:1024~49151(网络端口号的字节序)(端口号存储在2个字节的无符号整数中)
  29.  
    sin.sin_addr.s_addr = inet_addr(IP); //本机IP inconfig查看(本机IP地址的字节序)
  30.  
     
  31.  
    //设置端口允许端口被快速复用
  32.  
    int reuse = 1;
  33.  
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
  34.  
    {
  35.  
    ERR_MSG("setsockopt");
  36.  
    return -1;
  37.  
    }
  38.  
    printf("允许端口快速重用成功\n");
  39.  
    //绑定服务器的IP和端口号--->必须绑定( bind )
  40.  
    if(bind(sfd , (struct sockaddr*)&sin, sizeof(sin)) < 0)
  41.  
    {
  42.  
    ERR_MSG("bind");
  43.  
    return -1;
  44.  
    }
  45.  
    printf("bind success\n");
  46.  
     
  47.  
    //将套接字设置为被动监听状态( listen)
  48.  
    if( listen(sfd,128) < 0)
  49.  
    {
  50.  
    ERR_MSG("listen");
  51.  
    return -1;
  52.  
    }
  53.  
    printf("listen success\n");
  54.  
     
  55.  
    struct sockaddr_in cin; //存储客户端的地址信息
  56.  
    socklen_t addrlen = sizeof(cin);
  57.  
    int newfd = -1;
  58.  
    //从已完成连接的队列中获取一个客户端信息,生成一个新的文件描述符
  59.  
    //该文件描述符才是与客户端的通信的文件描述符
  60.  
    //int newfd = accept(sfd, NULL ,NULL);
  61.  
    while(1)
  62.  
    {
  63.  
    newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);
  64.  
    if(newfd < 0)
  65.  
    {
  66.  
    ERR_MSG("accept");
  67.  
    return -1;
  68.  
    }
  69.  
    printf("[%s : %d]newfd=%d 客户端连接成功\n",\
  70.  
    inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
  71.  
    char buf[128]="";
  72.  
    ssize_t res = 0;
  73.  
    while(1)
  74.  
    {
  75.  
    //接收数据
  76.  
    bzero(buf,sizeof(buf));
  77.  
    res=recv(newfd,buf,sizeof(buf),0);
  78.  
    if(res < 0)
  79.  
    {
  80.  
    ERR_MSG("res");
  81.  
    return -1;
  82.  
    }
  83.  
    else if(0 == res)
  84.  
    {
  85.  
    printf("[%s : %d]newfd=%d 客户端已下线\n",\
  86.  
    inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
  87.  
    break;
  88.  
    }
  89.  
    printf("[%s : %d]newfd=%d : %s\n",\
  90.  
    inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd, buf);
  91.  
    //发送数据
  92.  
    strcat(buf,"*_*");
  93.  
    if(send(newfd,buf,sizeof(buf),0) < 0)
  94.  
    {
  95.  
    ERR_MSG("send");
  96.  
    return -1;
  97.  
    }
  98.  
    printf("send succuss\n");
  99.  
     
  100.  
    }
  101.  
    }
  102.  
    //关闭所有文件描述符
  103.  
    close(newfd);
  104.  
    close(sfd);
  105.  
    return 0;
  106.  
    }
学新通

二、并发服务器【常用】

  1. 可以同时处理多个客户端请求
  2. 父进程 / 主线程专门用于负责连接,创建子进程 / 分支线程用来与客户端交互。

1) 多进程

模型

  1.  
    void zombie_callBack(int sig)
  2.  
    {
  3.  
    while(waitpid(-1, NULL, WNOHANG) > 0);
  4.  
    }
  5.  
     
  6.  
    signal(17, zombie_callback);
  7.  
     
  8.  
    sfd = socket();
  9.  
    bind();
  10.  
    listen();
  11.  
    while(1)
  12.  
    {
  13.  
    newfd = accept();
  14.  
    if(0 == fork())
  15.  
    {
  16.  
    close(sfd) ;
  17.  
    recv();
  18.  
    send();
  19.  
    close(newfd);
  20.  
    exit(0);
  21.  
    }
  22.  
    close(newfd);
  23.  
    }
  24.  
    close(sfd);
学新通

源码

  1.  
    #include <stdio.h>
  2.  
    #include <sys/socket.h>
  3.  
    #include <sys/types.h>
  4.  
    #include <arpa/inet.h>
  5.  
    #include <string.h>
  6.  
    #include <stdlib.h>
  7.  
    #include <signal.h>
  8.  
    #include <sys/wait.h>
  9.  
    #include <netinet/in.h>
  10.  
    #include <head.h>
  11.  
     
  12.  
    #define PORT 6666 //1024-49151
  13.  
    #define IP "192.168.122.80" //ifconfig查看本机ip
  14.  
     
  15.  
    int deal_cli_msg(int newfd,struct sockaddr_in cin);
  16.  
     
  17.  
    void handlr(int sig)
  18.  
    {
  19.  
    while(waitpid(-1,NULL,WNOHANG) > 0);
  20.  
    }
  21.  
     
  22.  
    int main(int argc, const char *argv[])
  23.  
    {
  24.  
    if(signal(17,handlr) == SIG_ERR)
  25.  
    {
  26.  
    ERR_MSG("signal");
  27.  
    return -1;
  28.  
    }
  29.  
    printf("捕获成功\n");
  30.  
     
  31.  
    //创建流式套接字
  32.  
    int sfd = socket(AF_INET,SOCK_STREAM,0);
  33.  
    if(sfd < 0)
  34.  
    {
  35.  
    ERR_MSG("socket");
  36.  
    return -1;
  37.  
    }
  38.  
    printf("sfd= %d\n",sfd);
  39.  
     
  40.  
    //设置端口允许端口被快速复用
  41.  
    int reuse = 1;
  42.  
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
  43.  
    {
  44.  
    ERR_MSG("setsockopt");
  45.  
    return -1;
  46.  
    }
  47.  
    printf("允许端口快速重用成功\n");
  48.  
     
  49.  
    //填充地址信息结构体,真实的地址信息结构体根据地址族制定
  50.  
    //AF_INET: man 7 ip
  51.  
    struct sockaddr_in sin;
  52.  
    sin.sin_family = AF_INET; //必须填AF_INET
  53.  
    sin.sin_port = htons(PORT); //端口号:1024~49151(网络端口号的字节序)(端口号存储在2个字节的无符号整数中)
  54.  
    sin.sin_addr.s_addr = inet_addr(IP); //本机IP inconfig查看(本机IP地址的字节序)
  55.  
     
  56.  
    //绑定服务器的IP和端口号--->必须绑定( bind )
  57.  
    if(bind(sfd , (struct sockaddr*)&sin, sizeof(sin)) < 0)
  58.  
    {
  59.  
    ERR_MSG("bind");
  60.  
    return -1;
  61.  
    }
  62.  
    printf("bind success\n");
  63.  
     
  64.  
    //将套接字设置为被动监听状态( listen)
  65.  
    if( listen(sfd,128) < 0)
  66.  
    {
  67.  
    ERR_MSG("listen");
  68.  
    return -1;
  69.  
    }
  70.  
    printf("listen success\n");
  71.  
     
  72.  
    struct sockaddr_in cin; //存储客户端的地址信息
  73.  
    socklen_t addrlen = sizeof(cin);
  74.  
    int newfd = -1;
  75.  
     
  76.  
    pid_t cpid = 0;
  77.  
    while(1)
  78.  
    {
  79.  
    newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);
  80.  
    if(newfd < 0)
  81.  
    {
  82.  
    ERR_MSG("accept");
  83.  
    return -1;
  84.  
    }
  85.  
    printf("[%s : %d]newfd=%d 客户端连接成功\n",\
  86.  
    inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
  87.  
     
  88.  
    cpid = fork();
  89.  
    if(0 == cpid)
  90.  
    {
  91.  
    close(sfd);
  92.  
    deal_cli_msg(newfd,cin);
  93.  
    exit(0);
  94.  
    }
  95.  
    close(newfd);
  96.  
    }
  97.  
    //关闭所有文件描述符
  98.  
    close(sfd);
  99.  
    return 0;
  100.  
    }
  101.  
     
  102.  
    int deal_cli_msg(int newfd,struct sockaddr_in cin)
  103.  
    {
  104.  
    char buf[128]="";
  105.  
    ssize_t res = 0;
  106.  
    while(1)
  107.  
    {
  108.  
    //接收数据
  109.  
    bzero(buf,sizeof(buf));
  110.  
    res=recv(newfd,buf,sizeof(buf),0);
  111.  
    if(res < 0)
  112.  
    {
  113.  
    ERR_MSG("recv");
  114.  
    return -1;
  115.  
    }
  116.  
    else if(0 == res)
  117.  
    {
  118.  
    printf("[%s : %d]newfd=%d 客户端已下线\n",\
  119.  
    inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
  120.  
    break;
  121.  
    }
  122.  
    printf("[%s : %d]newfd=%d : %s\n",\
  123.  
    inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd, buf);
  124.  
    //发送数据
  125.  
    strcat(buf,"*_*");
  126.  
    if(send(newfd,buf,sizeof(buf),0) < 0)
  127.  
    {
  128.  
    ERR_MSG("send");
  129.  
    return -1;
  130.  
    }
  131.  
    printf("send succuss\n");
  132.  
    }
  133.  
    close(newfd);
  134.  
    }
学新通

2) 多线程

模型

  1.  
    sfd = socket();
  2.  
    bind();
  3.  
    listen();
  4.  
    while(1)
  5.  
    {
  6.  
    newfd = accept();
  7.  
    pthread_create(); --> callBack();
  8.  
    pthread_detach(tid);
  9.  
    }
  10.  
    close(sfd);
  11.  
     
  12.  
    void* callBack(void* arg)
  13.  
    {
  14.  
    参数另存
  15.  
    recv();
  16.  
    send();
  17.  
    close(newfd);
  18.  
    pthread_exit(NULL);
  19.  
    }
学新通

源码

  1.  
    #include <stdio.h>
  2.  
    #include <sys/types.h>
  3.  
    #include <sys/socket.h>
  4.  
    #include <arpa/inet.h>
  5.  
    #include <netinet/in.h>
  6.  
    #include <string.h>
  7.  
    #include <unistd.h>
  8.  
    #include <pthread.h>
  9.  
     
  10.  
    #define ERR_MSG(msg) do{\
  11.  
    fprintf(stderr, "line:%d ", __LINE__);\
  12.  
    perror(msg);\
  13.  
    }while(0)
  14.  
     
  15.  
    #define PORT 6666 //1024~49151
  16.  
    #define IP "127.0.0.1" //IP地址,本机IP ifconfig
  17.  
     
  18.  
    struct cli_msg
  19.  
    {
  20.  
    int newfd;
  21.  
    struct sockaddr_in cin;
  22.  
    };
  23.  
     
  24.  
    void* deal_cli_msg(void* arg);
  25.  
     
  26.  
    int main(int argc, const char *argv[])
  27.  
    {
  28.  
    //创建流式套接字
  29.  
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
  30.  
    if(sfd < 0)
  31.  
    {
  32.  
    ERR_MSG("socket");
  33.  
    return -1;
  34.  
    }
  35.  
    printf("socket create success sfd = %d\n", sfd);
  36.  
     
  37.  
    //设置允许端口快速被重用
  38.  
    int resue = 1;
  39.  
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &resue, sizeof(resue)) < 0)
  40.  
    {
  41.  
    ERR_MSG("setsockopt");
  42.  
    return -1;
  43.  
    }
  44.  
     
  45.  
    //填充服务器的地址信息结构体
  46.  
    //真实的地址信息结构体根据地址族执行,AF_INET: man 7 ip
  47.  
    struct sockaddr_in sin;
  48.  
    sin.sin_family = AF_INET; //必须填AF_INET;
  49.  
    sin.sin_port = htons(PORT); //端口号的网络字节序,1024~49151
  50.  
    sin.sin_addr.s_addr = inet_addr(IP); //IP地址的网络字节序,ifconfig查看
  51.  
     
  52.  
    //绑定---必须绑定
  53.  
    if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
  54.  
    {
  55.  
    ERR_MSG("bind");
  56.  
    return -1;
  57.  
    }
  58.  
    printf("bind success __%d__\n", __LINE__);
  59.  
     
  60.  
    //将套接字设置为被动监听状态
  61.  
    if(listen(sfd, 128) < 0)
  62.  
    {
  63.  
    ERR_MSG("listen");
  64.  
    return -1;
  65.  
    }
  66.  
    printf("listen success __%d__\n", __LINE__);
  67.  
     
  68.  
     
  69.  
    //功能:阻塞函数,阻塞等待客户端连接成功。
  70.  
    //当客户端连接成功后,会从已完成连接的队列头中获取一个客户端信息,
  71.  
    //并生成一个新的文件描述符;新的文件描述符才是与客户端通信的文件描述符
  72.  
    struct sockaddr_in cin; //存储连接成功的客户端的地址信息
  73.  
    socklen_t addrlen = sizeof(cin);
  74.  
    int newfd = -1;
  75.  
     
  76.  
    pthread_t tid;
  77.  
    struct cli_msg info;
  78.  
     
  79.  
    while(1)
  80.  
    {
  81.  
    //主线程负责连接
  82.  
    //accept函数阻塞之前,会先预选一个没有被使用过的文件描述符
  83.  
    //当解除阻塞后,会判断预选的文件描述符是否被使用
  84.  
    //如果被使用了,则重新遍历一个没有被用过的
  85.  
    //如果没有被使用,则直接返回预先的文件描述符;
  86.  
    newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);
  87.  
    if(newfd < 0)
  88.  
    {
  89.  
    ERR_MSG("accept");
  90.  
    return -1;
  91.  
    }
  92.  
    printf("[%s : %d] newfd=%d 客户端连接成功\n", \
  93.  
    inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);
  94.  
     
  95.  
    info.newfd = newfd;
  96.  
    info.cin = cin;
  97.  
     
  98.  
    //能运行到当前位置,则代表有客户端连接成功
  99.  
    //则需要创建一个分支线程用来,与客户端交互
  100.  
    if(pthread_create(&tid, NULL, deal_cli_msg, &info) != 0)
  101.  
    {
  102.  
    fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);
  103.  
    return -1;
  104.  
    }
  105.  
     
  106.  
    pthread_detach(tid); //分离线程
  107.  
    }
  108.  
     
  109.  
     
  110.  
    //关闭所有套接字文件描述符
  111.  
    close(sfd);
  112.  
    return 0;
  113.  
    }
  114.  
     
  115.  
    //线程执行体
  116.  
    void* deal_cli_msg(void* arg) //void* arg = (void*)&info
  117.  
    {
  118.  
    //必须要另存,因为同一个进程下的线程共享其附属进程的所有资源
  119.  
    //如果使用全局,则会导致每次连接客户端后, newfd和cin会被覆盖
  120.  
    //如果使用指针间接访问外部成员变量,也会导致,成员变量被覆盖。
  121.  
     
  122.  
    int newfd = ((struct cli_msg*)arg)->newfd;
  123.  
    struct sockaddr_in cin = ((struct cli_msg*)arg)->cin;
  124.  
     
  125.  
    char buf[128] = "";
  126.  
    ssize_t res = -1;
  127.  
    while(1)
  128.  
    {
  129.  
    bzero(buf, sizeof(buf));
  130.  
    //接收
  131.  
    res = recv(newfd, buf, sizeof(buf), 0);
  132.  
    if(res < 0)
  133.  
    {
  134.  
    ERR_MSG("recv");
  135.  
    break;
  136.  
    }
  137.  
    else if(0 == res)
  138.  
    {
  139.  
    fprintf(stderr, "[%s : %d] newfd=%d 客户端下线\n", \
  140.  
    inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);
  141.  
    break;
  142.  
    }
  143.  
    printf("[%s : %d] newfd=%d : %s\n", \
  144.  
    inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, buf);
  145.  
     
  146.  
    //发送 -- 将数据拼接一个 *_* 发送回去
  147.  
    strcat(buf, "*_*");
  148.  
    if(send(newfd, buf, sizeof(buf), 0) < 0)
  149.  
    {
  150.  
    ERR_MSG("send");
  151.  
    break;
  152.  
    }
  153.  
    printf("send success\n");
  154.  
    }
  155.  
    close(newfd);
  156.  
     
  157.  
    pthread_exit(NULL);
  158.  
    }
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhhifbke
系列文章
更多 icon
同类精品
更多 icon
继续加载