MsQuick编译和使用
MsQuick编译和使用
- 编译
- 克隆代码
- 使用cmake+vs2022编译
- 使用示例
编译
克隆代码
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 1024const 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;
}