百万并发服务器

简介: 百万并发服务器

1、tcp套接字通信

服务器端通过socket函数创建套接字,bind绑定套接字,通过listen函数接受客户端发送过来的请求,通过三次握手建立链接后,通过accept函数接受请求并返回一个能与客户端收发数据的socket描述符,后面客户端和服务器通过recv和send函数收发数据,当调用close函数后关闭链接。

       int socket(int domain, int type, int protocol);创建套接字

       int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);绑定套接字

       int listen(int sockfd, int backlog);监听请求

       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);接受请求

       ssize_t recv(int sockfd, void *buf, size_t len, int flags);接受数据

       ssize_t send(int sockfd, const void *buf, size_t len, int flags);发送数据

       int close(int fd);关闭套接字链接

客户端通过socket函数创建套接字,bind绑定套接字,通过connect函数向服务端发送链接请求,与服务端建立链接后,后面通过recv和send函数收发数据,当调用close函数后关闭链接。

       int socket(int domain, int type, int protocol);创建套接字

       int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);绑定套接字

      int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);建立链接

       ssize_t recv(int sockfd, void *buf, size_t len, int flags);接受数据

       ssize_t send(int sockfd, const void *buf, size_t len, int flags);发送数据

       int close(int fd);关闭套接字链接

网络io通信函数功能:

       1、connect函数:阻塞的,客户端通过connect函数发送连接请求,直到三次握手成功或超时失败才返回。服务端listen函数是被动连接的。可通过fcntl函数设置为非阻塞的。

       2、listen() 函数:非阻塞的,主要作用就是将传入的套接字变成被动的连接监听(被动等待客户端的连接请求),参数 backlog是设置内核中连接队列的长度。它将该套接字和套接字对应的连接队列长度告诉 Linux 内核,然后,listen()函数就结束。内核为任何一个给定的监听套接口维护两个队列:1)、半连接队列,服务端接受到客户端第一次握手请求。将该请求套接字描述符放入半连接队列中,并将该连接请求置为SYN_RCVD 状态。2)、全连接队列,服务端收到客户端第三次握手请求,创建新的套接字描述符,并将半连接队列中对应的套接字描述符删除,加入全连接队列。并将该连接请求置为 ESTABLISHED 状态。

       3、accept()函数:从全连接队列的头部取出一个已完成的连接,若全连接队列中没有已完成的链接,则accpet函数阻塞,直到全连接队列中有已完成连接并将其取出。若半连接队列或全连接队列满了,则connet建立连接超时,返回ETIMEDOUT。可通过fcntl函数设置为非阻塞的。

       4、send函数:阻塞的,从发送缓冲区取出数据并将数据发出,若发送缓冲区没有数据,则该函数阻塞。直到发送缓冲区有数据可发。可通过fcntl函数设置为非阻塞的。

       5、recv函数:阻塞的,从接收缓冲区读取数据,若接收缓冲区没有数据,则阻塞,直到接收缓冲区有数据可读。可通过fcntl函数设置为非阻塞的。

2、网络io模型

1、网络io阻塞模型:



服务器实现:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include<stdio.h>
int main()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        return -1;
    }
    int port = 9999;
    struct sockaddr_in servaddr;
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(port);
    if(bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1)
    {
        return -1;
    }
    if(listen(sockfd, 10) == -1)
    {
        return -1;
    }
    struct sockaddr_in client;
    socklen_t clilen = sizeof(client);
    int connfd = accept(sockfd, (struct sockaddr*)&client, &clilen);
    if(connfd == -1)
    {
        printf("accept error\n");
        return -1;
    }
    char rbuffer[100] = {0};
    int rlen = recv(connfd, rbuffer, sizeof(rbuffer),0);
    if(rlen <= 0)
    {
        printf("recv error\n");
        return -1;
    }
    printf("recv from client: %s\n", rbuffer);
    char wbuffer[100] = "data from server";
    int wlen = send(connfd, wbuffer, sizeof(wbuffer), 0);
    if(wlen <= 0)
    {
        return -1;
    }
    printf("send to client: %s\n", wbuffer);
    close(sockfd);
    close(connfd);
    return 0;
}

