首页 > 编程语言 >网络编程day05(循环服务器、并发服务器)

网络编程day05(循环服务器、并发服务器)

时间:2024-09-11 21:20:35浏览次数:11  
标签:printf addr 编程 day05 acceptfd saddr 服务器 include sin

目录

服务器模型

 1》循环服务器

 2》并发服务器

1> 多进程:每有一个客户端连接创建一个进程进行通信

2>  多线程:每有一个客户端连接创建一个线程进行通信

 3> IO多路复用

4> 总结


服务器模型

在网络通信中,通常一个服务器要连接多个客户端

为了处理多个客户端的请求,通常有多种表现形式:循环服务器、并发服务器

 1》循环服务器

循环服务器:一个服务器在同一时间只能处理一个客户端的请求

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>

int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        perror("argc err\n");
        return -1;
    }
    char buf[128] = {0};
    // 1.创建套接字(socket)---------------》有手机
    int socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if (socketfd < 0)
    {
        perror("socket err\n");
        return -1;
    }
    printf("socketfd: %d\n", socketfd);
    // 2.指定(自己)网络信息--------------------》有号码
    // struct sockaddr_in
    // {
    //     sa_family_t sin_family;  /* address family: AF_INET */
    //     in_port_t sin_port;      /* port in network byte order */
    //     struct in_addr sin_addr; /* internet address */
    // };

    // /* Internet address. */
    // struct in_addr
    // {
    //     uint32_t s_addr; /* address in network byte order */
    // };

    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1]));
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); // 全局地址,自动获取本机地址
    // saddr.sin_addr.s_addr = INADDR_ANY;
    // 3.绑定套接字(bind)------------------》绑定手机(插卡)
    if (bind(socketfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind err\n");
        return -1;
    }
    printf("bind ok\n");
    // 4.监听套接字(listen)-----------------》待机
    if (listen(socketfd, 6) < 0)
    {
        perror("listen err\n");
        return -1;
    }
    printf("listen ok\n");

    // 5.接收客户端连接请求(accept)--》接电话
    while (1) // 循环等待客户端链接,实现循环服务器功能
    {
        int len = sizeof(caddr);
        int acceptfd = accept(socketfd, (struct sockaddr *)&caddr, &len);
        if (acceptfd < 0)
        {
            perror("accept err\n");
            return -1;
        }
        printf("acceptfd: %d\n", acceptfd);
        printf("port: %d  ip: %s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr)); // 来电显示,显示客户端信息
        // 6.接收、发送数据(recv send)---》通话
        int ret;

        /*设置套接字属性:超时重传*/
        struct timeval tm = {2, 0};
        setsockopt(acceptfd, SOL_SOCKET, SO_REUSEADDR, &tm, sizeof(tm));

        while (1)
        {
            ret = recv(acceptfd, buf, sizeof(buf), 0);
            if (ret < 0)
            { 
                perror("recv error\n");
                
            }
            else if (ret == 0)
            {
                printf("client exit\n");
                break;
            }
            else
            {
                printf("buf: %s\n", buf);
                memset(buf, 0, sizeof(buf));
            }
        }

        // 7.关闭套接字(close)-----------------》挂电话
        close(acceptfd);
    }
    close(socketfd);

    return 0;
}

 2》并发服务器

并发服务器:一个服务器在同一时间内可以处理多个客户端的请求,有三种实现方法

1> 多进程:每有一个客户端连接创建一个进程进行通信

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>

void handler(int sig)
{
    wait(NULL);//回收子进程资源
}

int main(int argc, char const *argv[])
{
    pid_t pid;
    char buf[128] = {0};
    int ret, acceptfd;
    // 1.创建套接字(socket)---------------》有手机
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return -1;
    }
    printf("sockfd:%d\n", sockfd); // 3
    // 2.指定网络信息---------------------------》有号码
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;            // IPV4
    saddr.sin_port = htons(atoi(argv[1])); // 端口号
    // saddr.sin_addr.s_addr = inet_addr("192.168.50.13"); // 虚拟机IP
    // saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    saddr.sin_addr.s_addr = INADDR_ANY;
    int len = sizeof(caddr);
    // 3.绑定套接字(bind)------------------》绑定手机(插卡)
    if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind err");
        return -1;
    }
    printf("bind ok\n");
    // 4.监听套接字(listen)-----------------》待机
    if (listen(sockfd, 6) < 0)
    {
        perror("listen err");
        return -1;
    }
    printf("listen ok\n");
    // 5.接收客户端连接连接请求(accept)--》接电话
    // tcp服务器一共有两类文件描述符,一类用于连接,一类用于通信
    // socket函数返回值:用于连接的文件描述符
    // accept函数返回值:用于通信的文件描述符
    signal(SIGCHLD, handler);//设置信号处理方式
    while (1)
    {
        acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
        if (acceptfd < 0)
        {
            perror("accept err");
            return -1;
        }
        printf("port:%d ip:%s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));
        printf("acceptfd:%d\n", acceptfd);
        pid = fork();//创建子进程
        if (pid < 0)
        {
            perror("fork err");
            return -1;
        }
        else if (pid == 0)//子进程用于通信
        {
            // 6.接收、发送数据(recv send)---》通话
            while (1)
            {
                // read/write()
                ret = recv(acceptfd, buf, sizeof(buf), 0);
                if (ret < 0)
                {
                    perror("recv err");
                    break;
                }
                else if (ret == 0)//客户端退出
                {
                    printf("client exit\n");
                    break;
                }
                else
                {
                    printf("buf:%s\n", buf);
                    memset(buf, 0, sizeof(buf));
                }
            }
            // 7.关闭套接字(close)-----------------》挂电话
            close(acceptfd);//关闭子进程通信描述符
            exit(0);//退出子进程
        }
        else//父进程用于循环链接客户端
            close(acceptfd);//关闭父进程通信文件描述符
    }

    close(sockfd);

    return 0;
}

