>  기사  >  백엔드 개발  >  C/C++ 코루틴의 어셈블리 기반 구현(서버용)

C/C++ 코루틴의 어셈블리 기반 구현(서버용)

php是最好的语言
php是最好的语言원래의
2018-08-02 16:09:002585검색

이 글은 C/C++ 코루틴을 구현한 것입니다. 우리는 다음 두 가지 목표를 달성해야 합니다.

  1. 기능 설계 및 코드 디버깅을 용이하게 하는 동기식 서버 프로그래밍에 대한 순차적 아이디어를 갖습니다. - libco에서 코루틴 부분을 사용했습니다.

  2. 비동기 I/ O ——libevent apache php mysql

에서 이벤트 I/O를 사용했습니다. 구조적으로 libco와 libevent의 기능을 결합했기 때문에 프로젝트 이름을 libevent 기반이라는 뜻의 libcoevent로 명명했습니다. 동기식 코루틴 서버 프로그래밍 프레임워크입니다. " 이름에 있는 co라는 단어는 libco가 아니라 코루틴을 의미합니다.

프로그래밍 언어 측면에서 C++를 선택한 이유는 libco가 x86 또는 x64 아키텍처 기반의 Linux만 지원하고 이러한 아키텍처는 기본적으로 충분한 리소스와 좋은 성능을 갖춘 PC 또는 임베디드 시스템이기 때문입니다. C++를 실행 중입니다. 이 문서에서는 코드가 어떻게 구현되는지 설명합니다.

본 프로젝트를 사용하시려면 링크 옵션에 -lco -levent -lcoevent 3가지 옵션을 추가해주세요. -lco -levent -lcoevent 三个选项。

类关系及基本功能

类关系

类继承关系

类的基本继承关系图如下:

C/C++ 코루틴의 어셈블리 기반 구현(서버용)

在实际调用中,只有处于继承关系树的叶子结点上的类才会被实际使用到,其他类均视为虚类。

类从属关系

各类的实例在程序运行中是有从属关系的,除了作为顶层的 Base 类之外,其他树叶类都需依附于其他的类所在的运行环境中才能执行。从属关系图如下:

C/C++ 코루틴의 어셈블리 기반 구현(서버용)

  • Base 类提供最基本的运行环境,并管理 Server 对象;

  • Procedure 对象管理 Client 对象。在图中体现为 ServerSession 对象均管理 Client 对象。

    • Server 对象由应用程序创建并初始化到 Base 对象中运行。当服务器结束或当其从属的 Base 对象销毁时,可配置自动销毁 Server 对象。

    • Session 对象由处于会话模式(session mode)的 Server 对象自动创建,并调用应用程序指定的程序入口运行;当会话结束时(函数调用 return)或其从属的 Server 对象服务结束时,由 Server 对象自动销毁。

  • Client 对象由应用程序调用 Procedure 对象的接口创建,用于与第三方服务交互。应用程序可提前调用接口要求销毁 Client 对象,也可以待 Procedure 服务结束时自动统一销毁。

Base 和 Event 类

C/C++ 코루틴의 어셈블리 기반 구현(서버용)

Base 类用于运行 libcoevent 的各个服务。每个 Base 类的实例应对应着一个线程,所有的服务以协程的方式在 Base 实例中运行。从上图可知,Base 类包含一个 libevent 库的 event_base 对象和本协程库的一系列 Event 对象。

C/C++ 코루틴의 어셈블리 기반 구현(서버용)

Event 类其实是借用了 libevent 的 struct event 名称,因为每一个 Event 类的实例,对应着 libevent 的一个 event

클래스 관계 및 기본 기능

클래스 관계클래스 상속 관계클래스의 기본 상속 관계 다이어그램은 다음과 같습니다.

C/C++ 코루틴의 어셈블리 기반 구현(서버용)

실제 호출에서는 상속의 리프 노드에 있는 클래스만 관계 트리는 실제로 사용될 때 다른 클래스도 가상 클래스로 간주됩니다.