客户端实现:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        printf("usage: ip port\n");
        return -1;
    }
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        return -1;
    }
    int servport = atoi(argv[2]);
    struct sockaddr_in servaddr;
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(argv[1]);
    servaddr.sin_port = htons(servport);
    socklen_t servlen = sizeof(servaddr);
    if(connect(sockfd, (struct sockaddr*)&servaddr, servlen) == -1)
    {
        return -1;
    }
    char wbuffer[100] = "data from client";
    int wlen = send(sockfd, wbuffer, sizeof(wbuffer), 0);
    if(wlen <= 0)
    {
        return -1;
    }
    printf("send to server: %s\n", wbuffer);
    char rbuffer[100] = {0};
    int rlen = recv(sockfd, rbuffer, sizeof(rbuffer),0);
    if(rlen <= 0)
    {
        return -1;
    }
    printf("recv from server: %s\n", rbuffer);
    close(sockfd);
    return 0;
}

2、网络io非阻塞模型:

通过fcntl函数设置为非阻塞的。在缓冲区数据没有就绪的情况下,read函数立即返回(accpet、recv等函数原理相同)。

3、IO多路复用:

可通过内核提供的select、epoll实现,这种方式也称为事件驱动IO,IO多路复用顾名思义,就是单个 process 就可以同时处理多个网络连接的 IO,它的基本原理就是 select/epoll 这个 function 会不断的轮询所负责的所有 socket,当某个 socket 有数据到达了,就通知用户进程。

epoll是linux内核实现的一种模型,它给应用程序提供的关键接口如下:

                       int epoll_create(int size);创建一个epoll实例并返回操作epoll实例的文件描述符,从linux 2.6.8 size参数被忽略,但是必须比0大。

                       int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);该系统调用用于添加、修改或移除在epoll实例中被epfd文件描述符提及的fd条目,它请求的op操作为设置目标fd而被执行,event参数描述关联到fd文件描述符的对象。

                       int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);该系统调用等待文件描述符epfd涉及的事件,events用于存储准备就绪的事件,上限为maxevents,被epoll_wait返回,maxevents参数必须大于0。

epoll模型总结:

epoll模型实现并发服务器

服务端实现


#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include<stdio.h>
#include <sys/epoll.h>
#define AVAILABLE_EVENTS 1024
int main()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        return -1;
    }
    int port = 9999;
    struct sockaddr_in servaddr;
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(port);
    if(bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1)
    {
        return -1;
    }
    if(listen(sockfd, 10) == -1)
    {
        return -1;
    }
    int epofd = epoll_create(1);
    if(epofd == -1)
    {
        return -1;
    }
    struct epoll_event event;
    event.data.fd = sockfd;
    event.events = EPOLLIN;
    if(epoll_ctl(epofd, EPOLL_CTL_ADD, sockfd, &event) == -1)
    {
        return -1;
    }
    struct epoll_event evebuffer[AVAILABLE_EVENTS];
    while(1)
    {
        int epowaifd = epoll_wait(epofd, evebuffer, AVAILABLE_EVENTS, 0);
        if(epowaifd == -1)
        {
            return -1;
        }
        for(int i=0 ; i<epowaifd ; i++)
        {
            printf("epowaifd==%d\n", epowaifd);
            if(sockfd == evebuffer[i].data.fd)
            {
                struct sockaddr_in client;
                socklen_t clilen = sizeof(client);
                int connfd = accept(sockfd, (struct sockaddr*)&client, &clilen);
                if(connfd == -1)
                {
                    printf("accept error\n");
                    return -1;
                }
                event.data.fd = connfd;
                event.events = EPOLLIN;
                if(epoll_ctl(epofd, EPOLL_CTL_ADD, connfd, &event) == -1)
                {
                    return -1;
                }
                printf("accept seccess\n");
            }
            else if(evebuffer[i].events == EPOLLIN)
            {
                char rbuffer[100] = {0};
                int rlen = recv(evebuffer[i].data.fd, rbuffer, sizeof(rbuffer),0);
                if(rlen <= 0)
                {
                    printf("recv error\n");
                    return -1;
                }
                printf("recv:%s\n", rbuffer);
                event.data.fd = evebuffer[i].data.fd;
                event.events = EPOLLOUT;
                if(epoll_ctl(epofd, EPOLL_CTL_MOD, evebuffer[i].data.fd, &event) == -1)
                {
                    return -1;
                }
                printf("recv seccess\n");
            }
            else if(evebuffer[i].events == EPOLLOUT)
            {
                char wbuffer[100] = "server data";
                int wlen = send(evebuffer[i].data.fd, wbuffer, sizeof(wbuffer), 0);
                if(wlen <= 0)
                {
                    printf("send error\n");
                    return -1;
                }
                printf("send:%s\n", wbuffer);
                event.data.fd = evebuffer[i].data.fd;
                event.events = EPOLLIN;
                if(epoll_ctl(epofd, EPOLL_CTL_MOD, evebuffer[i].data.fd, &event) == -1)
                {
                    return -1;
                }
                printf("send seccess\n");
            }
        }
    }
    close(sockfd);
    return 0;
}

