百万并发服务器

简介: 百万并发服务器

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;
}

测试结果

 

目录
相关文章
|
2月前
|
弹性计算
阿里云3M带宽云服务器并发多大?阿里云3M带宽云服务器测评参考
在探讨云服务器3M带宽能支持多大并发这一问题时,我们首先要明白一个关键点:并发量并非仅由带宽决定,还与网站本身的大小密切相关。一般来说,一个优化良好的普通网站页面大小可能只有几K,为便于计算,我们可以暂且假定每个页面大小为50K。
842 1
|
2月前
|
存储 弹性计算 云计算
9M带宽的阿里云服务器支持多少用户并发访问?阿里云9M带宽服务器测评
随着云计算技术的飞速进步与日益完善,云服务器已经逐渐成为了众多企业与个人的首选服务器类型。它以其出色的弹性扩展、高可用性以及灵活的管理方式,赢得了广大用户的青睐。那么,对于一款拥有9M带宽的云服务器来说,到了2024年,它究竟能够支持多少用户进行并发访问呢?这无疑是许多准备使用云服务的用户非常关心的问题。
150 0
|
2月前
|
弹性计算 缓存 测试技术
2核4g服务器能支持多少人访问?阿里云2核4G服务器并发数测试
2核4g服务器能支持多少人访问?阿里云2核4G服务器并发数测试,2核4G服务器并发数性能测试,阿小云账号下的2核4G服务器支持20人同时在线访问,然而应用不同、类型不同、程序效率不同实际并发数也不同,2核4G服务器的在线访问人数取决于多个变量因素
|
2月前
|
弹性计算 缓存 测试技术
云服务器2核4G能支持多少人同时访问?2核4G5M并发量评测!
阿里云2核4g服务器能支持多少人访问?2核4G服务器并发数性能测试,阿小云账号下的2核4G服务器支持20人同时在线访问,然而应用不同、类型不同、程序效率不同实际并发数也不同,2核4G服务器的在线访问人数取决于多个变量
|
2月前
|
弹性计算 大数据 测试技术
阿里云8核16G云服务器并发承载量多少?2024年阿里云8核16G云服务器测评
阿里云8核16G云服务器采用了高性能的处理器和大容量内存,具备强大的计算能力和内存带宽,可以满足多个应用程序的同时运行和访问需求。阿里云8核16G云服务器的并发承载量同样受到多种因素的影响,如服务器配置、网络环境、应用程序的架构和优化等。选择云服务器时,除了考虑服务器的性能表现,还需要考虑其他因素,如云服务提供商的服务质量、技术支持、价格等。因此,建议在购买前进行充分的调研和测试,选择最适合自己需求的云服务器。
|
3月前
|
存储 负载均衡 监控
epoll服务器百万并发测试
epoll服务器百万并发测试
31 1
|
4月前
|
网络协议 Linux 网络安全
Linux服务器百万并发实现与问题排查
Linux服务器百万并发实现与问题排查
49 0
|
1天前
|
负载均衡 固态存储 Linux
阿里云轻量应用服务器、云服务器、gpu云服务器最新收费标准参考
轻量应用服务器、云服务器、gpu云服务器是阿里云服务器产品中,比较热门的云服务器产品类型,不同类型的云服务器产品收费模式与收费标准是不一样的,本文为大家展示这几个云服务器产品的最新收费标准情况,以供参考。
阿里云轻量应用服务器、云服务器、gpu云服务器最新收费标准参考
|
1天前
|
弹性计算 负载均衡 容灾
应用阿里云弹性计算:打造高可用性云服务器ECS架构
阿里云弹性计算助力构建高可用云服务器ECS架构,通过实例分布、负载均衡、弹性IP、数据备份及多可用区部署,确保业务连续稳定。自动容错和迁移功能进一步增强容灾能力,提供全方位高可用保障。
6 0
|
1天前
|
存储 弹性计算 监控
探索阿里云弹性计算:如何优化云服务器ECS的性能与成本
在云时代,【阿里云ECS】的性能优化与成本控制至关重要。利用实例规格选择、自动伸缩、网络和存储配置,可增强性能、减少成本。结合监控工具和优化建议,用户能解决性能问题,提升应用稳定性,实现高效且经济的云计算运营。
7 1