클래스 소속
  1. 다양한 유형의 인스턴스는 프로그램 실행 중에 소속을 갖습니다. 최상위 기본 클래스를 제외하고 다른 리프 클래스는 실행되기 위해 다른 클래스의 실행 환경에 종속되어야 합니다. 소속 다이어그램은 다음과 같습니다.

    2 .png
  • Base 클래스는 가장 기본적인 실행 환경을 제공하고

    Server
  • 객체를 관리하고,

    Procedure 객체 관리 Client 객체를 관리합니다. .

    Server

    Session

    개체가 모두 Client 개체를 관리한다는 사실이 그림에 반영되어 있습니다.

    🎜
      🎜🎜🎜Server🎜 객체는 애플리케이션에 의해 생성되고 🎜Base🎜 객체에서 실행되도록 초기화됩니다. 🎜Server🎜 개체는 서버가 종료되거나 해당 종속 🎜Base🎜 개체가 삭제될 때 자동으로 삭제되도록 구성할 수 있습니다. 🎜🎜🎜🎜🎜Session🎜 개체는 세션 모드에서 🎜Server🎜 개체에 의해 자동으로 생성되며, 세션이 종료되면 응용 프로그램에서 지정한 프로그램 항목을 호출하여 실행됩니다(함수 호출 return). ) 또는 하위 🎜Server🎜 개체 서비스가 🎜Server🎜 개체에 의해 자동으로 삭제됩니다. 🎜🎜
    🎜🎜🎜Client🎜 개체는 🎜Procedure🎜 개체의 인터페이스를 호출하는 응용 프로그램에 의해 생성되며 타사 서비스와 상호 작용하는 데 사용됩니다. 애플리케이션은 미리 인터페이스를 호출하여 🎜Client🎜 개체 삭제를 요청하거나 🎜Procedure🎜 서비스가 종료되면 자동으로 해당 개체를 삭제할 수 있습니다. 🎜🎜
🎜기본 및 이벤트 클래스🎜🎜C/C++ 코루틴의 어셈블리 기반 구현(서버용)🎜🎜🎜Base🎜 클래스는 libcoevent의 다양한 서비스를 실행하는 데 사용됩니다. Base 클래스의 각 인스턴스는 스레드에 대응해야 하며 모든 서비스는 🎜Base🎜 인스턴스에서 코루틴 방식으로 실행됩니다. 위 그림에서 볼 수 있듯이 🎜Base🎜 클래스에는 libevent 라이브러리의 event_base 개체와 이 코루틴 라이브러리의 일련의 Event 개체가 포함되어 있습니다. 🎜🎜C/C++ 코루틴의 어셈블리 기반 구현(서버용)🎜🎜 🎜Event🎜 클래스는 실제로 libevent에서 struct event 이름을 차용합니다. 🎜Event🎜 클래스의 각 인스턴스가 libevent의 event 개체에 해당하기 때문입니다. 우리가 집중해야 할 초점은 🎜Procedure🎜 및 🎜Client🎜 클래스입니다. 🎜🎜Procedure 클래스🎜🎜🎜Procedure🎜 클래스에는 두 가지 주요 기능이 있습니다. 🎜🎜🎜🎜각 개체에는 자체 독립적인 컨텍스트 정보가 있고 독립적인 서버 프로세스(프로시저)를 작성하는 데 사용할 수 있는 libco 코루틴이 있습니다. 🎜Procesure 하위 클래스는 🎜Client🎜 개체를 생성하여 타사 서버와 통신하고 상호 작용할 수 있습니다. 🎜🎜🎜🎜🎜Procedure🎜 클래스에는 🎜Server🎜 및 🎜Session🎜이라는 두 개의 하위 클래스가 있습니다. 🎜🎜Server 클래스 🎜🎜Server 클래스는 🎜Base🎜 개체에서 실행되는 응용 프로그램에 의해 생성되고 초기화됩니다. 서버 클래스에는 세 가지 하위 클래스가 있습니다. 🎜
  • SubRoutine: 실제로 서버 프로그램의 역할을 하지는 않지만, 가장 기본적인 sleep() 기능을 제공하고 이를 지원합니다. Procedure 클래스에는 Client 객체를 생성하는 기능이 있으므로 애플리케이션을 임시로 생성하거나 상주하는 내부 프로그램으로 사용할 수 있습니다. sleep() 函数,并支持 Procedure 类的创建 Client 对象的功能,因此应用程序可以用来作为临时创建或常驻的内部程序来使用。

  • UDPServer:应用程序创建并初始化 UDPServer 对象后,程序会自动绑定到一个数据报 socket 接口上。应用可以通过在网络接口中收发数据包来实现网络服务。UDPServer 同时提供普通模式会话模式

  • TCPServer:应用程序创建并初始化 TCPPServer 对象后,程序会自动绑定并监听流 socket。TCPServer 只支持会话模式

所谓的 “普通模式”,也就是应用程序注册 Server 对象的入口函数,并且由应用程序操作 Server 对象的行为。

