config.h
#pragma once
#include <array>
#include <memory>
#include <vector>
#include <cstdint>
#include <asio2/udp/udp_cast.hpp>
#include <asio2/udp/udp_client.hpp>
#include <asio2/udp/udp_server.hpp>
#include <asio2/udp/udp_session.hpp>
using byte = uint8_t;
using uint16 = uint16_t;
using uint32 = uint32_t;
using uint64 = uint64_t;
constexpr uint32 MAX_BUFFER_SIZE = 1024;
constexpr uint64 CRC32_SETENCE = 0x104C11DB7;
constexpr uint32 CRC32_GENERATE = CRC32_SETENCE & 0xFFFF;
enum class NetworkLayerStatus {
PHYSICAL_LAYER_READY,
NETWORK_LAYER_READY,
FRAME_RECEIVED,
DATA_TIMEOUT,
ACK_TIMEOUT
};
constexpr uint32_t MAX_SEQ = 15;
constexpr uint32_t FRAME_LEN = 256;
constexpr uint32_t CACHE_SIZE = (MAX_SEQ + 1) / 2;
struct Frame {
using uint8 = unsigned char;
uint8 type;
uint8 ack;
uint8 seqNum;
std::array<uint8, FRAME_LEN> data;
uint8 CRC32;
};
main.cpp
#include "ProtocolApp/ProtocolApp.h"
int main(int argc, char* argv[]) {
ProtocolApp app("1919");
app.startServer().detach();
app.startClient().detach();
while (true) {
std::this_thread::yield();
}
return 0;
}
ProtocolApp.h
#pragma once
#include <config.h>
class ProtocolApp {
public:
using strView = std::string_view;
explicit ProtocolApp(const strView& port):
_port(port)
{
std::thread(&ProtocolApp::InitServer, this).detach();
std::thread(&ProtocolApp::InitClient, this).detach();
}
virtual ~ProtocolApp() {
_server.stop();
_client.stop();
}
static void AddCRC32Tail(std::vector<byte>& data);
static bool CheckCRC32Tail(const std::vector<byte>& data);
void InitServer();
void InitClient();
[[nodiscard]] std::thread startServer() {
return std::thread([this] {
_server.start("127.0.0.1", _port);
});
}
[[nodiscard]] std::thread startClient() {
return std::thread([this] {
if (!_client.start("127.0.0.1", _port))
printf("start failure : %d %s\n", asio2::last_error_val(), asio2::last_error_msg().c_str());
});
}
private:
strView _port;
std::mutex _initMutex;
asio2::udp_server _server;
asio2::udp_client _client;
};
protocolApp.cpp
#include "ProtocolApp.h"
void ProtocolApp::InitServer() {
_server.bind_recv([](std::shared_ptr<asio2::udp_session>& session_ptr, std::string_view s)
{
printf("recv : %u %.*s\n", (unsigned)s.size(), (int)s.size(), s.data());
session_ptr->no_delay(true);
session_ptr->send(s, []() {});
}).bind_connect([](auto& session_ptr)
{
printf("client enter : %s %u %s %u\n", session_ptr->remote_address().c_str(), session_ptr->remote_port(),
session_ptr->local_address().c_str(), session_ptr->local_port());
}).bind_disconnect([](auto& session_ptr)
{
printf("client leave : %s %u %s\n", session_ptr->remote_address().c_str(),
session_ptr->remote_port(), asio2::last_error_msg().c_str());
}).bind_handshake([](auto& session_ptr, asio::error_code ec)
{
printf("client handshake : %d %s\n", asio2::last_error_val(), asio2::last_error_msg().c_str());
}).bind_start([this](asio::error_code ec)
{
if (asio2::get_last_error())
printf("start udp server failure : %d %s\n", asio2::last_error_val(), asio2::last_error_msg().c_str());
else
printf("start udp server success : %s %u\n", _server.listen_address().c_str(), _server.listen_port());
}).bind_stop([&](asio::error_code ec)
{
printf("stop : %d %s\n", asio2::last_error_val(), asio2::last_error_msg().c_str());
}).bind_init([&]()
{
});
}
void ProtocolApp::InitClient() {
_client.connect_timeout(std::chrono::seconds(3));
_client.local_endpoint(asio::ip::udp::v4(), 15678);
_client.bind_connect([&](asio::error_code ec)
{
if (asio2::get_last_error())
printf("connect failure : %d %s\n", asio2::last_error_val(), asio2::last_error_msg().c_str());
else
printf("connect success : %s %u\n", _client.local_address().c_str(), _client.local_port());
_client.send(std::string("<abcdefghijklmnopqrstovuxyz0123456789>"));
}).bind_disconnect([](asio::error_code ec)
{
printf("disconnect : %d %s\n", asio2::last_error_val(), asio2::last_error_msg().c_str());
}).bind_recv([&](std::string_view sv)
{
printf("recv : %u %.*s\n", (unsigned)sv.size(), (int)sv.size(), sv.data());
std::string s;
s += '<';
int len = 33 + std::rand() % (126 - 33);
for (int i = 0; i < len; i++)
{
s += (char)((std::rand() % 26) + 'a');
}
s += '>';
_client.send(std::move(s));
});
}
void ProtocolApp::AddCRC32Tail(std::vector<byte>& data) {
uint32 crc = ~0;
size_t dataSize = data.size();
std::array<byte, 4> crcTail = { 0x0, 0x0, 0x0, 0x0 };
const byte* current = static_cast<byte*>(data.data());
while (dataSize-- != 0) {
byte s = byte(crc) ^ *current++;
uint32 low = (s ^ (s << 6)) & 0xFF;
uint32 tmp = (low * ((1 << 23) + (1 << 14) + (1 << 2)));
crc = (crc >> 8) ^
(low * ((1 << 24) + (1 << 16) + (1 << 8))) ^
(tmp) ^
(tmp >> 1) ^
(low * ((1 << 20) + (1 << 12))) ^
(low << 19) ^
(low << 17) ^
(low >> 2);
}
crc = ~crc;
crcTail[0] = static_cast<byte>(crc >> 24);
crcTail[1] = static_cast<byte>(crc >> 16 & 0xFF);
crcTail[2] = static_cast<byte>(crc >> 8 & 0xFF);
crcTail[3] = static_cast<byte>(crc & 0xFF);
for (auto code : crcTail) {
data.push_back(code);
}
}
bool ProtocolApp::CheckCRC32Tail(const std::vector<byte>& data) {
uint32 crc = ~0;
size_t dataSize = data.size() - 4;
std::array<byte, 4> crcTail = {
data[data.size() - 4],
data[data.size() - 3],
data[data.size() - 2],
data[data.size() - 1]
};
const byte* current = (byte*)data.data();
while (dataSize-- != 0) {
byte s = byte(crc) ^ *current++;
uint32 low = (s ^ (s << 6)) & 0xFF;
uint32 tmp = (low * ((1 << 23) + (1 << 14) + (1 << 2)));
crc = (crc >> 8) ^
(low * ((1 << 24) + (1 << 16) + (1 << 8))) ^
(tmp) ^
(tmp >> 1) ^
(low * ((1 << 20) + (1 << 12))) ^
(low << 19) ^
(low << 17) ^
(low >> 2);
}
crc = ~crc;
return !(
crcTail[0] ^ static_cast<byte>(crc >> 24) ||
crcTail[1] ^ static_cast<byte>(crc >> 16 & 0xFF) ||
crcTail[2] ^ static_cast<byte>(crc >> 8 & 0xFF) ||
crcTail[3] ^ static_cast<byte>(crc & 0xFF)
);
}