当前位置: 首页 > news >正文

 UNIX网络编程笔记:TCP客户/服务器程序示例

服务器实例

有个著名的项目,tiny web,本项目将其改到windows下,并使用RAII重构,编写过程中对于内存泄漏确实很头疼,还没写完,后面会继续更:

#include <iostream>
#include <vector>
#include <stdexcept>
#include <string>
#include<ranges>
#include<format>
#include <stdio.h>
#include <WinSock2.h>
#include <Windows.h>
#pragma comment(lib, "WS2_32.lib")

class WSAInitializer {
public:
    WSAInitializer() {
        WSADATA data;
        int ret = WSAStartup(MAKEWORD(2, 2), &data);
        if (ret != 0) {
            throw std::runtime_error("WSAStartup failed. Error code: " + std::to_string(ret));
        }
    }
    ~WSAInitializer() {
        WSACleanup();
    }
    WSAInitializer(const WSAInitializer&) = delete;
    WSAInitializer& operator=(const WSAInitializer&) = delete;
};

class Socket {
public:
    Socket() = default;
    Socket(int domain, int type, int protocol) {
        sock_ = socket(domain, type, protocol);
        if (sock_ == INVALID_SOCKET) {
            throw std::runtime_error("Socket creation failed. Error code: " + std::to_string(WSAGetLastError()));
        }
    }
    explicit Socket(SOCKET s) : sock_(s) {}
    ~Socket() {
        if (sock_ != INVALID_SOCKET) {
            closesocket(sock_);
        }
    }
    Socket(Socket&& other) noexcept : sock_(other.sock_) {
        other.sock_ = INVALID_SOCKET;
    }
    Socket& operator=(Socket&& other) noexcept {
        if (this != &other) {
            closesocket(sock_);
            sock_ = other.sock_;
            other.sock_ = INVALID_SOCKET;
        }
        return *this;
    }
    SOCKET get() const { return sock_; }
    operator SOCKET() const { return sock_; }
    Socket(const Socket&) = delete;
    Socket& operator=(const Socket&) = delete;
private:
    SOCKET sock_ = INVALID_SOCKET;
};

Socket startup(unsigned short* port) {
    Socket server_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    int opt = 1;
    if (setsockopt(server_socket.get(), SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&opt), sizeof(opt)) == SOCKET_ERROR) {
        throw std::runtime_error("setsockopt failed. Error code: " + std::to_string(WSAGetLastError()));
    }

    sockaddr_in server_addr = {};
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(*port);
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(server_socket.get(), reinterpret_cast<const sockaddr*>(&server_addr), sizeof(server_addr)) == SOCKET_ERROR) {
        throw std::runtime_error("bind failed. Error code: " + std::to_string(WSAGetLastError()));
    }

    if (*port == 0) {
        sockaddr_in name;
        int nameLen = sizeof(name);
        if (getsockname(server_socket.get(), reinterpret_cast<sockaddr*>(&name), &nameLen) == SOCKET_ERROR) {
            throw std::runtime_error("getsockname failed. Error code: " + std::to_string(WSAGetLastError()));
        }
        *port = ntohs(name.sin_port);
    }

    if (listen(server_socket.get(), 5) == SOCKET_ERROR) {
        throw std::runtime_error("listen failed. Error code: " + std::to_string(WSAGetLastError()));
    }

    return server_socket;
}

int get_line(SOCKET sock, std::vector<char>& buff, int size) {
    buff.resize(size);
    char c = 0;
    int i = 0;
    while (i < size - 1 && c != '\n') {
        int n = recv(sock, &c, 1, 0);
        if (n > 0) {
            if (c == '\r') {
                n = recv(sock, &c, 1, MSG_PEEK);
                if (n > 0 && c == '\n') {
                    recv(sock, &c, 1, 0);
                } else {
                    c = '\n';
                }
            }
            buff[i++] = c;
        } else {
            c = '\n';
        }
    }
    buff[i] = '\0';
    return i;
}

void unimplemented(SOCKET client) {
    const char* response = "HTTP/1.1 501 Not Implemented\r\n"
                           "Content-Type: text/html\r\n"
                           "\r\n"
                           "<html><head><title>Not Implemented</title></head>"
                           "<body><h1>501 Not Implemented</h1></body></html>";
    send(client, response, strlen(response), 0);
}