测试结果

 

目录
相关文章
|
4月前
|
开发框架 缓存 .NET
并发请求太多,服务器崩溃了?试试使用 ASP.NET Core Web API 操作筛选器对请求进行限流
并发请求太多,服务器崩溃了?试试使用 ASP.NET Core Web API 操作筛选器对请求进行限流
232 0
|
7月前
|
弹性计算
阿里云3M带宽云服务器并发多大?阿里云3M带宽云服务器测评参考
在探讨云服务器3M带宽能支持多大并发这一问题时,我们首先要明白一个关键点:并发量并非仅由带宽决定,还与网站本身的大小密切相关。一般来说,一个优化良好的普通网站页面大小可能只有几K,为便于计算,我们可以暂且假定每个页面大小为50K。
1283 1
|
7月前
|
算法 Java
并发垃圾回收算法对于大规模服务器应用的优势
并发垃圾回收算法对于大规模服务器应用的优势
|
6月前
|
Java
Java Socket编程与多线程:提升客户端-服务器通信的并发性能
【6月更文挑战第21天】Java网络编程中,Socket结合多线程提升并发性能,服务器对每个客户端连接启动新线程处理,如示例所示,实现每个客户端的独立操作。多线程利用多核处理器能力,避免串行等待,提升响应速度。防止死锁需减少共享资源,统一锁定顺序,使用超时和重试策略。使用synchronized、ReentrantLock等维持数据一致性。多线程带来性能提升的同时,也伴随复杂性和挑战。
114 0
|
5月前
|
缓存 弹性计算 数据库
阿里云2核4G服务器支持多少人在线?程序效率、并发数、内存CPU性能、公网带宽多因素
2核4G云服务器支持的在线人数取决于多种因素:应用效率、并发数、内存、CPU、带宽、数据库性能、缓存策略、CDN和OSS使用,以及用户行为和系统优化。阿里云的ECS u1实例2核4G配置,适合轻量级应用,实际并发量需结合具体业务测试。
99 0
阿里云2核4G服务器支持多少人在线?程序效率、并发数、内存CPU性能、公网带宽多因素
|
6月前
|
网络协议
UDP服务器的并发方案
UDP服务器的并发方案
84 0
|
7月前
|
存储 弹性计算 云计算
9M带宽的阿里云服务器支持多少用户并发访问?阿里云9M带宽服务器测评
随着云计算技术的飞速进步与日益完善,云服务器已经逐渐成为了众多企业与个人的首选服务器类型。它以其出色的弹性扩展、高可用性以及灵活的管理方式,赢得了广大用户的青睐。那么,对于一款拥有9M带宽的云服务器来说,到了2024年,它究竟能够支持多少用户进行并发访问呢?这无疑是许多准备使用云服务的用户非常关心的问题。
238 0
|
1天前
|
人工智能 JSON Linux
利用阿里云GPU加速服务器实现pdf转换为markdown格式
随着AI模型的发展,GPU需求日益增长,尤其是个人学习和研究。直接购置硬件成本高且更新快,建议选择阿里云等提供的GPU加速型服务器。
利用阿里云GPU加速服务器实现pdf转换为markdown格式
|
8天前
|
弹性计算 运维 安全
阿里云轻量应用服务器与ECS的区别及选择指南
轻量应用服务器和云服务器ECS(Elastic Compute Service)是两款颇受欢迎的产品。本文将对这两者进行详细的对比,帮助用户更好地理解它们之间的区别,并根据自身需求做出明智的选择。
|
10天前
|
SQL 弹性计算 安全
阿里云上云优选与飞天加速计划活动区别及购买云服务器后续必做功课参考
对于很多用户来说,购买云服务器通常都是通过阿里云当下的各种活动来购买,这就有必要了解这些活动的区别,同时由于活动内的云服务器购买之后还需要单独购买并挂载数据盘,还需要设置远程密码以及安全组等操作之后才能正常使用云服务器。本文就为大家介绍一下目前比较热门的上云优选与飞天加速计划两个活动的区别,以及通过活动来购买云服务器之后的一些必做功课,确保云服务器可以正常使用,以供参考。