所谓的 “会话模式”,指的是 UDPServer 或 TCPServer 对象,在接收到传入数据后,自动区分客户端,并单独创建 Session 对象进行处理。每个 Session 对象只服务于一个客户端。

Session 类

Session 对象不能由应用主动创建,而是由处于会话模式的 Server 类自动按需创建。Session 对象的特点是,只能与单一一个客户端(相比起 UDPServer 对象而言)进行通信,因此没有 send() 函数,只有 reply()

在头文件 coevent.h 声明的 Session 类及其子类均为纯虚类,目的是防止应用程序显式地构建 Session 对象并隐藏实现细节。

Client 类

Client 对象由 Procedure 对象创建,并且由 Procedure 对象进行回收。Client 对象的作用是主动向远程服务器发起通信。由于从客户-服务结构的角度,这个动作属于客户端,所以命名为 Client。

DNSClient

Client 的子类中比较特别的是 DNSClient 类,这个类的存在是为了解决在异步 I/O 中的 getaddrinfo() 阻塞问题。DNSClient 的实现原理请参见代码和我之前的文章《DNS 报文结构和个人 DNS 解析代码实现》。

而对于 DNSClient 类而言,具体实现原理,就是封装了一个 UDPClient 对象,通过该对象完成 DNS 报文的收发,并在类中实现报文的解析。

UDPServer——基于 libevent 的协程实现

UDPServer 类普通模式的原理,就是一个非常典型的基于 libevent 的同步协程服务器框架。其代码实现中,核心功能就是以下几个函数:

  • _libco_routine(),协程的入口函数,使用这个函数,转化成为 liboevent 的统一服务入口函数

  • _libevent_callback()libevent 时间回调函数,在这个函数里,实现协程上下文的恢复。

  • UDPServer::recv_in_timeval(),数据接收函数,在这个函数中,实现关键的数据等待功能,同时实现了协程上下文的保存

上述三个函数的代码总量,加上空行也不超过 200 行,我相信还是很容易看明白的。以下具体解释实现原理:

libco 协程接口

正如前文所说,我使用的是 libco 作为协程库。协程对于应用程序是透明的,但是对于库的实现而言,这才是核心。

下面解释一下 libco 的协程功能所提供的几个接口(libco 的文档数量简直 “感人”,这也是网上经常被吐槽的……):

创建和销毁协程

Libco 使用结构体 struct stCoRoutine_t * 保存协程,通过调用 co_create() 可以创建协程对象;使用 co_release() 销毁协程资源。

进入协程

创建了协程之后,调用 co_resume() 可以从协程函数的开头开始执行协程。

暂停协程

当协程到了需要交出 CPU 使用权的时候,可以调用 co_yield() 释放协程、切换掉上下文。调用之后,上下文会恢复到上一个调用 co_resume() 的协程中。调用 co_yield() 的位置可以视为一个 “断点”。

恢复协程

恢复协程和创建协程所用的函数都是 co_resume()

UDPServer#🎜🎜#: 애플리케이션이 UDPServer 객체를 생성하고 초기화한 후 프로그램은 자동으로 데이터그램 소켓 인터페이스에 바인딩됩니다. 애플리케이션은 네트워크 인터페이스에서 데이터 패킷을 보내고 받음으로써 네트워크 서비스를 구현할 수 있습니다. UDPServer는 #🎜🎜#Normal Mode#🎜🎜# 및 #🎜🎜#Session Mode#🎜🎜#을 모두 제공합니다. #🎜🎜##🎜🎜##🎜🎜##🎜🎜#TCPServer#🎜🎜#: 애플리케이션이 TCPPServer 객체를 생성하고 초기화한 후 프로그램은 자동으로 스트림 소켓을 바인딩하고 수신합니다. TCPServer는 #🎜🎜#세션 모드#🎜🎜#만 지원합니다. #🎜🎜##🎜🎜#일명 "#🎜🎜#Normal mode#🎜🎜#"은 애플리케이션이 Server 객체를 등록하기 위한 진입 기능이며, 애플리케이션이 이를 동작시키는 기능이다. 서버 개체 동작. #🎜🎜##🎜🎜#일명 "#🎜🎜#Session Mode#🎜🎜#"은 UDPServer 또는 TCPServer 객체를 의미하며 들어오는 데이터를 받은 후 자동으로 클라이언트를 구별하여 별도로 생성합니다#🎜🎜 #Session#🎜🎜# 처리할 개체입니다. 각 세션 개체는 하나의 클라이언트에만 서비스를 제공합니다. #🎜🎜#

세션 클래스

