突破编程_C++_网络编程(Windows 套接字(API 接口(1)))

1 初始化与清理

1.1 WSAStartup

WSAStartup 函数用于初始化 Winsock 库,并指定应用程序所需的 Winsock 版本。它允许应用程序与 Winsock DLL(动态链接库)建立联系,并准备 Winsock 环境以供后续使用。

(1)函数原型如下:

int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

(2)参数说明:

  • wVersionRequested:指定应用程序请求的 Winsock 版本。通常,这个值设置为 MAKEWORD(2, 2),表示请求 Winsock 2.2 版本。
  • lpWSAData:指向 WSADATA 结构体的指针,该结构体用于接收关于 Winsock 库的信息。

(3)返回值:

  • 如果函数成功,返回值为 0。
  • 如果函数失败,返回值为 SOCKET_ERROR,并且可以通过调用 WSAGetLastError 函数来获取具体的错误代码。

(4)示例代码:

#include <winsock2.h>  
#include <ws2tcpip.h>  
#include <stdio.h>  
  
int main() {  
    WSADATA wsaData;  
    int result = WSAStartup(MAKEWORD(2, 2), &wsaData);  
    if (result != 0) {  
        printf("WSAStartup failed with error: %d\n", result);  
        return 1;  
    }  
  
    // 在这里进行网络操作...  
  
    WSACleanup(); // 完成网络操作后,调用 WSACleanup 清理 Winsock 库  
    return 0;  
}

1.2 WSACleanup

WSACleanup 函数用于清理 Winsock 库,释放所有由 Winsock 分配的资源。在应用程序完成所有网络操作后,应该调用此函数来确保资源得到正确释放。

(1)函数原型如下:

int WSACleanup(void);

(2)返回值:

  • 如果函数成功,返回值为 0。
  • 如果函数失败,返回值为 SOCKET_ERROR,并且可以通过调用 WSAGetLastError 函数来获取具体的错误代码。

在 1.1 的示例代码中,WSACleanup 函数被调用在完成所有网络操作之后,以确保 Winsock 库得到正确的清理。这是一个良好的编程实践,可以防止资源泄漏和其他潜在问题。

2 socket() 函数:创建套接字

在 Windows 套接字(Winsock)API 中,socket() 函数是用于创建一个新的套接字描述符的接口。套接字是网络通信中的一个端点,用于发送和接收数据。通过调用 socket() 函数,应用程序可以请求一个套接字,并根据其需要的通信类型进行配置。

(1)函数原型

SOCKET socket(int af, int type, int protocol);

(2)参数说明

  • af(地址族):这个参数指定了应用程序使用的通信协议族。对于 IPv4,通常使用 AF_INET;对于 IPv6,使用 AF_INET6。还有其他一些地址族,但这两个是最常用的。

  • type(套接字类型):这个参数指定了套接字的类型,它决定了套接字的行为以及数据的发送和接收方式。常见的套接字类型有:

    • SOCK_STREAM:提供面向连接的、可靠的、基于字节流的服务,如 TCP。
    • SOCK_DGRAM:提供无连接的、不可靠的、基于数据报的服务,如 UDP。
    • SOCK_RAW:提供原始套接字,允许应用程序直接访问底层协议。
  • protocol(协议):这个参数通常设置为 0,表示让系统自动选择该地址族和套接字类型对应的默认协议。如果需要显式指定协议,这里可以填入协议号,但一般情况下不推荐这样做。

(3)返回值

  • 如果函数成功,socket() 返回一个非负整数,即新创建的套接字描述符。这个描述符在后续的网络操作中用于标识该套接字。

  • 如果函数失败,socket() 返回 SOCKET_ERROR,并且可以通过调用 WSAGetLastError() 来获取更详细的错误信息。

(4)示例代码

以下是一个简单的示例,展示如何使用 socket() 函数创建一个 TCP 套接字:

#include <winsock2.h>  
#include <stdio.h>  
  
