vs2022 cmake 的 socket 程序 demo 模拟最简单 web 处理(windows/linux 兼容)

第一次使用 cmake,做一个兼容 windows/linux 的小程序,使用 socket 模拟简单的 web 处理

为了兼容编译,遇到不少坑,终于完成了。代码现在放出来给大家参考。

https://e.coding.net/greenery2/demowebapp/demowebapp.git

使用效果截图:

bash 复制代码
┌─[yhen1@devops]─[~/.vs/CDemoWebApp/out/build/linux-debug]
└──╼ $./CDemoWebApp 8888
Demo web app. port: 8888
socket init: 3
=====================================================
waiting accept [8888]...
^CExit...
closesocket: 3 ...
bash 复制代码
┌─[yhen1@devops]─[~]
└──╼ $curl -vv 127.0.0.1:8888
* About to connect() to 127.0.0.1 port 8888 (#0)
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8888 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 127.0.0.1:8888
> Accept: */*
>
< HTTP/1.1 200 OK
* no chunk, no close, no size. Assume close to signal end
<
* Closing connection 0
OK

CMakeLists.txt

cpp 复制代码
# CMakeList.txt: CDemoWebApp 的 CMake 项目,在此处包括源代码并定义
# 项目特定的逻辑。
#
cmake_minimum_required (VERSION 3.8)

# Enable Hot Reload for MSVC compilers if supported.
if (POLICY CMP0141)
  cmake_policy(SET CMP0141 NEW)
  set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
endif()

project ("CDemoWebApp")

# 将源代码添加到此项目的可执行文件。
add_executable (CDemoWebApp "CDemoWebApp.cpp" "CDemoWebApp.h")

if (CMAKE_VERSION VERSION_GREATER 3.12)
  set_property(TARGET CDemoWebApp PROPERTY CXX_STANDARD 20)
endif()

# TODO: 如有需要,请添加测试并安装目标。

if(WIN32)
  target_link_libraries(CDemoWebApp wsock32 ws2_32)
endif()

CDemoWebApp.cpp

cpp 复制代码
// CDemoWebApp.cpp: 定义应用程序的入口点。
//

#include "CDemoWebApp.h"
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>

#ifdef WIN32
#include <winsock2.h>
#include <windows.h>
#define socklen_t int
#else
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define closesocket close
#define SOCKET int
#endif
#include <fcntl.h>
#include <errno.h>
#include <signal.h>



#define QUEUE 20  //连接请求队列
const char* DEFAULT_HTTP_RESPONE = "HTTP/1.1 200 OK\r\n\r\nOK";

using namespace std;
SOCKET sockfd, connfd;    // 定义服务端套接字和客户端套接字
bool appExitFlag = false;
void SigCatch(int sigNum)	//信号捕捉函数(捕获Ctrl+C)
{
    printf("Exit...\n");
    if (sockfd != -1)
    {
        appExitFlag = true;
        closesocket(sockfd);
        printf("closesocket: %d ...\n", sockfd);
        sockfd = -1;
    }
    exit(0);
}

int main(int argc, char** argv)
{
    char buffer[1024 * 8];

    u_short port = 80;
    if (argc >= 2) {
        port = atoi(argv[1]);
        if (port < 80) port = 80;
    }
    printf("Demo web app. port: %d\n", port);
    signal(SIGINT, SigCatch);	//注册信号捕获函数

    //printf("AF_INET: %d\n", AF_INET);  //IPv4协议
    //printf("SOCK_STREAM: %d\n", SOCK_STREAM); //字节流套接字
#ifdef WIN32
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
    sockfd = socket(AF_INET, SOCK_STREAM, 0);  //若成功则返回一个sockfd (套接字描述符)
    printf("socket init: %d\n", sockfd);
    struct sockaddr_in server_sockaddr;  //一般是储存地址和端口,用于信息的显示及存储作用

    //下面设置sockaddr_in 结构体中相关参数
    server_sockaddr.sin_family = AF_INET;
    server_sockaddr.sin_port = htons(port); //将一个无符号短整型数值转换为网络字节序,即大端模式
    //printf("INADDR_ANY: %d\n", INADDR_ANY);
    //INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或"所有地址"、"任意地址"。
    //一般来说,在各个系统中均定义成为0值。
    server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);  //将主机无符号长整型数转换成网络字节顺序。

    if (bind(sockfd, (struct sockaddr*)&server_sockaddr, sizeof(server_sockaddr)) == -1)
    {
        perror("bind error!\n");
        exit(1);
    }

    if (listen(sockfd, QUEUE) == -1)
    {
        perror("listen error!\n");
        exit(1);
    }

    struct sockaddr_in client_addr;
    socklen_t length = sizeof(client_addr);

    while (1) {
        printf("=====================================================\n");
        printf("waiting accept [%d]...\n", port);

        //成功返回非负描述字,出错返回-1
        int conn = accept(sockfd, (struct sockaddr*)&client_addr, &length);
        //如果accept成功,那么其返回值是由内核自动生成的一个全新描述符,代表与所返回客户的TCP连接
        //accept之后就会用新的套接字conn
        if (conn < 0)
        {
            // 如果正在退出系统,那么就不用打印错误
            if (appExitFlag) break;

            perror("connect error!\n");
            exit(1);
        }
        printf("accept ok: %d...\n", conn);

        memset(buffer, 0, sizeof(buffer));
        int len = recv(conn, buffer, sizeof(buffer), 0);   //从TCP连接的另一端接收数据。
        /*该函数的第一个参数指定接收端套接字描述符;
        第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
        第三个参数指明buf的长度;
        第四个参数一般置0*/

        printf("%s", buffer);//如果有收到数据则输出数据
        // 返回默认 http respone
        int ret = send(conn, DEFAULT_HTTP_RESPONE, strlen(DEFAULT_HTTP_RESPONE), 0);//向TCP连接的另一端发送数据。
        printf("respone: [%d]\n%s\n", ret, DEFAULT_HTTP_RESPONE);
        closesocket(conn);   //因为accept函数连接成功后还会生成一个新的套接字描述符,结束后也需要关闭
        printf("close connect: %d\n", conn);
    }
    closesocket(sockfd);     //关闭socket套接字描述符

	return 0;
}

注意事项

头文件不一样

windows的和linux的不一样。我用宏判断分别导入不同的头文件

cpp 复制代码
#ifdef WIN32
#include <winsock2.h>
#include <windows.h>
#else
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#endif

注意,windows下面要先 include <winsock2.h> 再 include <windows.h>,否则会出现很多重复定义的错误

部分函数、类型不一样

windows的和linux的不一样。我简单统一了一下

cpp 复制代码
#ifdef WIN32
#define socklen_t int
#else
#define closesocket close
#define SOCKET int
#endif

windows 下面socket要加lib

我写在 CMakeLists.txt,通过宏判断,进行lib导入

target_link_libraries(CDemoWebApp wsock32 ws2_32)

windows 下面socket要初始化

cpp 复制代码
#ifdef WIN32
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
相关推荐
律宏阔2 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔2 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴2 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千2 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei372 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei372 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei372 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
GeW2 小时前
制造业高端化,为什么必须重估Linux和数据库?
linux
GeW2 小时前
工业场景中的Linux与数据库选型:高端化转型的技术底座
linux
yunwei372 小时前
eBPF 开发者教程: 简单的 XDP 负载均衡器
linux·后端·性能优化