2>  多线程:每有一个客户端连接创建一个线程进行通信

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>

void *handler(void *arg)//从线程函数
{
    int ret, acceptfd;
    acceptfd = *((int *)arg);//强转为 int 类型
    char buf[128] = {0};
    // 6.接收、发送数据(recv send)---》通话
    while (1)
    {
        // read/write()
        ret = recv(acceptfd, buf, sizeof(buf), 0);
        if (ret < 0)
        {
            perror("recv err");
            return NULL;
        }
        else if (ret == 0)
        {
            printf("client exit\n");
            break;
        }
        else
        {
            printf("buf:%s\n", buf);
            memset(buf, 0, sizeof(buf));
        }
        // 7.关闭套接字(close)-----------------》挂电话
        }
    close(acceptfd);
    pthread_exit(NULL);//退出线程
}

int main(int argc, char const *argv[])
{

    int acceptfd;
    pthread_t tid;
    // 1.创建套接字(socket)---------------》有手机
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return -1;
    }
    printf("sockfd:%d\n", sockfd); // 3
    // 2.指定网络信息---------------------------》有号码
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;            // IPV4
    saddr.sin_port = htons(atoi(argv[1])); // 端口号
    // saddr.sin_addr.s_addr = inet_addr("192.168.50.13"); // 虚拟机IP
    // saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    saddr.sin_addr.s_addr = INADDR_ANY;
    int len = sizeof(caddr);
    // 3.绑定套接字(bind)------------------》绑定手机(插卡)
    if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
    {
        perror("bind err");
        return -1;
    }
    printf("bind ok\n");
    // 4.监听套接字(listen)-----------------》待机
    if (listen(sockfd, 6) < 0)
    {
        perror("listen err");
        return -1;
    }
    printf("listen ok\n");
    // 5.接收客户端连接连接请求(accept)--》接电话
    // tcp服务器一共有两类文件描述符,一类用于连接,一类用于通信
    // socket函数返回值:用于连接的文件描述符
    // accept函数返回值:用于通信的文件描述符
    while (1)
    {

        acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
        if (acceptfd < 0)
        {
            perror("accept err");
            return -1;
        }
        printf("port:%d ip:%s\n", ntohs(caddr.sin_port), inet_ntoa(caddr.sin_addr));
        printf("acceptfd:%d\n", acceptfd);

        pthread_create(&tid, NULL, handler, &acceptfd);//创建从线程
        pthread_detach(tid);//不阻塞回收从线程资源
    }
    close(sockfd);

    return 0;
}

 3> IO多路复用

select  poll  epoll 见上篇博客 IO多路复用

4> 总结

多进程:

优点:服务器更稳定,父子进程资源独立,安全性高

缺点:需要开辟多个进程,资源消耗大,系统开销大

多线程:

优点:相对于多进程,资源开销小,多个线程共享同一个进程的资源

缺点:需要开辟多个线程,安全性差

IO多路复用:

优点:节省资源,系统开销小,性能高

缺点:代码复杂度高


