MsQuick编译和使用
编译
克隆代码
git clone --recurse-submodules https://github.com/microsoft/msquic.git
使用cmake+vs2022编译
然后直接configure之后Generate然后打开vs工程编译即可生成动态库
使用示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "msquic.h"
#define DEFAULT_PORT 4567
#define BUFFER_SIZE 1024
const QUIC_REGISTRATION_CONFIG RegConfig = { "quicecho", QUIC_EXECUTION_PROFILE_LOW_LATENCY };
const QUIC_BUFFER Alpn = { sizeof("sample") - 1, (uint8_t*)"sample" };
const uint32_t IdleTimeoutMs = 1000;
const QUIC_API_TABLE* MsQuic = nullptr;
HQUIC Registration = nullptr;
HQUIC ServerConfiguration = nullptr;
HQUIC ClientConfiguration = nullptr;
HQUIC Listener = nullptr;
// 服务器回调函数
QUIC_STATUS QUIC_API ServerStreamCallback(
HQUIC Stream,
void* Context,
QUIC_STREAM_EVENT* Event
) {
UNREFERENCED_PARAMETER(Context);
switch (Event->Type) {
case QUIC_STREAM_EVENT_SEND_COMPLETE:
free(Event->SEND_COMPLETE.ClientContext);
printf("[流][%p] 数据已发送\n", Stream);
break;
case QUIC_STREAM_EVENT_RECEIVE:
if (Event->RECEIVE.TotalBufferLength > 0) {
char* buffer = (char*)malloc(Event->RECEIVE.TotalBufferLength + 1);
uint32_t offset = 0;
for (uint32_t i = 0; i < Event->RECEIVE.BufferCount; ++i) {
memcpy(buffer + offset,
Event->RECEIVE.Buffers[i].Buffer,
Event->RECEIVE.Buffers[i].Length);
offset += Event->RECEIVE.Buffers[i].Length;
}
buffer[Event->RECEIVE.TotalBufferLength] = '\0';
printf("收到消息: %s\n", buffer);
// 回显给客户端
void* SendBufferRaw = malloc(sizeof(QUIC_BUFFER) + Event->RECEIVE.TotalBufferLength);
if (SendBufferRaw != NULL) {
QUIC_BUFFER* SendBuffer = (QUIC_BUFFER*)SendBufferRaw;
SendBuffer->Buffer = (uint8_t*)SendBufferRaw + sizeof(QUIC_BUFFER);
SendBuffer->Length = Event->RECEIVE.TotalBufferLength;
memcpy(SendBuffer->Buffer, buffer, Event->RECEIVE.TotalBufferLength);
MsQuic->StreamSend(Stream, SendBuffer, 1, QUIC_SEND_FLAG_NONE, SendBuffer);
}
free(buffer);
}
break;
case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN:
printf("[流][%p] 对端关闭发送\n", Stream);
break;
case QUIC_STREAM_EVENT_PEER_SEND_ABORTED:
printf("[流][%p] 对端中止发送\n", Stream);
MsQuic->StreamShutdown(Stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0);
break;
case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE:
printf("[流][%p] 流已关闭\n", Stream);
MsQuic->StreamClose(Stream);
break;
default:
break;
}
return QUIC_STATUS_SUCCESS;
}
QUIC_STATUS QUIC_API ServerConnectionCallback(
HQUIC Connection,
void* Context,
QUIC_CONNECTION_EVENT* Event
) {
UNREFERENCED_PARAMETER(Context);
switch (Event->Type) {
case QUIC_CONNECTION_EVENT_CONNECTED:
printf("[连接][%p] 已连接\n", Connection);
MsQuic->ConnectionSendResumptionTicket(Connection, QUIC_SEND_RESUMPTION_FLAG_NONE, 0, NULL);
break;
case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT:
if (Event->SHUTDOWN_INITIATED_BY_TRANSPORT.Status == QUIC_STATUS_CONNECTION_IDLE) {
printf("[连接][%p] 空闲超时关闭\n", Connection);
}
else {
printf("[连接][%p] 传输层关闭,错误码: 0x%x\n", Connection, Event->SHUTDOWN_INITIATED_BY_TRANSPORT.Status);
}
break;
case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER:
printf("[连接][%p] 对端关闭,错误码: 0x%llu\n", Connection, (unsigned long long)Event->SHUTDOWN_INITIATED_BY_PEER.ErrorCode);
break;
case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE:
printf("[连接][%p] 连接已关闭\n", Connection);
MsQuic->ConnectionClose(Connection);
break;
case QUIC_CONNECTION_EVENT_PEER_STREAM_STARTED:
printf("[流][%p] 对端创建新流\n", Event->PEER_STREAM_STARTED.Stream);
MsQuic->SetCallbackHandler(Event->PEER_STREAM_STARTED.Stream, (void*)ServerStreamCallback, nullptr);
break;
case QUIC_CONNECTION_EVENT_RESUMED:
printf("[连接][%p] 连接已恢复\n", Connection);
break;
default:
break;
}
return QUIC_STATUS_SUCCESS;
}
QUIC_STATUS QUIC_API ServerListenerCallback(
HQUIC Listener,
void* Context,
QUIC_LISTENER_EVENT* Event
) {
UNREFERENCED_PARAMETER(Context);
QUIC_STATUS Status = QUIC_STATUS_NOT_SUPPORTED;
switch (Event->Type) {
case QUIC_LISTENER_EVENT_NEW_CONNECTION:
MsQuic->SetCallbackHandler(Event->NEW_CONNECTION.Connection, (void*)ServerConnectionCallback, nullptr);
Status = MsQuic->ConnectionSetConfiguration(Event->NEW_CONNECTION.Connection, ServerConfiguration);
break;
default:
break;
}
return Status;
}
// 客户端回调函数
QUIC_STATUS QUIC_API ClientStreamCallback(
HQUIC Stream,
void* Context,
QUIC_STREAM_EVENT* Event
) {
UNREFERENCED_PARAMETER(Context);
switch (Event->Type) {
case QUIC_STREAM_EVENT_SEND_COMPLETE:
free(Event->SEND_COMPLETE.ClientContext);
printf("[流][%p] 数据已发送\n", Stream);
break;
case QUIC_STREAM_EVENT_RECEIVE:
if (Event->RECEIVE.TotalBufferLength > 0) {
char* buffer = (char*)malloc(Event->RECEIVE.TotalBufferLength + 1);
uint32_t offset = 0;
for (uint32_t i = 0; i < Event->RECEIVE.BufferCount; ++i) {
memcpy(buffer + offset,
Event->RECEIVE.Buffers[i].Buffer,
Event->RECEIVE.Buffers[i].Length);
offset += Event->RECEIVE.Buffers[i].Length;
}
buffer[Event->RECEIVE.TotalBufferLength] = '\0';
printf("收到服务器回显: %s\n", buffer);
free(buffer);
}
break;
case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN:
printf("[流][%p] 对端关闭发送\n", Stream);
break;
case QUIC_STREAM_EVENT_PEER_SEND_ABORTED:
printf("[流][%p] 对端中止发送\n", Stream);
break;
case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE:
printf("[流][%p] 流已关闭\n", Stream);
MsQuic->StreamClose(Stream);
break;
default:
break;
}
return QUIC_STATUS_SUCCESS;
}
QUIC_STATUS QUIC_API ClientConnectionCallback(
HQUIC Connection,
void* Context,
QUIC_CONNECTION_EVENT* Event
) {
UNREFERENCED_PARAMETER(Context);
switch (Event->Type) {
case QUIC_CONNECTION_EVENT_CONNECTED:
printf("[连接][%p] 已连接\n", Connection);
break;
case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT:
if (Event->SHUTDOWN_INITIATED_BY_TRANSPORT.Status == QUIC_STATUS_CONNECTION_IDLE) {
printf("[连接][%p] 空闲超时关闭\n", Connection);
}
else {
printf("[连接][%p] 传输层关闭,错误码: 0x%x\n", Connection, Event->SHUTDOWN_INITIATED_BY_TRANSPORT.Status);
}
break;
case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER:
printf("[连接][%p] 对端关闭,错误码: 0x%llu\n", Connection, (unsigned long long)Event->SHUTDOWN_INITIATED_BY_PEER.ErrorCode);
break;
case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE:
printf("[连接][%p] 连接已关闭\n", Connection);
MsQuic->ConnectionClose(Connection);
break;
default:
break;
}
return QUIC_STATUS_SUCCESS;
}
bool LoadServerConfiguration() {
QUIC_CREDENTIAL_CONFIG CredConfig;
memset(&CredConfig, 0, sizeof(CredConfig));
CredConfig.Flags = QUIC_CREDENTIAL_FLAG_NONE;
CredConfig.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE;
QUIC_CERTIFICATE_FILE CertFile;
memset(&CertFile, 0, sizeof(CertFile));
CertFile.CertificateFile = "server.cert";
CertFile.PrivateKeyFile = "server.key";
CredConfig.CertificateFile = &CertFile;
printf("正在从 %s 加载证书\n", CertFile.CertificateFile);
printf("正在从 %s 加载私钥\n", CertFile.PrivateKeyFile);
QUIC_STATUS Status = MsQuic->ConfigurationLoadCredential(
ServerConfiguration,
&CredConfig);
if (QUIC_FAILED(Status)) {
printf("加载证书失败,错误码: %d\n", Status);
return false;
}
printf("证书加载成功\n");
return true;
}
bool LoadClientConfiguration() {
QUIC_SETTINGS Settings = { 0 };
Settings.IdleTimeoutMs = IdleTimeoutMs;
Settings.IsSet.IdleTimeoutMs = TRUE;
QUIC_CREDENTIAL_CONFIG CredConfig;
memset(&CredConfig, 0, sizeof(CredConfig));
CredConfig.Type = QUIC_CREDENTIAL_TYPE_NONE;
CredConfig.Flags = QUIC_CREDENTIAL_FLAG_CLIENT |
QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION;
QUIC_STATUS Status = QUIC_STATUS_SUCCESS;
if (QUIC_FAILED(Status = MsQuic->ConfigurationOpen(
Registration,
&Alpn,
1,
&Settings,
sizeof(Settings),
NULL,
&ClientConfiguration))) {
printf("打开客户端配置失败,错误码: %d\n", Status);
return false;
}
if (QUIC_FAILED(Status = MsQuic->ConfigurationLoadCredential(
ClientConfiguration,
&CredConfig))) {
printf("加载客户端凭证失败,错误码: %d\n", Status);
return false;
}
return true;
}
void StartServer() {
QUIC_STATUS Status;
QUIC_SETTINGS Settings = { 0 };
QUIC_ADDR Address = { 0 };
Settings.IdleTimeoutMs = IdleTimeoutMs;
Settings.IsSet.IdleTimeoutMs = TRUE;
Settings.PeerBidiStreamCount = 100;
Settings.IsSet.PeerBidiStreamCount = TRUE;
Settings.PeerUnidiStreamCount = 100;
Settings.IsSet.PeerUnidiStreamCount = TRUE;
Settings.SendBufferingEnabled = FALSE;
Settings.IsSet.SendBufferingEnabled = TRUE;
QuicAddrSetFamily(&Address, QUIC_ADDRESS_FAMILY_INET);
QuicAddrSetPort(&Address, DEFAULT_PORT);
if (QUIC_FAILED(Status = MsQuic->ConfigurationOpen(
Registration,
&Alpn,
1,
&Settings,
sizeof(Settings),
NULL,
&ServerConfiguration))) {
printf("打开服务器配置失败,错误码: %d\n", Status);
return;
}
if (!LoadServerConfiguration()) {
return;
}
if (QUIC_FAILED(Status = MsQuic->ListenerOpen(
Registration,
ServerListenerCallback,
NULL,
&Listener))) {
printf("打开监听器失败,错误码: %d\n", Status);
return;
}
if (QUIC_FAILED(Status = MsQuic->ListenerStart(
Listener,
&Alpn,
1,
&Address))) {
printf("启动监听器失败,错误码: %d\n", Status);
return;
}
printf("服务器正在监听端口 %d\n", DEFAULT_PORT);
}
void ClientSend(HQUIC Connection, const char* message) {
QUIC_STATUS Status;
HQUIC Stream = NULL;
if (QUIC_FAILED(Status = MsQuic->StreamOpen(
Connection,
QUIC_STREAM_OPEN_FLAG_NONE,
ClientStreamCallback,
NULL,
&Stream))) {
printf("创建流失败,错误码: %d\n", Status);
return;
}
printf("[流][%p] 正在启动...\n", Stream);
if (QUIC_FAILED(Status = MsQuic->StreamStart(
Stream,
QUIC_STREAM_START_FLAG_NONE))) {
printf("启动流失败,错误码: %d\n", Status);
MsQuic->StreamClose(Stream);
return;
}
void* SendBufferRaw = malloc(sizeof(QUIC_BUFFER) + strlen(message));
if (SendBufferRaw == NULL) {
printf("发送缓冲区分配失败!\n");
MsQuic->StreamShutdown(Stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0);
return;
}
QUIC_BUFFER* SendBuffer = (QUIC_BUFFER*)SendBufferRaw;
SendBuffer->Buffer = (uint8_t*)SendBufferRaw + sizeof(QUIC_BUFFER);
SendBuffer->Length = (uint32_t)strlen(message);
memcpy(SendBuffer->Buffer, message, strlen(message));
printf("[流][%p] 正在发送数据...\n", Stream);
if (QUIC_FAILED(Status = MsQuic->StreamSend(
Stream,
SendBuffer,
1,
QUIC_SEND_FLAG_NONE,
SendBuffer))) {
printf("发送数据失败,错误码: %d\n", Status);
free(SendBufferRaw);
MsQuic->StreamShutdown(Stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0);
}
}
void ClientConnect(const char* message) {
if (!LoadClientConfiguration()) {
return;
}
QUIC_STATUS Status;
HQUIC Connection = NULL;
if (QUIC_FAILED(Status = MsQuic->ConnectionOpen(
Registration,
ClientConnectionCallback,
NULL,
&Connection))) {
printf("创建连接失败,错误码: %d\n", Status);
return;
}
printf("[连接][%p] 正在连接...\n", Connection);
if (QUIC_FAILED(Status = MsQuic->ConnectionStart(
Connection,
ClientConfiguration,
QUIC_ADDRESS_FAMILY_INET,
"localhost",
DEFAULT_PORT))) {
printf("启动连接失败,错误码: %d\n", Status);
MsQuic->ConnectionClose(Connection);
return;
}
// 等待连接建立
Sleep(100);
// 发送消息
ClientSend(Connection, message);
// 等待响应
Sleep(100);
// 关闭连接
MsQuic->ConnectionClose(Connection);
}
int main() {
QUIC_STATUS Status;
if (QUIC_FAILED(Status = MsQuicOpen2(&MsQuic))) {
printf("MsQuicOpen2 失败,错误码: %d\n", Status);
return -1;
}
if (QUIC_FAILED(Status = MsQuic->RegistrationOpen(&RegConfig, &Registration))) {
printf("RegistrationOpen 失败,错误码: %d\n", Status);
return -1;
}
// 启动服务器
StartServer();
printf("MsQuic 回显服务器控制台\n");
printf("输入 'exit' 退出程序\n");
char input[BUFFER_SIZE];
while (true) {
printf("请输入要发送的消息: ");
if (fgets(input, BUFFER_SIZE, stdin) == NULL) {
break;
}
// 移除换行符
input[strcspn(input, "\n")] = 0;
if (strcmp(input, "exit") == 0) {
break;
}
// 发送消息
ClientConnect(input);
}
// 清理资源
if (Listener != NULL) {
MsQuic->ListenerClose(Listener);
}
if (ServerConfiguration != NULL) {
MsQuic->ConfigurationClose(ServerConfiguration);
}
if (ClientConfiguration != NULL) {
MsQuic->ConfigurationClose(ClientConfiguration);
}
if (Registration != NULL) {
MsQuic->RegistrationClose(Registration);
}
if (MsQuic != NULL) {
MsQuicClose(MsQuic);
}
return 0;
}