int main() {  
    WSADATA wsaData;  
    int result = WSAStartup(MAKEWORD(2, 2), &wsaData);  
    if (result != 0) {  
        printf("WSAStartup failed with error: %d\n", result);  
        return 1;  
    }  
  
    // 创建 TCP 套接字  
    SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);  
    if (sock == SOCKET_ERROR) {  
        int err = WSAGetLastError();  
        printf("socket() failed with error: %d\n", err);  
        WSACleanup();  
        return 1;  
    }  
  
    // 在这里进行网络操作,例如绑定、监听、连接等...  
  
    // 关闭套接字  
    closesocket(sock);  
  
    // 清理 Winsock 库  
    WSACleanup();  
    return 0;  
}

在上面的示例中,我们首先调用 WSAStartup() 来初始化 Winsock 库。然后,我们使用 socket() 函数创建一个 IPv4(AF_INET)和 TCP(SOCK_STREAM)类型的套接字。如果 socket() 函数失败,我们通过调用 WSAGetLastError() 来获取错误信息,并清理 Winsock 库后退出程序。

3 套接字地址操作

3.1 inet_addr

inet_addr 函数是一个较老且相对简单的函数,用于将点分十进制的 IP 地址(例如 “192.168.1.1”)转换为一个 32 位的整数,该整数按照网络字节序(大端)排列。

(1)函数原型:

in_addr_t inet_addr(const char *cp);

(2)参数:

  • cp:指向以空字符结尾的字符串的指针,该字符串表示一个点分十进制的 IPv4 地址。

(3)返回值:

  • 如果转换成功,返回该 IP 地址对应的网络字节序整数。
  • 如果转换失败(例如,输入的不是有效的 IP 地址),返回 INADDR_NONE(通常是 -1)。

(4)示例:

struct sockaddr_in serverAddr;  
serverAddr.sin_addr.s_addr = inet_addr("192.168.1.1");  
if (serverAddr.sin_addr.s_addr == INADDR_NONE) {  
    // 处理错误  
}

需要注意的是,inet_addr 函数不支持 IPv6 地址,并且不支持错误处理(只能通过返回值判断)。对于更复杂的地址转换需求,推荐使用 inet_pton 函数。

3.2 htons/htonl

htons 和 htonl 是一组用于转换主机字节序到网络字节序的函数。它们分别用于转换 16 位(短整数)和 32 位(长整数)的数值。

(1)函数原型:

uint16_t htons(uint16_t hostshort);  
uint32_t htonl(uint32_t hostlong);

(2)参数:

  • hostshort:主机字节序的 16 位整数。
  • hostlong:主机字节序的 32 位整数。

(3)返回值:

  • 转换后的网络字节序的整数。

(4)示例:

struct sockaddr_in serverAddr;  
serverAddr.sin_port = htons(12345); // 将端口号从主机字节序转换为网络字节序

3.3 ntohs/ntohl

ntohs 和 ntohl 是 htons 和 htonl 的反向操作,用于将网络字节序的整数转换回主机字节序。

(1)函数原型:

uint16_t ntohs(uint16_t netshort);  
uint32_t ntohl(uint32_t netlong);

(2)参数:

  • netshort:网络字节序的 16 位整数。
  • netlong:网络字节序的 32 位整数。

(3)返回值:

  • 转换后的主机字节序的整数。

(4)示例:

uint16_t receivedPort = ntohs(someNetworkShort); // 将从网络接收到的端口号转换为主机字节序

3.4 bind

bind 函数用于将套接字绑定到本地地址和端口。在创建套接字后,通常需要调用 bind 函数来指定套接字应该使用的本地地址和端口。

(1)函数原型:

int bind(SOCKET s, const struct sockaddr *name, int namelen);

(2)参数:

  • s:要绑定的套接字描述符。
  • name:指向包含本地地址信息的 sockaddr 结构体的指针。
  • namelen:name 参数指向的地址结构体的长度。

(3)返回值:

  • 如果成功,返回 0。
  • 如果失败,返回 SOCKET_ERROR,可以通过 WSAGetLastError 获取错误代码。

(4)示例:

SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); // 创建一个 IPv4 TCP 套接字  
if (sock == INVALID_SOCKET) {  
    // 处理错误  
}  
  
struct sockaddr_in serverAddr;  
memset(&serverAddr, 0, sizeof(serverAddr)); // 初始化地址结构体  
serverAddr.sin_family = AF_INET; // 使用 IPv4 地址  
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定到任意本地地址  
serverAddr.sin_port = htons(12345); // 绑定到端口 12345  
  
