欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > 套接字、网络字节序、IP地址转换、地址结构及关键函数

套接字、网络字节序、IP地址转换、地址结构及关键函数

2025/2/25 23:18:06 来源:https://blog.csdn.net/SXXYNHHXX/article/details/144662730  浏览:    关键词:套接字、网络字节序、IP地址转换、地址结构及关键函数
目录
  1. 引言
  2. 套接字(Sockets)概述
  3. 网络字节序
  4. IP地址转换函数
  5. sockaddr 地址结构
  6. 套接字模型创建
  7. socketbind
  8. listenaccept
  9. 示例代码
  10. 总结

引言

在网络编程中,套接字(Sockets) 是实现不同主机之间通信的基础。通过套接字,程序可以在网络上发送和接收数据,实现客户端与服务器的交互。本文将全面介绍套接字编程中的关键概念,包括网络字节序、IP地址转换函数、sockaddr 地址结构,以及创建和管理套接字的关键函数如 socketbindlistenaccept


套接字(Sockets)概述

套接字(Socket) 是网络通信的端点,提供了一组用于发送和接收数据的接口。套接字抽象了网络协议的细节,允许开发者专注于数据交换逻辑。套接字编程通常基于BSD套接字接口,这是一个广泛支持的标准。

套接字类型

  • 流式套接字(Stream Sockets):基于TCP协议,提供面向连接的、可靠的数据传输。
  • 数据报套接字(Datagram Sockets):基于UDP协议,提供无连接的、不保证可靠的数据传输。
  • 原始套接字(Raw Sockets):允许直接访问底层协议,通常用于网络监控和测试。

网络字节序

字节序(Byte Order) 指的是多字节数据在内存中的存储顺序。网络字节序是指在网络上传输数据时使用的字节顺序,标准为大端字节序(Big-Endian)。为了确保不同架构的机器之间能够正确解释数据,需要在发送前将主机字节序转换为网络字节序,接收后再转换回来。

常用转换函数

  • htons(unsigned short hostshort):主机字节序到网络字节序(short类型)。
  • htonl(unsigned long hostlong):主机字节序到网络字节序(long类型)。
  • ntohs(unsigned short netshort):网络字节序到主机字节序(short类型)。
  • ntohl(unsigned long netlong):网络字节序到主机字节序(long类型)。

这些函数确保数据在不同系统之间的一致性和正确性。


IP地址转换函数

在套接字编程中,IP地址通常以文本形式表示(如 "192.168.1.1"),但在网络传输和套接字函数中,需要将其转换为二进制形式。

主要函数

  • inet_aton:将IPv4地址从字符串转换为二进制形式。

    #include <arpa/inet.h>struct in_addr addr;
    if (inet_aton("192.168.1.1", &addr) == 0) {// 转换失败
    }
    
  • inet_ntoa:将二进制形式的IPv4地址转换为字符串形式。

    #include <arpa/inet.h>struct in_addr addr;
    addr.s_addr = htonl(0xC0A80101); // 192.168.1.1
    char *ip_str = inet_ntoa(addr);
    printf("IP地址: %s\n", ip_str);
    
  • inet_pton:支持IPv4和IPv6地址的转换函数,功能更强大。

    #include <arpa/inet.h>struct sockaddr_in sa;
    if (inet_pton(AF_INET, "192.168.1.1", &(sa.sin_addr)) != 1) {// 转换失败
    }
    
  • inet_ntop:与 inet_pton 对应,用于将二进制地址转换为文本形式。

    #include <arpa/inet.h>struct sockaddr_in sa;
    char ip_str[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &(sa.sin_addr), ip_str, INET_ADDRSTRLEN);
    printf("IP地址: %s\n", ip_str);
    

这些函数在处理网络地址转换时至关重要,确保地址在不同表示形式之间的正确转换。


sockaddr 地址结构

在套接字编程中,地址信息通常通过 sockaddr 结构体传递。由于 sockaddr 本身较为通用,针对不同协议族(如 IPv4 和 IPv6)有更具体的结构体,如 sockaddr_insockaddr_in6

通用结构体

struct sockaddr {sa_family_t sa_family; // 地址族,如 AF_INETchar sa_data[14];      // 地址数据
};

IPv4结构体 (sockaddr_in)

struct sockaddr_in {short int sin_family;      // 地址族,AF_INETunsigned short sin_port;   // 端口号(网络字节序)struct in_addr sin_addr;   // IP地址unsigned char sin_zero[8]; // 填充,保持与 sockaddr 的大小一致
};

IPv6结构体 (sockaddr_in6)

struct sockaddr_in6 {sa_family_t     sin6_family;   // 地址族,AF_INET6in_port_t       sin6_port;     // 端口号(网络字节序)uint32_t        sin6_flowinfo; // 流信息struct in6_addr sin6_addr;     // IPv6地址uint32_t        sin6_scope_id; // 作用域ID
};

转换函数

由于套接字函数通常接受 struct sockaddr *,在调用时需要进行类型转换,例如将 sockaddr_in 转换为 sockaddr

struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
inet_pton(AF_INET, "192.168.1.1", &(server_addr.sin_addr));
memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero));struct sockaddr *addr = (struct sockaddr *)&server_addr;

正确设置 sockaddr 结构体对于成功建立网络连接至关重要。


套接字模型创建

