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

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

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

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

使用效果截图:

┌─[yhen1@devops]─[~/.vs/CDemoWebApp/out/build/linux-debug]
└──╼ $./CDemoWebApp 8888
Demo web app. port: 8888
socket init: 3
=====================================================
waiting accept [8888]...
^CExit...
closesocket: 3 ...
┌─[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

# 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

// 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的不一样。我用宏判断分别导入不同的头文件

#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的不一样。我简单统一了一下

#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要初始化

#ifdef WIN32
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值