MsQuicのセットアップ (Linux用)
CMakeのアップグレード(>= 3.16)
バージョン確認
ターミナル
cmake --version
リポジトリの追加
ターミナル
echo "deb http://deb.debian.org/debian bookworm-backports main contrib non-free" | sudo tee /etc/apt/sources.list.d/bookworm-backports.list
必要なパッケージをインストール
ターミナル
sudo apt update
sudo apt install -t bookworm-backports cmake
MsQuicのインストール
必要なパッケージをインストール
ターミナル
sudo apt install -y git software-properties-common apt-transport-https curl libssl-dev libunwind-dev clang ninja-build libtool m4 autoconf
GitHubからダウンロード
ターミナル
git clone https://github.com/microsoft/msquic.git
cd msquic/
git submodule update --init --recursive
ビルド & インストール
ターミナル
mkdir build
cd build
QUIC_ENABLE_PREVIEW_FEATURES=1 cmake -B build -S .. -GNinja
cmake --build build
sudo cmake --install build
sudo ldconfig
インストール先
-- Install configuration: "Release"
-- Installing: /usr/local/lib/libmsquic.so.2.6.0
-- Installing: /usr/local/lib/libmsquic.so.2
-- Installing: /usr/local/lib/libmsquic.so
-- Installing: /usr/local/lib/libmsquic_platform.a
-- Installing: /usr/local/include/msquic.h
-- Installing: /usr/local/include/msquic.hpp
-- Installing: /usr/local/include/msquic_fuzz.h
-- Installing: /usr/local/include/msquic_posix.h
-- Installing: /usr/local/include/msquic_winkernel.h
-- Installing: /usr/local/include/msquic_winuser.h
-- Installing: /usr/local/include/msquichelper.h
-- Installing: /usr/local/include/msquicp.h
-- Installing: /usr/local/include/quic_cert.h
-- Installing: /usr/local/include/quic_crypt.h
-- Installing: /usr/local/include/quic_datapath.h
-- Installing: /usr/local/include/quic_driver_helpers.h
-- Installing: /usr/local/include/quic_hashtable.h
-- Installing: /usr/local/include/quic_pcp.h
-- Installing: /usr/local/include/quic_platform.h
-- Installing: /usr/local/include/quic_platform_posix.h
-- Installing: /usr/local/include/quic_platform_winkernel.h
-- Installing: /usr/local/include/quic_platform_winuser.h
-- Installing: /usr/local/include/quic_sal_stub.h
-- Installing: /usr/local/include/quic_storage.h
-- Installing: /usr/local/include/quic_tls.h
-- Installing: /usr/local/include/quic_toeplitz.h
-- Installing: /usr/local/include/quic_trace.h
-- Installing: /usr/local/include/quic_trace_manifested_etw.h
-- Installing: /usr/local/include/quic_var_int.h
-- Installing: /usr/local/include/quic_versions.h
-- Installing: /usr/local/share/msquic/msquic-config.cmake
-- Installing: /usr/local/share/msquic/msquic.cmake
-- Installing: /usr/local/share/msquic/msquic-release.cmake
cat ファイルパス | grep [オプション -A : 後ろ -B : 前 -C : 前後] [行数] [検索文字列]
でファイルの中身を検索出来る
MsQuicのセットアップ (Windows用)
Visual Studioをインストールする
Download
CMakeをインストールする
最新版をインストール
Download
Windows x64 Installerを選択 (インストール後パスが通っていることを確認)
MsQuicをビルド
コマンドプロンプト
cd C:/
mkdir Programs
cd Programs
git clone https://github.com/microsoft/msquic.git
cd msquic
git submodule update --init --recursive
mkdir build
cd build
cmake -B build -S .. -G "Visual Studio 17 2022" -A x64 -DQUIC_TLS_LIB=schannel
cmake --build build
ライブラリ
ライブラリのパス
C:/Programs/msquic/build/build/bin/Debug/msquic.dll
ヘッダのパス
C:/Programs/msquic/src/inc/msquic.h
Visual Studio の設定
ヘッダーファイルのインクルードパスを設定
[プロジェクト] → [プロパティ] → [構成プロパティ] → [VC++ ディレクトリ] → [全般] → [外部インクルードディレクトリ]
C:/Programs/msquic/src/inc
ライブラリディレクトリの追加
[プロジェクト] → [プロパティ] → [構成プロパティ] → [リンカー] → [全般] → [追加のライブラリディレクトリ]
C:/Programs/msquic/build/build/obj/Debug
リンクするライブラリの指定
[プロジェクト] → [プロパティ] → [構成プロパティ] → [リンカー] → [入力] → [追加の依存ファイル]
msquic.lib
環境変数にDLLのPATHを追加
C:\Programs\msquic\build\build\bin\Debug
サンプルコード
ヘッダファイル (QUICStart.cpp)
#define _CRT_SECURE_NO_WARNINGS //SDLチェック無効
#include <cstddef>
#include <limits>
#include <climits>
#include <cfloat>
#include <cstdint>
#include <cstdlib>
#include <new>
#include <typeinfo>
#include <exception>
#include <initializer_list>
#include <cstdalign>
#include <stdexcept>
#include <cassert>
#include <cerrno>
#include <system_error>
#include <string>
#if __has_include(<string_view>)
# include <string_view>
#endif
#include <array>
#include <deque>
#include <forward_list>
#include <list>
#include <vector>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <queue>
#include <stack>
#include <iterator>
#include <algorithm>
#include <cfenv>
#include <random>
#include <numeric>
#include <iosfwd>
#include <iostream>
#include <ios>
#include <streambuf>
#include <istream>
#include <ostream>
#include <iomanip>
#include <sstream>
#include <fstream>
#if __has_include(<filesystem>)
# include <filesystem>
#endif
#include <cstdio>
#include <cinttypes>
#include <regex>
#include <atomic>
#include <thread>
#include <mutex>
#include <shared_mutex>
#include <condition_variable>
#include <future>
#include <cstring>
#include <tuple>
#include <ctime>
#include <chrono>
#define _USE_MATH_DEFINES //数値演算定数を定義
#include <cmath>
#define OPEN_PROCESS_TOKEN (TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY) // アクセス権の定数
#ifdef __linux__
//linux code
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#elif _WIN32
// windows code
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <direct.h>
#include <tchar.h>
#include <WinSock2.h>
#include <ws2tcpip.h>
#include <windowsx.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "msquic.lib")
#pragma warning(disable:4996)
#else
#endif
#if __has_include(<msquic.h>)
#include <msquic.h>
class QUICStart{
protected:
QUIC_API_TABLE* MsQuic;
QUIC_STATUS status;
HQUIC Listener = NULL;
HQUIC Configuration = NULL;
HQUIC Registration = NULL;
HQUIC QUICConnection = NULL;
QUIC_SETTINGS Settings = {0};
QUIC_ADDR Address = {0};
QUIC_BUFFER AlpnBuffer;
QUIC_REGISTRATION_CONFIG RegistrationConfig; // 登録設定
QUIC_CERTIFICATE_FILE CertificateConfig; // 証明書と秘密鍵の設定
QUIC_CREDENTIAL_CONFIG CredConfig;
QUIC_BUFFER* Buffer;
bool isServer = true;
std::string ipv4;
int port;
std::vector<uint8_t> recvData;
std::string sendData = "";
bool debug = false;
bool retry = false;
std::string alpn;
_IRQL_requires_max_(DISPATCH_LEVEL)
_Function_class_(QUIC_LISTENER_CALLBACK)
static QUIC_STATUS QUIC_API ListenerCallback( // ListenerCallback関数
_In_ HQUIC Listener,
_In_opt_ void* Context,
_Inout_ QUIC_LISTENER_EVENT* Event
) {
QUICStart* self = reinterpret_cast<QUICStart*>(Context);
switch (Event->Type) { // イベントを重複定義しないよう switch case文を使う
case QUIC_LISTENER_EVENT_NEW_CONNECTION:
if (self->debug) printf("[ListenerCallback] NEW_CONNECTION\n");
self->QUICConnection = Event->NEW_CONNECTION.Connection;
self->MsQuic->SetCallbackHandler(self->QUICConnection, (void*)QUICStart::ConnectionCallback, self);
self->status = self->MsQuic->ConnectionSetConfiguration(self->QUICConnection, self->Configuration);
if (QUIC_FAILED(self->status)) {
printf("[FAILED] ConnectionSetConfiguration: 0x%x\n", self->status);
self->MsQuic->ConnectionClose(self->QUICConnection);
}
break;
default:
if (self->debug) printf("[ListenerCallback] Listener event type: 0x%X\n", Event->Type);
break;
}
return QUIC_STATUS_SUCCESS;
}
_IRQL_requires_max_(DISPATCH_LEVEL)
_Function_class_(QUIC_CONNECTION_CALLBACK)
static QUIC_STATUS QUIC_API ConnectionCallback( // ConnectionCallback関数
_In_ HQUIC Connection,
_In_opt_ void* Context,
_Inout_ QUIC_CONNECTION_EVENT* Event
) {
HQUIC Stream;
QUICStart* self = reinterpret_cast<QUICStart*>(Context);
switch (Event->Type) {
case QUIC_CONNECTION_EVENT_CONNECTED:
if (self->debug) printf("[ConnectionCallback] QUIC_CONNECTION_EVENT_CONNECTED\n");
if(!self->isServer){
self->status = self->MsQuic->StreamOpen(// ストリームを作成
Connection,
QUIC_STREAM_OPEN_FLAG_NONE,
(QUIC_STREAM_CALLBACK_HANDLER)QUICStart::StreamCallback,
Context,
&Stream
);
if (QUIC_SUCCEEDED(self->status)) {
self->MsQuic->StreamStart(Stream, QUIC_STREAM_START_FLAG_IMMEDIATE);
} else {
printf("[FAILED] StreamOpen : 0x%x\n", self->status);
// ストリーム作成失敗時に Connection を閉じる処理を追加しても良い
self->MsQuic->ConnectionShutdown(Connection, QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, 0);
}
}
break;
case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT:
if (self->debug) printf("[ConnectionCallback] QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT\n");
self->MsQuic->ConnectionClose(Connection);
break;
case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE:
if (self->debug) printf("[ConnectionCallback] QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE\n");
self->MsQuic->ConnectionClose(Connection);
break;
case QUIC_CONNECTION_EVENT_PEER_STREAM_STARTED:
if (self->debug) printf("[ConnectionCallback] QUIC_CONNECTION_EVENT_PEER_STREAM_STARTED\n");
if(self->isServer){
Stream = Event->PEER_STREAM_STARTED.Stream;
self->MsQuic->SetCallbackHandler(Stream, (void*)QUICStart::StreamCallback, self);
self->MsQuic->StreamReceiveSetEnabled(Stream, TRUE);
}
break;
case QUIC_CONNECTION_EVENT_PEER_NEEDS_STREAMS:
if (self->debug) printf("[ConnectionCallback] QUIC_CONNECTION_EVENT_PEER_NEEDS_STREAMS\n");
break;
default:
if (self->debug) printf("[ConnectionCallback] Connection event type: 0x%X\n", Event->Type);
break;
}
return QUIC_STATUS_SUCCESS;
}
_IRQL_requires_max_(DISPATCH_LEVEL)
_Function_class_(QUIC_STREAM_CALLBACK)
static QUIC_STATUS QUIC_API StreamCallback( // StreamCallback関数
_In_ HQUIC Stream,
_In_opt_ void* Context,
_Inout_ QUIC_STREAM_EVENT* Event
) {
size_t msgLen;
uint8_t* message;
QUICStart* self = reinterpret_cast<QUICStart*>(Context);
switch (Event->Type) {
case QUIC_STREAM_EVENT_START_COMPLETE:
if (self->debug) printf("[StreamCallback] QUIC_STREAM_EVENT_START_COMPLETE\n");
if (self->sendData != ""){
msgLen = self->sendData.length();
message = (uint8_t*)malloc(msgLen + 1);
if (!message) {
printf("[FAILED] malloc\n");
return QUIC_STATUS_OUT_OF_MEMORY;
}
memcpy(message, self->sendData.c_str(), msgLen + 1);
self->Buffer = (QUIC_BUFFER*)malloc(sizeof(QUIC_BUFFER));
if (!self->Buffer) {
printf("[FAILED] malloc\n");
free(message);
return QUIC_STATUS_OUT_OF_MEMORY;
}
self->Buffer->Buffer = (uint8_t*)message;
self->Buffer->Length = (uint32_t)msgLen;
self->MsQuic->StreamSend(
Stream,
self->Buffer,
1,
QUIC_SEND_FLAG_START | QUIC_SEND_FLAG_FIN,
self->Buffer
);
//MsQuic->StreamSend(Stream, NULL, 0, QUIC_SEND_FLAG_FIN, NULL);
if (self->debug) printf("[StreamCallback] StreamSend : %s\n", self->sendData.c_str());
}
break;
case QUIC_STREAM_EVENT_SEND_COMPLETE:
if (self->debug) printf("[StreamCallback] QUIC_STREAM_EVENT_SEND_COMPLETE\n");
self->Buffer = (QUIC_BUFFER*)Event->SEND_COMPLETE.ClientContext;
if (self->Buffer) {
free(self->Buffer->Buffer); // message の解放
free(self->Buffer); // Buffer の解放
}
break;
case QUIC_STREAM_EVENT_RECEIVE:
if (self->debug) printf("[StreamCallback] QUIC_STREAM_EVENT_RECEIVE\n");
for (uint32_t i = 0; i < Event->RECEIVE.BufferCount; ++i) {
message = static_cast<uint8_t*>(Event->RECEIVE.Buffers[i].Buffer);
msgLen = static_cast<size_t>(Event->RECEIVE.Buffers[i].Length);
self->recvData.insert(self->recvData.end(), message, message + msgLen);
}
self->MsQuic->StreamReceiveComplete(Stream, Event->RECEIVE.TotalBufferLength);
break;
case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN:
if (self->debug) printf("[StreamCallback] QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN\n");
self->MsQuic->StreamShutdown(Stream, QUIC_STREAM_SHUTDOWN_FLAG_NONE, 0);
break;
case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE:
if (self->debug) printf("[StreamCallback] QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE\n");
self->MsQuic->StreamClose(Stream);
break;
default:
if (self->debug) printf("[StreamCallback] Stream event type: 0x%X\n", Event->Type);
break;
}
return QUIC_STATUS_SUCCESS;
}
public:
QUICStart(
std::string serverIPv4 = "127.0.0.1",
int serverPort = 9999,
std::string appName = "QUICApplication",
std::string alpn = "echo",
bool isServer = true,
std::string certFile = "certificate.crt",
std::string keyFile = "private.key",
int streamCount = 5,
bool debug = false
) {
this->ipv4 = serverIPv4;
this->port = serverPort;
this->isServer = isServer;
this->debug = debug;
this->RegistrationConfig.ExecutionProfile = QUIC_EXECUTION_PROFILE_LOW_LATENCY;
this->RegistrationConfig.AppName = appName.c_str();
this->alpn = alpn;
this->AlpnBuffer.Buffer = (uint8_t*)this->alpn.c_str();
this->AlpnBuffer.Length = (uint32_t)strlen(this->alpn.c_str());
this->Settings.IsSet.PeerBidiStreamCount = TRUE;
this->Settings.PeerBidiStreamCount = streamCount; // 双方向ストリーム数
this->Settings.IsSet.PeerUnidiStreamCount = TRUE;
this->Settings.PeerUnidiStreamCount = 0;
// 接続のセキュリティ設定
memset(&this->CredConfig, 0, sizeof(this->CredConfig));
if(this->isServer){
this->CertificateConfig.CertificateFile = certFile.c_str();
this->CertificateConfig.PrivateKeyFile = keyFile.c_str();
this->CredConfig.Flags = QUIC_CREDENTIAL_FLAG_NONE;
this->CredConfig.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE;
this->CredConfig.CertificateFile = &this->CertificateConfig;
}
else{
this->CredConfig.Flags = (
QUIC_CREDENTIAL_FLAG_CLIENT |
QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION
);
}
QuicAddrSetFamily(&this->Address, QUIC_ADDRESS_FAMILY_INET);
QuicAddrSetPort(&this->Address, this->port);
this->Address.Ipv4.sin_addr.s_addr = inet_addr(this->ipv4.c_str()); // IPアドレスを直接設定
}
int connection_start(){
if (!this->retry) {
this->status = MsQuicOpenVersion(QUIC_API_VERSION_2, (const void**)&this->MsQuic);
if (QUIC_FAILED(this->status)) {
printf("[FAILED] MsQuicOpen : 0x%x\n", this->status);
return 1;
}
this->status = this->MsQuic->RegistrationOpen(&this->RegistrationConfig, &this->Registration);
if (QUIC_FAILED(this->status)) {
printf("[FAILED] RegistrationOpen : 0x%x\n", this->status);
this->clear();
return 1;
}
this->status = this->MsQuic->ConfigurationOpen(
this->Registration,
&this->AlpnBuffer,
1, // BufferCount
&this->Settings,
sizeof(this->Settings),
NULL,
&this->Configuration
);
if (QUIC_FAILED(this->status)) {
printf("[FAILED] ConfigurationOpen : 0x%x\n", this->status);
this->MsQuic->RegistrationClose(this->Registration);
this->clear();
return 1;
}
this->status = this->MsQuic->ConfigurationLoadCredential(
this->Configuration,
&this->CredConfig
);
if (QUIC_FAILED(this->status)) {
printf("[FAILED] ConfigurationLoadCredential : 0x%x\n", this->status);
this->clear();
return 1;
}
}
if(this->isServer){
this->status = this->MsQuic->ListenerOpen(
this->Registration,
this->ListenerCallback,
this,
&this->Listener
);
if (QUIC_FAILED(this->status)) {
printf("[FAILED] ListenerOpen : 0x%x\n", this->status);
this->clear();
return 1;
}
this->status = this->MsQuic->ListenerStart(
this->Listener,
&this->AlpnBuffer,
1,
&this->Address
);
if (QUIC_FAILED(this->status)) {
printf("[FAILED] ListenerStart : 0x%x\n", this->status);
this->clear();
return 1;
}
}
else{
this->status = this->MsQuic->ConnectionOpen(
this->Registration,
this->ConnectionCallback,
this,
&this->QUICConnection
);
if (QUIC_FAILED(this->status)) {
printf("[FAILED] ConnectionOpen : 0x%x\n", this->status);
this->clear();
return 1;
}
// 接続を開始
this->status = this->MsQuic->ConnectionStart(
this->QUICConnection,
this->Configuration,
QUIC_ADDRESS_FAMILY_UNSPEC,
this->ipv4.c_str(),
this->port
);
if (QUIC_FAILED(this->status)) {
printf("[FAILED] ConnectionStart : 0x%x\n", this->status);
this->clear();
return 1;
}
}
if(this->debug) printf("[CONNECTED] %s : %d\n", this->ipv4.c_str(), this->port);
return 0;
}
~QUICStart(){
this->clear();
}
void send_data_set(std::string sendData = "Hello QUIC World!!"){
this->sendData = sendData;
}
void disp_recv_data(){
std::string data(this->recvData.begin(), this->recvData.end());
printf("[RECV] %s\n", data.c_str());
this->recvData.clear();
}
void clear(bool retry = false){
this->retry = retry;
this->recvData.clear();
if (this->Listener != NULL) {
this->MsQuic->ListenerClose(this->Listener);
this->Listener = NULL;
}
if (this->QUICConnection != NULL) {
this->MsQuic->ConnectionClose(this->QUICConnection);
this->QUICConnection = NULL;
}
if (!this->retry) {
if (this->Configuration != NULL) {
this->MsQuic->ConfigurationClose(this->Configuration);
this->Configuration = NULL;
}
if (this->Registration != NULL) {
this->MsQuic->RegistrationClose(this->Registration);
this->Registration = NULL;
}
if (this->MsQuic != NULL) {
MsQuicClose(this->MsQuic);
this->MsQuic = NULL;
}
}
}
};
#endif
クライアントサンプルコード (quic_sample_client.cpp)
#include "QUICStart.cpp"
int main(int argc, char** argv){
QUICStart quic = QUICStart(
"192.168.10.64",
9999,
"QUICApplication",
"echo",
false,
"certificate.crt",
"private.key",
5,
true
);
quic.send_data_set("Hello !!");
quic.connection_start();
getchar();
quic.clear(true);
quic.send_data_set("World !!");
quic.connection_start();
getchar();
return 0;
}
GCCビルド (Linux)
g++ -I /usr/local/include -L /usr/local/lib -g quic_sample_client.cpp -lmsquic -lssl -lcrypto -ldl -lpthread -lrt -o quic_sample_client.exe
サーバサンプルコード (quic_sample_server.cpp)
#include "QUICStart.cpp"
int main(int argc, char** argv){
QUICStart quic = QUICStart(
"192.168.10.64",
9999,
"QUICApplication",
"echo",
true,
"certificate.crt",
"private.key",
5,
true
);
quic.connection_start();
getchar();
quic.disp_recv_data();
quic.clear(true);
printf("再受信するには<ENTER>を押してください...");
getchar();
quic.connection_start();
getchar();
quic.disp_recv_data();
getchar();
return 0;
}
GCCビルド (Linux)
g++ -I /usr/local/include -L /usr/local/lib -g quic_sample_server.cpp -lmsquic -lssl -lcrypto -ldl -lpthread -lrt -o quic_sample_server.exe