创建一个套接字需要使用 socket 函数,该函数定义了通信协议和传输方式。创建套接字后,可以使用 bind 将其与特定地址和端口关联。

socket 函数

#include <sys/types.h>
#include <sys/socket.h>// 原型
int socket(int domain, int type, int protocol);

参数说明

  • domain:地址族,如 AF_INET(IPv4)、AF_INET6(IPv6)或 AF_UNIX(本地通信)。
  • type:套接字类型,如 SOCK_STREAM(流式)、SOCK_DGRAM(数据报)。
  • protocol:协议类型,通常设置为 0,由系统自动选择适合的协议。

示例

int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {perror("socket failed");exit(EXIT_FAILURE);
}

成功创建套接字返回一个文件描述符,用于后续的通信操作。


socketbind

bind 函数用于将套接字与特定的IP地址和端口号相关联,使其成为一个可监听的服务器端套接字。

bind 函数原型

#include <sys/types.h>
#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数说明

  • sockfd:由 socket 函数返回的套接字文件描述符。
  • addr:指向包含要绑定的地址信息的结构体(如 sockaddr_in)。
  • addrlen:地址结构体的大小。

示例

struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
inet_pton(AF_INET, "0.0.0.0", &(server_addr.sin_addr));
memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero));if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("bind failed");close(sockfd);exit(EXIT_FAILURE);
}

注意事项

  • 通常,服务器端会绑定到特定的端口上,以便客户端能够连接。
  • IP地址 "0.0.0.0" 表示监听所有可用的网络接口。
  • 如果端口号已经被占用,bind 将失败,需要选择其他端口。

listenaccept

在服务器端,listenaccept 函数用于监听和接受客户端的连接请求。

listen 函数

#include <sys/types.h>
#include <sys/socket.h>int listen(int sockfd, int backlog);

参数说明

  • sockfd:已经绑定的套接字文件描述符。
  • backlog:挂起连接队列的最大长度。

示例

if (listen(sockfd, 10) == -1) {perror("listen failed");close(sockfd);exit(EXIT_FAILURE);
}

accept 函数

#include <sys/types.h>
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数说明

  • sockfd:正在监听的套接字文件描述符。
  • addr:指向 sockaddr 结构体的指针,用于存储客户端的地址信息。
  • addrlen:指向 socklen_t 变量的指针,表示地址结构体的大小。

示例

struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int new_sock = accept(sockfd, (struct sockaddr *)&client_addr, &client_len);
if (new_sock == -1) {perror("accept failed");close(sockfd);exit(EXIT_FAILURE);
}// 获取客户端IP地址
char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN);
printf("Accepted connection from %s:%d\n", client_ip, ntohs(client_addr.sin_port));

注意事项

  • accept 会阻塞,直到有客户端连接请求到来。
  • 每次调用 accept 成功后,会返回一个新的套接字文件描述符,用于与客户端的通信,原来的套接字继续监听。
  • 需要处理多个客户端时,可以采用多线程、进程或非阻塞I/O等方式。

示例代码

以下是一个完整的简单TCP服务器示例,展示了上述概念的应用:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define PORT 8080
#define BACKLOG 10
#define BUFFER_SIZE 1024int main() {int server_fd, new_socket;struct sockaddr_in server_addr, client_addr;socklen_t client_len = sizeof(client_addr);char buffer[BUFFER_SIZE] = {0};char *hello = "Hello from server";// 创建套接字if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {perror("socket failed");exit(EXIT_FAILURE);}// 配置服务器地址结构server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = INADDR_ANY; // 绑定所有接口server_addr.sin_port = htons(PORT);memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero));// 绑定套接字if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("bind failed");close(server_fd);exit(EXIT_FAILURE);}// 监听连接if (listen(server_fd, BACKLOG) == -1) {perror("listen failed");close(server_fd);exit(EXIT_FAILURE);}printf("Server listening on port %d\n", PORT);// 接受连接if ((new_socket = accept(server_fd, (struct sockaddr *)&client_addr, &client_len)) == -1) {perror("accept failed");close(server_fd);exit(EXIT_FAILURE);}// 打印客户端信息char client_ip[INET_ADDRSTRLEN];inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN);printf("Accepted connection from %s:%d\n", client_ip, ntohs(client_addr.sin_port));// 读取客户端数据int bytes_read = read(new_socket, buffer, BUFFER_SIZE);if (bytes_read > 0) {printf("Received: %s\n", buffer);}// 发送响应send(new_socket, hello, strlen(hello), 0);printf("Hello message sent\n");// 关闭套接字close(new_socket);close(server_fd);return 0;
}

客户端示例(使用 telnet):

telnet 127.0.0.1 8080

连接后,输入任意文本,服务器将接收并回复 "Hello from server"


总结

本文全面介绍了套接字编程中的关键概念,包括:

  • 套接字(Sockets):网络通信的基本端点,支持多种协议和传输方式。
  • 网络字节序:确保不同系统间数据传输的一致性。
  • IP地址转换函数:在文本和二进制地址表示之间进行转换的重要工具。
  • sockaddr 地址结构:用于存储和传递地址信息的通用结构体。
  • 关键函数
    • socket:创建一个新的套接字。
    • bind:将套接字与特定地址和端口关联。
    • listen:开始监听传入的连接请求。
    • accept:接受一个传入的连接。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词