if (bind(sock, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {  
    int error = WSAGetLastError();  
    // 处理错误  
}

3.5 getpeername

getpeername 函数用于获取已连接套接字的远程地址。这对于服务器程序来说尤其有用,因为它们需要知道客户端的地址信息。

(1)函数原型:

int getpeername(SOCKET s, struct sockaddr *name, int *namelen);

(2)参数:

  • s:已连接的套接字描述符。
  • name:指向用于接收远程地址信息的 sockaddr 结构体的指针。
  • namelen:指向 name 参数指向的地址结构体长度的指针。

(3)返回值:

  • 如果成功,返回 0。
  • 如果失败,返回 SOCKET_ERROR,可以通过 WSAGetLastError 获取错误代码。

(4)示例:

SOCKET sock = ...; // 假设 sock 是一个已连接的套接字  
struct sockaddr_in peerAddr;  
int addrlen = sizeof(peerAddr);  
  
if (getpeername(sock, (struct sockaddr*)&peerAddr, &addrlen) == SOCKET_ERROR) {  
    int error = WSAGetLastError();  
    // 处理错误  
} else {  
    char peerIP[INET_ADDRSTRLEN];  
    inet_ntop(AF_INET, &peerAddr.sin_addr, peerIP, sizeof(peerIP));  
    printf("Remote peer IP address: %s\n", peerIP);  
    printf("Remote peer port: %d\n", ntohs(peerAddr.sin_port));  
}

3.6 getsockname

getsockname 函数用于获取套接字的本地地址。这对于服务器和客户端程序都可能是有用的,因为它们可能需要知道它们自己的本地地址。

(1)函数原型:

int getsockname(SOCKET s, struct sockaddr *name, int *namelen);

(2)参数:

  • s:要绑定的套接字描述符。
  • name:指向包含本地地址信息的 sockaddr 结构体的指针。
  • namelen:name 参数指向的地址结构体的长度。

(3)返回值:

  • 如果成功,返回 0。
  • 如果失败,返回 SOCKET_ERROR,可以通过 WSAGetLastError 获取错误代码。

(4)示例:

SOCKET sock = ...; // 假设 sock 是一个已绑定或已连接的套接字  
struct sockaddr
sockaddr_in localAddr;
int addrlen = sizeof(localAddr);

if (getsockname(sock, (struct sockaddr*)&localAddr, &addrlen) == SOCKET_ERROR) {
int error = WSAGetLastError();
	// 处理错误
} else {
	char localIP[INET_ADDRSTRLEN];
	inet_ntop(AF_INET, &localAddr.sin_addr, localIP, sizeof(localIP));
	printf("Local IP address: %s\n", localIP);
	printf("Local port: %d\n", ntohs(localAddr.sin_port));
}

相关推荐

  1. C++编程

    2024-04-09 17:28:02       38 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-09 17:28:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-09 17:28:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-09 17:28:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-09 17:28:02       20 阅读

热门阅读

  1. 文本转语音常用的几个python库

    2024-04-09 17:28:02       13 阅读
  2. 【AIGC调研系列】在手机上运行的Octopusv2模型

    2024-04-09 17:28:02       14 阅读
  3. 2024年下半年软考考试科目有哪些?

    2024-04-09 17:28:02       16 阅读
  4. MySql01

    MySql01

    2024-04-09 17:28:02      12 阅读
  5. mapbox 工作问题暂时记录

    2024-04-09 17:28:02       12 阅读
  6. golang 协程池 动态扩缩容

    2024-04-09 17:28:02       13 阅读
  7. 谷粒商城学习日志

    2024-04-09 17:28:02       11 阅读
  8. 蓝桥杯刷题 深度优先搜索-[2410]最大连通(C++)

    2024-04-09 17:28:02       15 阅读
  9. ChopticsDriver调用说明

    2024-04-09 17:28:02       16 阅读
  10. [安卓逆向]常见调试和反调试及解决方案

    2024-04-09 17:28:02       12 阅读
  11. Redis 常见面试题

    2024-04-09 17:28:02       13 阅读
  12. Arrays类

    Arrays类

    2024-04-09 17:28:02      11 阅读
  13. 系统设计之订单系统中如何防止商品超卖

    2024-04-09 17:28:02       14 阅读
  14. Vue Router的介绍与引入

    2024-04-09 17:28:02       15 阅读