#🎜🎜#Session 개체는 애플리케이션에서 직접 생성할 수 없지만 세션 모드에서 서버 클래스에 의해 요청 시 자동으로 생성됩니다. Session 개체의 특징은 (UDPServer 개체에 비해) 단일 클라이언트와만 통신할 수 있다는 점이므로 send() 함수는 없고 reply()만 있습니다. > . #🎜🎜##🎜🎜#헤더 파일 coevent.h에 선언된 #🎜🎜#Session#🎜🎜# 클래스와 해당 하위 클래스는 순수 가상 클래스로, 애플리케이션이 명시적으로 세션을 종료하는 것을 방지하기 위한 것입니다. #🎜🎜#Session#🎜🎜# 개체를 독립적으로 구성하고 구현 세부 정보를 숨깁니다. #🎜🎜#

클라이언트 클래스

#🎜🎜##🎜🎜#Client#🎜🎜# 객체는 Procedure 객체에 의해 생성되고 Procedure 객체에 의해 재활용됩니다. 클라이언트 개체의 역할은 원격 서버와의 통신을 적극적으로 시작하는 것입니다. 이 작업은 클라이언트-서비스 구조의 관점에서 클라이언트에 속하므로 이름이 클라이언트입니다. #🎜🎜#

DNSClient

#🎜🎜#클라이언트의 가장 특별한 하위 클래스는 #🎜🎜#DNSClient#🎜🎜# 클래스입니다. 이 클래스는 비동기 I/O 의 문제를 해결하기 위해 존재합니다. getaddrinfo() 차단 문제. DNSClient의 구현 원리는 코드와 이전 기사 "#🎜🎜#DNS#🎜🎜# 메시지 구조 및 개인 #🎜🎜#DNS#🎜🎜# 구문 분석 코드 구현"을 참조하세요. #🎜🎜##🎜🎜#DNSClient 클래스의 경우 구체적인 구현 원칙은 UDPClient 개체를 캡슐화하고 이 개체를 사용하여 DNS 메시지 전송 및 수신을 완료하고 클래스에서 메시지 구문 분석을 구현하는 것입니다. #🎜🎜#

UDPServer - libevent 기반 코루틴 구현

#🎜🎜##🎜🎜#UDPServer#🎜🎜# 일반 모드의 원리는 libevent 서버 프레임워크 기반의 매우 일반적인 동기식 코루틴입니다. 코드 구현 시 핵심 기능은 다음과 같습니다. 코루틴의 진입 기능인 #🎜🎜##🎜🎜##🎜🎜##🎜🎜#_libco_routine()은 이 기능을 사용하세요. , liboevent #🎜🎜##🎜🎜##🎜🎜#_libevent_callback()의 통합 서비스 진입 함수로 변환, #🎜🎜#libevent#🎜🎜# 시간 콜백 함수, in this 함수에서 코루틴 컨텍스트가 복원됩니다. #🎜🎜##🎜🎜##🎜🎜#UDPServer::recv_in_timeval(), 데이터 수신 기능, 이 기능에는 핵심 데이터 대기 기능이 구현되어 있으며 동시에 시간, 조정 프로세스 컨텍스트 저장#🎜🎜##🎜🎜#위 세 가지 기능에 대한 전체 코드 양은 빈 줄을 포함해도 200줄을 넘지 않는 것으로 생각됩니다. 이해하기 쉽다. 구현 원리는 아래에 자세히 설명되어 있습니다. #🎜🎜#

libco 코루틴 인터페이스

#🎜🎜#앞서 언급했듯이 libco를 코루틴 라이브러리로 사용합니다. 코루틴은 애플리케이션에 투명하지만 라이브러리 구현에는 이것이 핵심입니다. #🎜🎜##🎜🎜#다음은 libco의 코루틴 함수가 제공하는 여러 인터페이스에 대해 설명합니다(libco의 문서 수는 단순히 "터치"에 불과하며 이는 인터넷에서 종종 불평됩니다...): #🎜🎜#

생성 및 코루틴 삭제

#🎜🎜##🎜🎜#Libco#🎜🎜# co_create() 코루틴을 호출하여 코루틴을 저장하려면 struct stCoRoutine_t * 구조를 사용하세요. 객체를 생성할 수 있습니다. co_release()를 사용하여 코루틴 리소스를 제거하세요. #🎜🎜#

코루틴 입력

#🎜🎜#코루틴을 생성한 후 co_resume()을 호출하여 코루틴 함수 시작 부분부터 코루틴 실행을 시작합니다. #🎜🎜#

코루틴 일시 중지

