helio_raw_echo_server
Helio raw_echo_server
소개
raw_echo_server는 입력받은 내용을 그대로 내보낸다.
즉, "Hello"를 받으면 "Hello"를 그대로 리턴한다.
목차
실행 옵션
실행 옵션 보기: build-dbg$ raw_echo_server --helpfull
- Flags from helio/examples/raw_echo_server.cc:
- --port (Echo server port); default: 8081;
- --size (Message size); default: 512;
- --threads (Number of connection threads. 0 to run as single threaded. Otherwise, the main thread accepts connections and worker threads are handling TCP connections); default: 0;
- Flags from helio/build-dbg/_deps/abseil_cpp-src/absl/flags/parse.cc:
- --flagfile (comma-separated list of files to load flags from); default: ;
- --fromenv (comma-separated list of flags to set from the environment [use 'export FLAGS_flag1=value']); default: ;
- --tryfromenv (comma-separated list of flags to try to set from the environment if present); default: ;
- --undefok (comma-separated list of flag names that it is okay to specify on the command line even if the program does not define a flag with that name); default: ;
- Flags from helio/build-dbg/_deps/glog-src/src/logging.cc:
- --alsologtoemail (log messages go to these email addresses in addition to logfiles); default: "";
- --alsologtostderr (log messages go to stderr in addition to logfiles); default: false;
- --colorlogtostderr (color messages logged to stderr (if supported by terminal)); default: false;
- --colorlogtostdout (color messages logged to stdout (if supported by terminal)); default: false;
- --drop_log_memory (Drop in-memory buffers of log contents. Logs can grow very quickly and they are rarely read before they need to be evicted from memory. Instead, drop them from memory as soon as they are flushed to disk.); default: true;
- --log_backtrace_at (Emit a backtrace when logging at file:linenum.); default: "";
- --log_dir (If specified, logfiles are written into this directory instead of the default logging directory.); default: "";
- --log_link (Put additional links to the log files in this directory); default: "";
- --log_prefix (Prepend the log prefix to the start of each log line); default: true;
- --log_utc_time (Use UTC time for logging.); default: false;
- --log_year_in_prefix (Include the year in the log prefix); default: true;
- --logbuflevel (Buffer log messages logged at this level or lower (-1 means don't buffer; 0 means buffer INFO only; ...)); default: 0;
- --logbufsecs (Buffer log messages for at most this many seconds); default: 30;
- --logcleansecs (Clean overdue logs every this many seconds); default: 300;
- --logemaillevel (Email log messages logged at this level or higher (0 means email all; 3 means email FATAL only; ...)); default: 999;
- --logfile_mode (Log file mode/permissions.); default: 436;
- --logmailer (Mailer used to send logging email); default: "";
- --logtostderr (log messages go to stderr instead of logfiles); default: false;
- --logtostdout (log messages go to stdout instead of logfiles); default: false;
- --max_log_size (approx. maximum log file size (in MB). A value of 0 will be silently overridden to 1.); default: 1800;
- --minloglevel (Messages logged at a lower level than this don't actually get logged anywhere); default: 0;
- --stderrthreshold (log messages at or above this level are copied to stderr in addition to logfiles. This flag obsoletes --alsologtostderr.); default: 2;
- --stop_logging_if_full_disk (Stop attempting to log to disk if the disk is full.); default: false;
- --timestamp_in_logfile_name (put a timestamp at the end of the log file name); default: true;
- Flags from helio/build-dbg/_deps/glog-src/src/utilities.cc:
- --symbolize_stacktrace (Symbolize the stack trace in the tombstone); default: true;
- Flags from helio/build-dbg/_deps/glog-src/src/vlog_is_on.cc:
- --v (Show all VLOG(m) messages for m <= this. Overridable by --vmodule.); default: 0;
- --vmodule (per-module verbose level. Argument is a comma-separated list of
= . is a glob pattern, matched against the filename base (that is, name ignoring .cc/.h./-inl.h). overrides any value given by --v.); default: "";
테스트 프로그램
테스트 프로그램은 python으로 작성했다.
- t_raw_echo1.py: 8081 포트에 접속해서 'Hello, raw_echo_server!' 메시지를 보내고 그대로 받는다.
- t_raw_echo2.py: 메시지를 입력할 수 있다. 메시지(문자열) 길이는 23바이트이다. 23바이트 이상 입력하면 다음 응답 시 받는다.
- t_raw_echo3.py: 횟수(count)를 입력하면 메시지를 반복해서 보낸다.
- t_raw_echo4.py: 횟수(count)를 반복해서 입력할 수 있도록 수정했다. 종료는 'exit'.
- t_raw_echo5.py: 스레드 개수를 입력받아 여러 개 스레드로 메시지를 보내도록 수정했다.
서버 로그
서버 실행: [build-dbg]$ raw_echo_server --logtostderr --v 1 --threads 2 --size 23
서버 실행 직후 서버 로그
테스트 프로그램 t_raw_echo1.py 실행
[examples]$ python t_raw_echo1.py
Sending: Hello, raw_echo_server!
Received: Hello, raw_echo_server!
서버 로그
테스트 프로그램 t_raw_echo2.py 실행
[examples]$ python t_raw_echo2.py
Enter message to send (type "exit" to quit): Hello, raw_echo_server!AAAA -> 'AAAA'를 추가로 입력
Sending: Hello, raw_echo_server!AAAA -> 27바이트 입력
Received: Hello, raw_echo_server! -> 23바이트만 도착, 'AAAA'는 다음에 옵니다.
서버 로그
t_raw_echo2.py 계속
Enter message to send (type "exit" to quit): Hello, raw_echo_server!BBBBB -> 'BBBBB'를 추가로 입력
Sending: Hello, raw_echo_server!BBBBB
Received: AAAAHello, raw_echo_ser -> 이전에 입력한 'AAAA'가 앞에 붙어서 23바이트 도착
서버 로그
t_raw_echo2.py 종료
Enter message to send (type "exit" to quit): exit -> 테스트 프로그램 종료
Exiting...
서버 로그
테스트 프로그램 t_raw_echo3.py 실행
[examples]$ python t_raw_echo3.py
Enter the number of times to send the message: 5 -> 횟수(count) 입력
Sending : Hello, raw_echo_server! (Message 1/5)
Received: Hello, raw_echo_server!
Sending : Hello, raw_echo_server! (Message 2/5)
Received: Hello, raw_echo_server!
Sending : Hello, raw_echo_server! (Message 3/5)
Received: Hello, raw_echo_server!
Sending : Hello, raw_echo_server! (Message 4/5)
Received: Hello, raw_echo_server!
Sending : Hello, raw_echo_server! (Message 5/5)
Received: Hello, raw_echo_server!
서버 로그
Ctrl+C(^C) 서버 종료
서버 로그
소스 설명
raw_echo_server.cc 소스 설명입니다.
◼️ include, FLAG(option)
include 파일 설명(chatgpt)
- absl/strings/str_cat.h -> string nm = absl::StrCat("worker", i); main()
- gperftools/profiler.h
-> ProfilerEnable()
CPU 프로파일링을 수행하기 위해 사용되는 헤더 파일입니다.
이를 통해 개발자는 코드의 어느 부분이 CPU 시간을 많이 소모하는지 파악할 수 있습니다. - netinet/in.h
-> struct sockaddr_in, struct in_addr, socket(), bind(), connect(), listen(), etc
UNIX 계열 운영체제(예: Linux, macOS)에서 네트워크 소켓 프로그래밍을 할 때 필수적으로 사용됩니다.
이 헤더 파일은 인터넷 주소 패밀리와 관련된 다양한 구조체와 상수, 함수 프로토타입을 정의합니다.
- poll.h -> poll(), POLLIN, POLLOUT, POLLERR, POLLHUP, POLLNVAL
- queue -> std::queue, push(), pop(), front(), back(), empty(), size(), etc
- thread
-> std::thread, join(), detach()
스레드는 --threads 옵션을 받아 main()에서 base::StartThread() 함수를 사용해서 직접 생성합니다.
ping_ioring_server에서 사용했던 --proactor_threads(proactor_pool.cc)를 사용하지 않습니다.
◼️ 시그널(signal) 처리 관련 함수들
◼️ CbNotify, struct ResumablePoint
'CbNotify'는 특정한 조건을 가진 함수 객체를 정의하는 타입 별칭입니다.
이 함수 객체는 내부에 함수를 저장하고, 해당 함수를 호출할 때 'fb2::detail::FiberInterface*'와
'CqeResult'를 인자로 받으며, 반환값은 없습니다.
◼️ struct Worker
멀티스레딩 환경에서 여러 작업자(Worker)들을 관리하기 위한 기초를 제공합니다.
각 작업자(Worker)는 자체 큐를 가지고 있어, 작업을 독립적으로 처리할 수 있습니다.
스레드는 'pthread_t'를 사용하여 식별되고, 작업자의 큐는 MPMC 큐를 통해 생산자와 소비자 간의
데이터를 안전하게 전달할 수 있게 합니다.
'workers' 벡터를 통해 여러 작업자를 손쉽게 관리할 수 있습니다.
◼️ SuspendMyself() 함수
이 함수는 io_uring을 사용하여 비동기 작업을 처리하는 중에 현재 Fiber를 일시 정지시키고, 특정 조건이 충족되면 동일한 스레드에서 다시 활성화되도록 설정하는 역할을 합니다.
• SuspendMyself() 함수 설명(chatgpt)
◼️ Recv() 함수
이 함수는 주어진 파일 디스크립터(fd)에서 데이터를 읽어오는 함수입니다.
io_uring을 사용하여 비동기적으로 데이터를 수신하는 기능을 제공합니다.
◼️ Send() 함수
이 함수는 특정 파일 디스크립터(fd)에 데이터를 비동기적으로 전송하는 함수입니다.
io_uring 인터페이스를 사용하여 효율적인 입출력 작업을 수행합니다.
◼️ HandleSocket() 함수
이 함수는 데이터를 소켓으로부터 받아 다시 소켓으로 보내는 에코 서버의 주요 기능을 수행합니다.
데이터 수신 및 전송 시 발생하는 시간을 기록하여 성능을 모니터링할 수 있습니다.
AcceptFiber(), RunEventLoop()에서 호출합니다.
• HandleSocket() 함수 설명(chatgpt)
◼️ AcceptFiber() 함수
주어진 리스닝 파일 디스크립터(listen_fd)로 들어오는 네트워크 연결을 수락하고,
이를 처리하기 위해 io_uring 인터페이스를 사용하는 함수입니다.
함수의 주요 목적은 새로운 연결을 지속적으로 수락하고,
이러한 연결을 워커(worker)에 분배하거나 직접 처리하는 것입니다.
• AcceptFiber() 함수 설명(chatgpt)
◼️ DispatchCqe() 함수
이 함수는 IO 작업이 완료된 후 적절한 콜백을 호출하여 결과를 처리하는 역할을 합니다.
'io_uring_cqe'는 IO 작업 완료 이벤트를 나타내며,
'suspended_list'는 대기 중인 작업의 콜백 함수를 관리합니다.
• DispatchCqe() 함수 설명(chatgpt)
◼️ RunEventLoop() 함수
이 이벤트 루프 함수는 주어진 'io_uring' 인스턴스를 사용하여 이벤트를 처리하고, Fiber를 통해 비동기 작업을 관리합니다. MyPolicy.Run() call RunEventLoop()
• RunEventLoop() 함수 설명(chatgpt)
◼️ SetupIORing() 함수
이 함수는 'io_uring'을 설정하는 역할을 합니다.
• SetupIORing() 함수 설명(chatgpt)
◼️ SetupListener() 함수
이 함수는 주어진 포트 번호를 사용하여 네트워크 소켓을 설정하고, 이 소켓을 통해 클라이언트 연결을 수신할 준비를 합니다.
• SetupListener() 함수 설명(chatgpt)
◼️ MyPolicy() 클래스
MyPolicy는 fb2::DispatchPolicy를 상속받은 클래스입니다.
MyPolicy 클래스는 특정 인덱스를 기반으로 I/O 링을 설정하고,
이를 통해 이벤트 루프를 실행하는 역할을 합니다.
◼️ WorkerThread() 함수
네트워크 연결을 처리하는 워커 스레드를 초기화하고 관리하는 역할을 합니다.
• WorkerThread() 함수 설명(chatgpt)
◼️ main() 함수
raw_echo_server의 main() 함수입니다.
◼️ 프로그램 수정 내용
- 원 소스 코드의 오류: 첫번째 스레드에서 accept4()해서 데이터를 받아서
worker 스레드에게 분배하는 방식인데, 스레드가 2개 이상일 때는
두번째 스레드가 깨어나지 않으므로 작동하지 않는다.
그래서 각 스레드마다 accept4()를 실행해서 클라이언트의 접속을 받아서
처리하도록 수정했다.
위에 설명한 소스는 변경된 소스입니다. - 테스트: t_raw_echo1.py ~ t_raw_echo5.py -> 성공.
- 소스 코드 설명
Server - Client 통신 순서: socket()->bind()->listen()->accept()
- 변경된 코드
