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

服务器端的准备工作

  • 1 网络通信初始化
    • (突然忘记了#pragma是做什么的了)
      • 1. #pragma comment(lib, "库名") 的作用
      • 2. 其他常见 #pragma 指令
        • (1) #pragma once
        • (2) #pragma warning
        • (3) #pragma pack
  • 2 创建套接字(Socket)
  • 3 设置套接字属性 :端口可复用(可省略)
    • 端口复用(SO_REUSEADDR)的作用是什么?
    • setsockopt() 是套接字编程中用于配置套接字选项的核心函数:
  • 4 绑定套接字和网络地址
  • 5 动态分配端口(可略)
    • 1. 什么是动态分配端口?
  • 6 监听
  • 服务器端的准备工作

在接受浏览器前端的网页请求之前,服务器端需要做一些准备工作,流程如下:

1 网络通信初始化

//网络通信需要包含头文件,需要加载的库文件
#include<WinSock2.h>
#pragma comment(lib,"WS2_32.lib")
  • WinSock2.h:Windows Sockets 2(Winsock)的头文件,定义了网络编程所需的函数、结构体和常量。

  • #pragma comment(lib, "WS2_32.lib"):指示编译器链接到WS2_32.lib库,确保程序能正确调用Winsock函数。

//1,网络通信初始化(windows)
WSADATA wsaData; // 网络通信相关的版本等信息// 在windows系统使用网络通信,必须先进行网络协议初始化(Linux系统不需要)
int ret = WSAStartup( // WSAStartup 网络通信初始化,MAKEWORD(1, 1),   // 指定使用Windows Sockets规范的1.1版本&wsaData);        // 存储初始化后的版本等信息
if (ret != 0) {return -1;//初始化失败
}

(突然忘记了#pragma是做什么的了)

#pragma 是 C/C++ 中的一种预处理指令,用于向编译器传递特定的控制命令。

1. #pragma comment(lib, "库名") 的作用


#pragma comment(lib, "WS2_32.lib")
  • 功能:告诉编译器在链接阶段需要将 WS2_32.lib 库文件链接到最终的可执行文件中。

  • 为什么需要它:Windows 的 Winsock API 函数(如 WSAStartupsocket)的实现代码位于 WS2_32.lib 库中。如果不链接此库,编译时会报“未解析的外部符号”错误。

2. 其他常见 #pragma 指令

(1) #pragma once
#pragma once
  • 功能:确保头文件只被包含一次,避免重复定义(类似 #ifndef HEADER_H + #define HEADER_H 的传统方式)。
(2) #pragma warning
#pragma warning(disable: 4996) // 禁用警告 C4996
#pragma warning(push, 3)       // 保存当前警告级别,并设置为级别3
#pragma warning(pop)           // 恢复之前的警告级别
  • 功能:控制编译器的警告信息输出。
(3) #pragma pack
#pragma pack(push, 1) // 按1字节对齐结构体
struct MyStruct {char a;int b;
};
#pragma pack(pop)      // 恢复默认对齐方式
  • 功能:控制结构体的内存对齐方式,常用于网络传输或硬件交互时精确控制数据布局。

2 创建套接字(Socket)

socket() 是网络编程中用于**创建套接字(Socket)**的核心函数,它是操作系统提供的底层接口,用于实现不同设备之间的网络通信(如客户端与服务器)
它是网络通信的端点。套接字是网络编程的基础,用于实现 TCP/UDP 通信、监听连接、发送和接收数据等操作。

int socket(int domain, int type, int protocol);
  • 返回值

    • 成功:返回一个文件描述符(非负整数),代表创建的套接字。

    • 失败:返回 -1,并通过 errno 表示错误类型(如权限不足、协议不支持等)。

1. domain(协议族/地址族)
指定套接字使用的网络协议族,决定套接字可处理的地址类型。


2. type(套接字类型)
指定套接字的数据传输方式,决定通信的可靠性和行为。


3. protocol(协议类型)
明确指定要使用的传输层协议。通常设为 0,表示根据 domain 和 type 自动选择协议。


3 设置套接字属性 :端口可复用(可省略)

端口复用(SO_REUSEADDR)的作用是什么?

当服务器程序崩溃或主动关闭后,绑定的端口可能处于 TIME_WAIT 状态(TCP 连接的正常关闭阶段)。此时立即重启服务器会因端口被占用而失败,并提示 Address already in use。设置 SO_REUSEADDR 允许套接字绑定到处于 TIME_WAIT 状态的端口,从而快速重启服务。

setsockopt() 是套接字编程中用于配置套接字选项的核心函数:

int setsockopt(int sockfd,         // 套接字描述符int level,          // 选项的协议层int optname,        // 选项名const void *optval, // 指向选项值的指针socklen_t optlen    // 选项值的长度
);

返回值

  • 成功:返回 0

  • 失败:返回 -1

1. sockfd(套接字描述符)

  • 作用:要设置选项的套接字

2. level(协议层)
指定选项所属的协议层,常见值:


3. optname(选项名)
具体要设置的选项名称,常见选项:


4. optval(选项值)

  • 作用:指向选项值的指针,具体类型由 optname 决定。
    • 常见类型

    • int:如 SO_REUSEADDR 启用时设为 1

    • struct timeval:如超时设置。

    • linger:控制关闭时的延迟行为。

5. optlen(选项值长度)

  • 作用:指定 optval 数据的大小(字节数)。

4 绑定套接字和网络地址

   服务器                           客户端+-------------------+           +-------------------+| 1. 创建 Socket    |           |                   ||   (socket())      |           |                   |+-------------------+           +-------------------+|                              |+-------------------+           +-------------------+| 2. 绑定地址和端口 |           |                   ||   (bind())        |           |                   |+-------------------+           +-------------------+|                              |+-------------------+           +-------------------+| 3. 监听连接      |           | 4. 发起连接       ||   (listen())     | <-------- |   (connect())     |+-------------------+           +-------------------+|                              |+-------------------+           +-------------------+| 5. 接受连接      |           |                   ||   (accept())     | <-------- |                   |+-------------------+           +-------------------+|                              |+-------------------+           +-------------------+| 6. 收发数据      | <-------> | 6. 收发数据       ||   (send/recv)    |           |   (send/recv)     |+-------------------+           +-------------------+|                              |+-------------------+           +-------------------+| 7. 关闭连接      |           | 7. 关闭连接       ||   (close())      |           |   (close())       |+-------------------+           +-------------------+

socket就像电话,bind()相当于给电话分配号码,listen()和accept()是等待来电,connect()是拨号,send()和recv()是通话过程。

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);


 

5 动态分配端口(可略)

1. 什么是动态分配端口?

当服务器将端口号设为 0 时,操作系统会自动选择一个空闲端口分配给套接字。这个过程称为 动态端口分配

getsockname 是一个网络编程函数,用于 获取套接字(Socket)绑定的本地地址信息(IP 地址和端口号)。它的作用类似于 查看电话机的实际分机号
. 使用场景

(1) 动态分配端口

当服务器将端口号设为 0 时,操作系统会自动分配一个可用端口。此时需要通过 getsockname 获取实际分配的端口号。

(2) 绑定到通配地址(INADDR_ANY

如果服务器绑定到所有网络接口(如 Wi-Fi、以太网),可以通过 getsockname 获取实际使用的 IP 地址。

int getsockname(int sockfd,            // 套接字描述符struct sockaddr *addr, // 存储获取的地址信息socklen_t *addrlen     // 地址结构体的长度(输入时为缓冲区大小,输出时为实际大小)
);

返回值

  • 成功:返回 0

  • 失败:返回 -1

6 监听

int listen(int sockfd, int backlog);

 

服务器端的准备工作

#include<stdio.h>//网络通信需要包含头文件,需要加载的库文件
#include<WinSock2.h>
#pragma comment(lib,"WS2_32.lib")void error_die(const char* str)
{perror(str);// 打印错误原因exit(1);
}//实现网络的初始化
//返回值:套接字(服务器端套接字)
//端口
//参数:port  表示端口
//         如果*port的值是0,那么就自动分配一个可用的端口
int startup(unsigned short* port)
{//1,网络通信初始化(windows)WSADATA wsaData; // 网络通信相关的版本等信息// 在windows系统使用网络通信,必须先进行网络协议初始化(Linux系统不需要)int ret = WSAStartup( // WSAStartup 网络通信初始化,MAKEWORD(1, 1),   // 指定使用Windows Sockets规范的1.1版本&wsaData);        // 存储初始化后的版本等信息if (ret != 0) {error_die("wsaData");//初始化失败}//2,创建套接字int server_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);if (server_socket == -1) {//打印错误提示,并结束程序error_die("socket");}//3 设置套接字属性  端口可复用(可省略)// 端口复用int opt = 1;ret = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));if (ret == -1) {error_die("setsockopt");}//4 绑定套接字和网络地址//配置服务器端的网络地址struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));//清零server_addr.sin_family = AF_INET;//网络地址的类型server_addr.sin_port = htons(*port);//转换为网络字节序    //    htons():将端口号从 主机字节序 转换为 网络字节序(大端模式)。//为什么需要转换?//不同计算机的字节序可能不同(如 x86 是小端,网络传输统一用大端),转换保证数据正确解析。server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定到本机所有可用的 IP 地址。if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {error_die("[bind]");}// 5 动态分配端口if (*port == 0) {int namelen = sizeof(server_addr);if (getsockname(server_socket, (struct sockaddr*)&server_addr, &namelen) == -1) {error_die("getsockname");}*port = ntohs(server_addr.sin_port); 转换端口号为主机字节序}//6 创建监听队列if (listen(server_socket, 5) < 0) {error_die("listen");}return(server_socket);}

相关文章:

  • 求解器介绍之gurobi
  • Linux电源管理(6)_Generic PM之挂起功能
  • 【自然语言处理与大模型】LlamaIndex的数据连接器和对话引擎
  • 二、Python变量基础(2)
  • 30天开发操作系统 第27天 -- LDT与库
  • 工业主义与民主的兴衰:历史逻辑与未来危机
  • uniswap v4 合约解析1 pool初始化
  • VTK 数据结构和算法类介绍
  • pyqt写一个单片机配置界面
  • 基于YOLOv的目标检测训练数据构建方法研究—图像采集、标注、划分与增强一体化流程设计
  • java单元测试代码
  • Python中的JSON库,详细介绍与代码示例
  • 《RESTful API版本控制的哲学思辨:稳定性与创新性的终极平衡》
  • Node.js 是什么?
  • 深入理解 TensorFlow 的模型保存与加载机制(SavedModel vs H5)
  • 蓝桥杯单片机国赛模板——基于柳离风模板
  • 列日-巴斯通-列日:与VELO Senso TT+见证精彩时刻
  • java类=null的回收
  • PostgreSQL 的 pg_ls_waldir 函数
  • Scala day6(Class,field,Single Object)
  • 江西浮梁县县长张汉坤被查,此前已有4个月无公开活动
  • 北美票房|“雷霆”开画票房比“美队4”低,但各方都能接受
  • 铁路五一假期运输旅客发送量累计超1亿人次,今日预计发送2110万人次
  • 苏杯登顶看到老将新人冲劲,国羽用冠军为奥运新周期开好头
  • 岳伟华任北京大学第六医院院长,陆林院士卸任
  • 中国海警局新闻发言人就日民用飞机侵闯我钓鱼岛领空发表谈话