#🎜🎜#코루틴이 CPU 사용 권한을 넘겨야 하는 경우 co_yield()를 호출하여 코루틴을 해제하고 문맥 . 호출 후 컨텍스트는 co_resume()을 호출한 마지막 코루틴으로 복원됩니다. co_yield()가 호출되는 위치는 "#🎜🎜#breakpoint#🎜🎜#"이라고 볼 수 있습니다. #🎜🎜#

코루틴 재개

#🎜🎜#코루틴을 복원하고 코루틴을 생성하는 데 사용되는 함수는 모두 co_resume()입니다. 현재 스택을 전환하려면 이 함수를 호출하세요. 코루틴의 컨텍스트를 지정하면 코루틴은 위에서 언급한 "#🎜🎜#breakpoint#🎜🎜#"부터 실행을 재개합니다. #🎜🎜#

코루틴 스케줄링 구현

이전 섹션에서 볼 수 있듯이 우리가 사용하는 libco 코루틴 함수 함수에는 코루틴 전환 기능이 포함되어 있지만 전환 시기와 전환 후 CPU 할당 방법은 이것이 필요합니다. 구현하고 캡슐화합니다. 일.

코루틴을 생성하고 파괴하는 시간은 자연스럽게 UDPServer 클래스가 초기화되고 파괴될 때입니다. 다음은 코루틴 시작, 일시 중지 및 재개 작업을 분석하는 데 중점을 둡니다.

코루틴 시작

코루틴 시작/재개 코드는 _libevent_callback()에 있습니다. _libevent_callback() 中,有这么一行:

// handle control to user application
co_resume(arg->coroutine);

如果当前协程还没有被执行过,那么执行了这句代码之后,程序会切换到创建 libco 协程时指定的协程函数开始执行。对于 UDPServer,也就是 _libco_routine() 函数。这个函数非常简单,只有三行:

static void *_libco_routine(void *libco_arg)
{
    struct _EventArg *arg = (struct _EventArg *)libco_arg;
    (arg->worker_func)(arg->fd, arg->event, arg->user_arg);
    return NULL;
}

通过传入参数,将 libco 回调函数转换为应用程序指定的服务器函数执行。

但是如何实现第一次的 libevent 回调呢?这还是很简单的,只需要在调用 libevent 的 event_add()时,将超时时间设置为 0 即可,这会导致 libevent 事件立即超时。通过这个机制,我们也就实现了在 Base 运行之后立即执行各 Procedure 服务函数的目的。

暂停和恢复协程

在什么时候调用 co_yield是本协程实现的重点,调用 co_yield 的位置,是一个可能会导致上下文切换的地方,也是将异步编程框架转换为同步框架的关键技术点。这里可以参照 UDPServerrecv_in_timeval() 函数。函数的基本逻辑如下:

C/C++ 코루틴의 어셈블리 기반 구현(서버용)

其中最重要的分支,就是对 libevent 事件标志的判断;而最重要的逻辑,就是 event_add()co_yield() 函数的调用。函数片段如下:

struct timeval timeout_copy;
timeout_copy.tv_sec = timeout.tv_sec;
timeout_copy.tv_usec = timeout.tv_usec;
    ...
event_add(_event, &timeout_copy);
co_yield(arg->coroutine);

这里,我们把 co_yield() 函数理解为一个断点,当程序执行到这里的时候,CPU 的使用权会被交出,程序回到调用 co_resume() 的上一级函数手中。这个 “上一级函数” 究竟是哪里呢?实际上就是前文提到的 _libevent_callback() 函数。

_libevent_callback() 的角度来看,程序会从 co_resume() 函数返回,并且继续往下执行。此时我们可以这么理解:协程的调度,实际上是借用了 libevent来进行的。这里我们要关注一下 co_resume() 上方的几句:

// switch into the coroutine
if (arg->libevent_what_ptr) {
    *(arg->libevent_what_ptr) = (uint32_t)what;
}

这里将 libevent 事件 flag 值传递给了协程,而这是前文进行事件判断的重要依据。当时间到来,_libevent_callback() 会在下面调用 co_resume() 的位置,将 CPU 使用权交回给协程。

销毁协程

除了 ci_yield() 之外,协程函数调用 return 也会导致从 co_resume() 返回,所以在 _libevent_callback() 中,我们还需要判断协程是否已经结束。如果协程结束,那么就应当销毁相关的协程资源了。参见 if (is_coroutine_end(arg->coroutine)) {...} 条件体内的代码。

会话模式(Session Mode)

