百万并发服务器

简介: 百万并发服务器

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

测试结果

 

目录
相关文章
|
10天前
|
网络协议
端口最多只有65535个,为什么服务器能承受百万并发
服务器通过四元组(源IP、源端口、目标IP、目标端口)识别不同TCP连接,每条连接对应独立socket。数据包携带四元组信息,服务端据此查找对应socket进行通信。只要四元组任一元素不同,即视为新连接,可创建独立socket。资源充足时,单进程可支持百万级并发连接,socket与端口非一一对应。
51 10
端口最多只有65535个,为什么服务器能承受百万并发
|
弹性计算
阿里云3M带宽云服务器并发多大?阿里云3M带宽云服务器测评参考
在探讨云服务器3M带宽能支持多大并发这一问题时,我们首先要明白一个关键点:并发量并非仅由带宽决定,还与网站本身的大小密切相关。一般来说,一个优化良好的普通网站页面大小可能只有几K,为便于计算,我们可以暂且假定每个页面大小为50K。
1542 1
|
开发框架 缓存 .NET
并发请求太多,服务器崩溃了?试试使用 ASP.NET Core Web API 操作筛选器对请求进行限流
并发请求太多,服务器崩溃了?试试使用 ASP.NET Core Web API 操作筛选器对请求进行限流
448 0
|
算法 Java
并发垃圾回收算法对于大规模服务器应用的优势
并发垃圾回收算法对于大规模服务器应用的优势
|
Java
Java Socket编程与多线程:提升客户端-服务器通信的并发性能
【6月更文挑战第21天】Java网络编程中,Socket结合多线程提升并发性能,服务器对每个客户端连接启动新线程处理,如示例所示,实现每个客户端的独立操作。多线程利用多核处理器能力,避免串行等待,提升响应速度。防止死锁需减少共享资源,统一锁定顺序,使用超时和重试策略。使用synchronized、ReentrantLock等维持数据一致性。多线程带来性能提升的同时,也伴随复杂性和挑战。
300 0
|
缓存 弹性计算 数据库
阿里云2核4G服务器支持多少人在线?程序效率、并发数、内存CPU性能、公网带宽多因素
2核4G云服务器支持的在线人数取决于多种因素:应用效率、并发数、内存、CPU、带宽、数据库性能、缓存策略、CDN和OSS使用,以及用户行为和系统优化。阿里云的ECS u1实例2核4G配置,适合轻量级应用,实际并发量需结合具体业务测试。
316 0
阿里云2核4G服务器支持多少人在线?程序效率、并发数、内存CPU性能、公网带宽多因素
|
网络协议
UDP服务器的并发方案
UDP服务器的并发方案
199 0
|
25天前
|
存储 缓存 数据挖掘
阿里云目前最便宜云服务器介绍:38元、99元、199元性能,选购攻略参考
轻量应用服务器2核2G峰值200M带宽38元1年;云服务器经济型e实例2核2G3M带宽99元1年;云服务器通用算力型u1实例2核4G5M带宽199元1年。对于还未使用过阿里云服务器的用户来说,大家也不免有些疑虑,这些云服务器性能究竟如何?它们适用于哪些场景?能否满足自己的使用需求呢?接下来,本文将为您全方位介绍这几款云服务器,以供您了解及选择参考。
|
29天前
|
网络安全 云计算
如何设置阿里云轻量应用服务器镜像?
本文介绍了在阿里云轻量应用服务器上创建与配置镜像的详细步骤。镜像是一种特殊的文件系统映射,可用于快速克隆服务器配置。内容涵盖准备条件、登录控制台、创建实例、生成镜像、下载与设置镜像,以及如何使用镜像启动新实例。适合希望提升服务器部署效率的用户参考。
|
1月前
|
存储 弹性计算 安全
阿里云轻量服务器通用型、CPU优化型、多公网IP型、国际型、容量型不同实例区别与选择参考
阿里云轻量应用服务器实例类型分为通用型、CPU优化型、多公网IP型、国际型、容量型,不同规格族的适用场景和特点不同,收费标准也不一样。本文为大家介绍轻量应用服务器通用型、多公网IP型、容量型有何区别?以及选择参考。