今天的分享就到这里结束啦,如果有哪里写的不好的地方,请指正。
如果觉得不错并且对你有帮助的话点个关注支持一下吧!

标签:printf,addr,编程,day05,acceptfd,saddr,服务器,include,sin
From: https://blog.csdn.net/dghbs/article/details/142148718

相关文章

  • 二、并发编程与多线程-2.2、多线程(下)
    2.2、多线程(下)2.2.9、线程池是如何实现线程复用的?答:线程池采用了生产者-消费者模型来实现线程复用。提交任务到线程池里的线程被称为生产者线程,它不断往线程池里传递任务,这些任务会被保存到线程池的阻塞队列里。然后线程池里的工作线程会不断从阻塞队列中获取任务并执行......
  • 网络编程9.10
    使用数据库完成工人管理系统:ubuntu@ubuntu:DB$ubuntu@ubuntu:DB$cat2.c#include<myhead.h>#include<sqlite3.h>#include<string.h>typedefstruct{intid;charname[20];doublesalary;}Worker;intdo_insert(sqlite3*ppDb){Wo......
  • Linux网络——socket编程与UDP实现服务器与客户机通信
    文章目录端口号TCP/UDP网络字节序socket的常见APIUDP实现服务器与客户机通信服务器客户机运行效果如下端口号我们说即便是计算机网络,他们之间的通信也仍然是进程间通信那么要如何在这么多计算机中,找到你想要的那个进程呢在网络中标识的唯一的计算机使用的是ip地......
  • 有不要钱的云服务器吗?推荐几款免费的云服务器
    确实存在不要钱(免费)的云服务器,这些服务通常由各大云服务提供商推出,旨在让用户免费体验其云计算产品。以下是一些值得推荐的免费云服务器:免费云服务器汇总:https://txy.ink/fwq/(实时更新建议收藏)一、雨云免费云服务器活动地址:点此直达雨云推出了云产品免费兑换活动,每......
  • UEFI原理与编程(二)
    系统表对UEFI应用程序和驱动程序开发人员来讲,系统表是最重要的数据结构之一,它是用户空间通往内核空间的通道。有了它,UEFI应用程序和驱动才可以访问UEFI内核、硬件资源和I/O设备。1在应用程序和驱动中访问系统表计算机系统进入DXE阶段后系统表被初始化,因而系统表只能用于DXE......
  • 云服务器有必要安装clamav吗
    ClamAV是一个开源的病毒扫描工具,主要用于检测恶意软件、病毒、特洛伊木马和其他恶意威胁。对于云服务器来说,是否安装ClamAV取决于多种因素:以下情况建议安装ClamAV:数据安全性要求高:如果你的服务器上存储或处理的数据对安全性有较高要求,安装ClamAV可以帮助你定期扫描和检测潜在的恶意......
  • Rust 助力无服务器构筑云计算新引擎
    引言今年AmazonLambda迎来了它的第一个十周年。在过去的十年里,无服务器架构改变了软件开发的方式,简化了应用程序的部署和扩展,成为云计算的新引擎。而在众多支持无服务器技术的编程语言中,Rust以其卓越的安全性和高性能成为了开发者的宠儿。在这篇博客中,我们将探讨Rust如何在......
  • 搭建加速服务器需要注意什么
    搭建加速服务器,无论是用于游戏、网站内容分发、文件下载加速还是其他用途,都需要注意以下几个方面:硬件和配置处理器:选择性能强劲的CPU,尤其是对于需要处理大量计算的应用。内存:确保有足够的RAM来处理高并发请求。存储:使用高速硬盘(如SSD),以提高数据读写速度。网络接口:具备足够带宽和低......
  • 【PHP编程】PHP闭包函数及函数回调的实现方式
    https://mp.weixin.qq.com/s/ji6hofCun7w7ErRvLuAiuA原创PHP星编程经验共享2023年10月19日08:01广东1.匿名函数在了解回调函数之前我们来了解一下什么是匿名函数?顾名思义,匿名函数就是一个没有确定函数名的函数,PHP将匿名函数和闭包视作相同的概念,所以匿名函数在PHP中......
  • 影视网站需要服务器多大硬盘
    影视网站对服务器硬盘空间的需求取决于多种因素,包括存储的视频数量、视频的分辨率和时长、网站流量以及未来可能的扩展需求。以下是一些参考点:视频文件大小标清视频(如480P):单个视频文件可能从几十MB到几百MB不等。高清视频(如720P、1080P):单个视频文件大小通常在1GB到几个GB之间。超清......