在本工程的实现中,提供了被称为 “会话模式” 的一个服务器设计模式。会话模式指的是 UDPServer 或 TCPServer 对象,在接收到传入数据后,自动区分客户端,并单独创建 Session 对象进行处理。每个 Session 对象只服务于一个客户端。

对于 TCPServer 而言,实现上述的功能比较简单,因为监听一个 TCP socket 之后,当有传入连接的时候,只要调用 accept(),就可以获得一个新的文件描述符,为这个文件描述符创建一个新的 Server 的子类就行了——这就是 TCPSession 类。

但是 UDPServer 就比较麻烦了,因为 UDP 不能这么做。我们只能自行实现所谓的 session。

UDPSession 实现

设计目标

我们需要实现 UDPSession 类的如下效果:

  • 类调用 recv 函数时,只会接收到对应的远程客户端发来的数据

  • 类调用 send 函数(实际实现是 reply()rrreee현재 코루틴이 아직 실행되지 않은 경우 이 코드를 실행한 후 프로그램은 libco 코루틴 생성 시 지정한 코루틴 함수로 전환하여 실행을 시작합니다. UDPServer

    의 경우 _libco_routine() 함수입니다. 이 함수는 단 세 줄로 매우 간단합니다: 🎜rrreee🎜 매개변수를 전달하면 libco 콜백 함수가 실행을 위해 애플리케이션에서 지정한 서버 함수로 변환됩니다. 🎜🎜하지만 첫 번째 libevent 콜백을 구현하는 방법은 무엇입니까? 이것은 여전히 ​​매우 간단합니다. libevent의 event_add()를 호출할 때 시간 초과를 0으로 설정하면 libevent 이벤트가 즉시 시간 초과됩니다. 이 메커니즘을 통해 우리는 🎜Base🎜가 실행된 후 즉시 각 🎜Procedure🎜 서비스 기능을 실행한다는 목적을 달성합니다. 🎜🎜코루틴 일시 중지 및 재개🎜🎜co_yield를 호출하는 시점이 이 코루틴 구현의 초점입니다. co_yield가 호출되는 위치는 컨텍스트 전환을 일으킬 수 있는 위치입니다. 비동기 프로그래밍 프레임워크를 동기 프레임워크로 변환하는 핵심 기술 포인트이기도 합니다. 여기에서 🎜UDPServer🎜의 recv_in_timeval() 함수를 참고할 수 있습니다. 함수의 기본 논리는 다음과 같습니다. 🎜🎜C/C++ 코루틴의 어셈블리 기반 구현(서버용)🎜🎜가장 중요한 분기는 libevent 이벤트 플래그의 판단이며 가장 중요한 논리는 event_add()co_yield()입니다. > 함수 호출. 함수 조각은 다음과 같습니다. 🎜rrreee🎜여기서는 co_yield() 함수를 중단점으로 이해합니다. 여기서 프로그램이 실행되면 CPU 사용 권한이 넘겨지며 프로그램이 실행됩니다. co_resume() 호출로 돌아갑니다. 이 "상위 수준 기능"은 정확히 어디에 있습니까? 사실 앞서 언급한 _libevent_callback() 함수입니다. 🎜🎜_libevent_callback()의 관점에서 보면 프로그램은 co_resume() 함수에서 반환되어 계속 실행됩니다. 이 시점에서 우리는 이것을 이해할 수 있습니다: 코루틴의 스케줄링은 실제로 libevent를 빌려 수행됩니다. 여기서 우리는 co_resume() 위의 문장에 주목하고 싶습니다. 🎜rrreee🎜여기서 🎜libevent🎜 이벤트 플래그 값은 위에서 언급한 이벤트 판단의 중요한 기반인 코루틴에 전달됩니다. 때가 되면 _libevent_callback()은 아래 co_resume()을 호출하여 CPU 사용량을 코루틴에 다시 전달합니다. 🎜🎜코루틴을 삭제합니다🎜🎜ci_yield() 외에도 코루틴 함수 호출 returnco_resume()에서도 반환을 발생시킵니다. 따라서 _libevent_callback()에서는 코루틴이 종료되었는지도 확인해야 합니다. 코루틴이 종료되면 관련 코루틴 리소스가 삭제되어야 합니다. if (is_coroutine_end(arg->coroutine)) {...} 조건 본문의 코드를 참조하세요. 🎜

    세션 모드

    🎜본 프로젝트 구현에서는 "세션 모드"라는 서버 설계 패턴이 제공됩니다. 세션 모드는 UDPServer 또는 TCPServer 개체를 참조하여 들어오는 데이터를 받은 후 자동으로 클라이언트를 구별하고 처리를 위해 별도의 🎜Session🎜 개체를 생성합니다. 각 세션 개체는 하나의 클라이언트에만 서비스를 제공합니다. 🎜🎜🎜TCPServer🎜의 경우 위 기능을 구현하는 것이 상대적으로 간단합니다. TCP 소켓을 모니터링한 후 들어오는 연결이 있을 때 accept()를 호출하면 새 연결을 얻을 수 있기 때문입니다. 파일 설명자 , 이 파일 설명자에 대해 새로운 🎜Server🎜 하위 클래스를 생성하세요. 이것이 🎜TCPSession🎜 클래스입니다. 🎜🎜하지만 🎜UDPServer🎜는 UDP가 이를 할 수 없기 때문에 더 번거롭습니다. 우리는 소위 세션을 스스로 구현할 수만 있습니다. 🎜🎜UDPSession 🎜🎜디자인 목표🎜🎜를 달성하려면 🎜UDPSession🎜 클래스의 다음 효과를 달성해야 합니다. 🎜
    • 🎜 클래스가 🎜recv🎜를 호출할 때 해당 data🎜
    • 🎜 클래스가 해당 원격 클라이언트에서 보낸 🎜send🎜 함수(실제 구현은 reply())를 호출할 때만 수신합니다. 🎜UDPServer🎜 포트를 사용하여 응답할 수 있습니다 🎜

    recv()

    프로젝트에서 UDPSession은 추상 클래스이고 실제 구현은 UDPItnlSession입니다. 그러나 정확하게 말하면 UDPItnlSession의 구현은 UDPServer에 밀접하게 의존합니다. 이 부분은 UDPServer_session_mode_worker() 함수에 있는 do-while() 루프 본문 코드를 참고하시면 됩니다. 프로그램 아이디어는 다음과 같습니다: _session_mode_worker() 函数中的 do-while() 循环体代码。程序思路如下:

    • UDPServer 维护一个 UDPSession 字典,以远程 IP + 端口名的组合作为 key。

    • 当数据到来时,判断远程 IP + 端口的组合是否在字典中,如果在,那么就把数据复制给对应的 session;如果不存在,则创建 session

    复制数据的代码,参见 UDPItnlSession 类的 forward_incoming_data() 函数实现。

    reply()

    发送数据其实就很简单,直接对 UDPServer 的 fd 进行 sendto() 就可以了。

    quit

    对于 session mode 的 Server 对象,代码中提供了一个可以由其 session 调用的、要求 server 退出并销毁资源的函数:quit_session_mode_server()。实现原理是向 server 触发一个 EV_SIGNAL 事件。对于普通的 I/O 事件而言,这是不应当出现的,我们这里活用来作为退出信号。如果 server 发现了这个信号,则触发退出逻辑。

    应用示例

    本工程的示例代码分为 server 和 client 两部分,其中 server 用到了 libcoevent,而 client 只是使用 Python 写的简单程序。本文就不说明 client 部分的代码了。

    Server 的代码,分别针对 Server 类的三个子类做了应用示例。使用了包括空行、调试语句、错误判断等在内的逻辑,仅使用不到 300 行,就实现了一个过程和两个服务。应该说,逻辑还是很清晰的,而且也节省了大量代码。

    SubRoutine

    通过函数 _simple_test_routine(),展示了一个一次性的线性网络逻辑。程序中,routine 首先创建了一个 DNSClient 对象,向默认域名服务器请求了一个域名,然后 connect() 该服务器的 80 端口。成功后,直接返回。

    这个函数展示了 SubRoutine 的使用场景,以及 Client 对象的使用方法,特别是 DNSClient 的简易使用方法。

    UDPServer

    UDPServer 的入口函数是 _udp_session_routine(),功能是为客户端提供域名查询服务。Clients 发送一段字符串作为待查询域名,然后 server 通过 DNSClient 对象请求后,将查询结果返回给客户端。

    这个函数展示了 UDPSession 对象和 DNSClient 的(比较复杂和完整的)使用方法。

    TCPServer

    入口函数是 _tcp_session_routine()

      UDPServer

      는 원격 IP + 포트 이름의 조합을 키로 사용하여

      UDPSession

      사전을 유지 관리합니다.

      데이터가 도착하면 사전에 원격 IP + 포트 조합이 있는지 확인하고, 존재하지 않으면 해당 세션에 데이터를 복사하고, 세션을 생성합니다.

    1. 복사할 코드; 데이터, UDPItnlSession 클래스의 forward_incoming_data() 함수 구현을 참조하세요. reply()

    2. 데이터 보내기는 실제로 매우 간단합니다.
    3. UDPServer

      의 fd에서 직접 sendto()를 수행하면 됩니다. quit

    4. 세션 모드의
    Server

    개체에 대해 코드는 세션에서 호출할 수 있는 함수를 제공하고 서버가 리소스를 종료하고 삭제하도록 요구합니다: quit_session_mode_server(). 구현 원칙은 서버에 EV_SIGNAL 이벤트를 트리거하는 것입니다. 일반적인 I/O 이벤트의 경우 이는 발생하지 않아야 하며 여기서는 종료 신호로 사용합니다. 서버가 이 신호를 감지하면 종료 로직이 트리거됩니다.

    애플리케이션 예제이 프로젝트의 샘플 코드는 서버와 클라이언트의 두 부분으로 나뉩니다. 서버는

    libcoevent

    를 사용하는 반면 클라이언트는 Python로 작성된 간단한 프로그램입니다. 이 문서에서는 코드의 클라이언트 부분을 설명하지 않습니다.

    Server의 코드는 Server 클래스의 세 가지 하위 클래스에 대한 응용 프로그램 예제를 제공합니다. 빈줄, 디버깅문, 오류판단 등의 로직을 사용하여 하나의 프로세스와 두 개의 서비스를 300줄 미만으로 구현했습니다. 여전히 로직이 매우 명확하고 많은 코드가 절약되었다고 할 수 있습니다.

    SubRoutine

    _simple_test_routine() 함수를 통해 일회성 선형 네트워크 논리를 보여줍니다. 프로그램에서 루틴은 먼저 DNSClient

    개체를 생성하고 기본 도메인 이름 서버에서 도메인 이름을 요청한 다음 서버의 connect() 포트 80을 요청합니다. 성공 후 직접 반환합니다. 🎜🎜이 기능은 🎜SubRoutine🎜의 사용 시나리오와 🎜Client🎜 개체의 사용, 특히 🎜DNSClient🎜의 간단한 사용을 보여줍니다. 🎜🎜UDPServer🎜🎜🎜UDPServer🎜의 진입 기능은 _udp_session_routine()으로, 클라이언트에게 도메인 이름 조회 서비스를 제공하는 기능입니다. 클라이언트는 쿼리할 도메인 이름으로 문자열을 전송하고, 서버는 🎜DNSClient🎜 개체를 통해 요청한 후 쿼리 결과를 클라이언트에 반환합니다. 🎜🎜이 기능은 🎜UDPSession🎜 개체와 🎜DNSClient🎜의 (더 복잡하고 완전한) 사용법을 보여줍니다. 🎜🎜TCPServer🎜🎜입력 함수는 _tcp_session_routine()이고 논리는 비교적 간단하며 주로 🎜TCPSession🎜의 사용법을 보여줍니다. 🎜🎜Postscript🎜🎜 🎜libcoevent🎜는 원칙적으로 개발되어 필요한 기능을 구현했으며, 서버 프로그램 작성에 사용할 수 있습니다. 물론, 이것이 첫 번째 버전이기 때문에 여전히 많은 코드가 다소 지저분해 보입니다. 이 라이브러리의 의의는 C/C++ 코루틴의 보다 독창적인 구현 원리를 교육적 관점에서 세심하게 설명할 수 있으며, 사용 가능한 코루틴 서버 라이브러리로도 사용할 수 있다는 점입니다. 🎜🎜이 라이브러리를 비판하는 독자들을 환영하며 새로운 요구 사항을 제안하는 것도 환영합니다. 예를 들어 TODO로 간주되는 몇 가지 요구 사항을 추가하기로 결정했습니다. 🎜🎜🎜🎜implementation🎜HTTPServer🎜을 🎜의 하위 클래스로 TCPServer🎜는 외부 SSL 요청을 처리하기 위해 🎜SSLClient🎜를 구현하는 HTTP fcgi 서비스 🎜🎜🎜🎜 클래스를 제공합니다. 🎜🎜🎜🎜관련 기사: 🎜🎜🎜C# 네트워크 프로그래밍 기사 시리즈(8) UdpClient는 동기화된 UDP 서버를 구현합니다🎜🎜🎜🎜C 언어는 PHP 서버를 구현합니다🎜🎜🎜관련 동영상: 🎜🎜🎜🎜C# 튜토리얼🎜🎜

위 내용은 C/C++ 코루틴의 어셈블리 기반 구현(서버용)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.