DWORD WINAPI accept_request(LPVOID arg) {
    std::unique_ptr<Socket> client_socket(static_cast<Socket*>(arg));
    SOCKET client = client_socket->get();

    std::vector<char> buff(1024);
    int numchars = get_line(client, buff, buff.size());

    // 示例:解析请求方法
    char method[255] = {0};
    int i = 0, j = 0;
    while (!isspace(buff[j]) && i < sizeof(method) - 1) {
        method[i++] = buff[j++];
    }

    // 示例:检查支持的HTTP方法
    if (_stricmp(method, "GET") && _stricmp(method, "POST")) {
        unimplemented(client);
        return 0;
    }

    // 示例:解析URL(简化版)
    std::vector<char> url(255, 0);
    i = 0;
    while (isspace(buff[j]) && j < buff.size()) j++;
    while (!isspace(buff[j]) && i < url.size() - 1 && j < buff.size()) {
        url[i++] = buff[j++];
    }

    // 示例:发送响应(实际应处理请求)
    const char* response = "HTTP/1.1 200 OK\r\n"
                           "Content-Type: text/html\r\n"
                           "\r\n"
                           "<html><head><title>Test</title></head>"
                           "<body><h1>Hello World</h1></body></html>";
    send(client, response, strlen(response), 0);

    return 0;
}

int main() {
    try {
        WSAInitializer wsa;
        unsigned short port = 8880;
        Socket server_socket = startup(&port);
        std::cout << "Server started on port " << port << std::endl;

        while (true) {
            sockaddr_in client_addr;
            int addr_len = sizeof(client_addr);
            SOCKET client_sock = accept(server_socket.get(), reinterpret_cast<sockaddr*>(&client_addr), &addr_len);
            
            if (client_sock == INVALID_SOCKET) {
                throw std::runtime_error("accept failed. Error code: " + std::to_string(WSAGetLastError()));
            }

            Socket* client_socket = new Socket(client_sock);
            HANDLE hThread = CreateThread(nullptr, 0, accept_request, client_socket, 0, nullptr);
            
            if (!hThread) {
                delete client_socket;
                throw std::runtime_error("CreateThread failed. Error code: " + std::to_string(GetLastError()));
            }
            CloseHandle(hThread);
        }
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    return 0;
}

相关文章:

  • 基于springboot的房屋租赁系统(028)
  • 电机控制常见面试问题(十七)
  • JetsonNano —— 4、Windows下对JetsonNano板卡烧录刷机Ubuntu20.04版本(官方教程)
  • 『uniapp』简单文本复制文字 富文本内容复制文字(详细图文注释)
  • GOC按钮点击器
  • 【JavaEE进阶】部署Web项目到Linux服务器
  • cartographer中地图转换
  • VMware虚拟机安装银河麒麟操作系统v10
  • Mininet源码框架概述
  • 深入 C++11:移动语义、Lambda表达式与新特性全面解析
  • 基于linux平台的C语言入门教程(3)代码注释
  • 【计算机网络原理】选择题+简答题
  • AI密码学
  • 基于linux平台的C语言入门教程(8)算术运算符
  • Qt+FFmpeg+SDL2播放进度显示及定位播放
  • Java多线程与高并发专题——Future 是什么?
  • MySQL 事务(Transaction)详解
  • 扣子平台知识库不能上传成功
  • 单表达式倒计时工具:datetime的极度优雅(DeepSeek)
  • python+ffmpeg给音频添加背景音乐
  • 辽宁辽阳市白塔区一饭店火灾事故举行新闻发布会,现场为遇难者默哀
  • 辽宁省全力开展辽阳一饭店火灾事故救援处置工作
  • 俄宣布停火三天,外交部:希望各方继续通过对话谈判解决危机
  • 对谈|李钧鹏、周忆粟:安德鲁·阿伯特过程社会学的魅力
  • 一季度我国服务进出口总额19741.8亿元,同比增长8.7%
  • 夜读丨怀念那个写信的年代