首页 > 其他分享 >套接字、网络字节序、IP地址转换、地址结构及关键函数

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

时间:2024-12-23 15:56:39浏览次数:6  
标签:sockaddr addr struct server IP地址 接字 sin 字节

目录
  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_INET
    char sa_data[14];      // 地址数据
};

IPv4结构体 (sockaddr_in)

struct sockaddr_in {
    short int sin_family;      // 地址族,AF_INET
    unsigned 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_INET6
    in_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 1024

int 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:接受一个传入的连接。

标签:sockaddr,addr,struct,server,IP地址,接字,sin,字节
From: https://blog.csdn.net/SXXYNHHXX/article/details/144662730

相关文章

  • xargs-awk-sed-管道拼接字符串
    ls|xargs-l|awk-F'.''{printf("%s%s\n",$1,$0)}'|sed's/^/fastbootflash/g'root@huang:/home/huang/1#lsabl.imgcmnlib.imgfw_4u1ea.imgoem_stanvbk.imgvbmeta.imgaop.imgdevcfg.img......
  • EVM介绍及字节码简单逆向
    什么是EVM以太坊是一个分布式的状态机,其中的状态不仅包含所有的账户和余额,还有EVM和EVM状态(可以被预先定义的规则所改变的东西);EVM是以太坊中的虚拟机,可以允许不被信任的代码执行;它是一个基于栈的虚拟机,有一个短暂的内存和一个永久存储的状态;PC:类似计算机中的PC寄存器,记录当......
  • 虚拟机下centos7系统实现修改ip地址为固定ip
    修改虚拟机配置目的如果不修改虚拟机配置,会出现设置了静态IP和相关参数之后,本地局域网可以互相访问,但是CentOS系统访问不了互联网。步骤一:以管理员身份打开虚拟机步骤二:打开虚拟网络编辑器左上角菜单栏找到“编辑”选项并单击,在下拉子菜单中单击选项“虚拟网络编辑器”。步......
  • 什么是电脑ip地址?电脑ip地址怎样查询
    在数字化时代,电脑已成为我们日常生活和工作中不可或缺的工具。而电脑IP地址,作为设备在网络中的唯一标识,更是扮演着举足轻重的角色。它相当于电脑的“网络身份证”,使得设备能够在浩瀚的网络世界中被准确识别。本文将详细介绍什么是电脑IP地址,以及怎样查找电脑的IP地址,帮助大家更......
  • 怎么更改设备的ip地址?简单易懂的操作
    在数字时代,IP地址作为设备在网络中的唯一标识,扮演着举足轻重的角色。无论是出于网络安全、隐私保护,还是解决网络冲突、访问特定资源的需求,有时我们都需要更改设备的IP地址。然而,对于许多非专业用户来说,更改IP地址可能是一个相对陌生且复杂的操作。本文将为大家详细介绍如何更改......
  • 适配器模式应用~获取IP地址时想起了适配器
    获取IP地址信息时,一般我们需要一个HttpServletRequest对象,然后从请求头里获取x-forwarded-for的值,而当我们使用dubbo+netty开发rest接口时,如果希望获取IP地址,HttpServletRequest是用不了的,你需要使用netty中的NettyRequestFacade对象,这时,你之前的获取IP地址的方法需要扩展,加一个只......
  • 【Azure Batch Account】批处理服务是否可以固定出口访问IP地址呢?
    问题描述使用AzureBatchAccount服务(批处理),所访问的资源受防火墙保护。现在需要把BatchAccount服务池中的实例地址IP加入到防火墙白名单中,但是由于BatchAccount被没有指定的出口访问IP地址,所以需要把BatchAccount服务的全部IP地址加入到白名单中,但是,它的范围的确太多了!如......
  • 【YashanDB知识库】如何处理yasql输入交互模式下单行字符总量超过限制4000字节
    现象在yasql执行sql语句后报错:YASQL-00021inputlineoverflow(>4000byteatline4)原因yasql在交互模式模式下单行字符总量限制4000字节,超出该限制即报错。交互式模式下,yasql会显示一个提示符,通常是SQL>,等待用户输入命令,用户执行的每个命令都会立即执行,并显示结果。......
  • 智慧园区算法视频分析服务器网络摄像机供电正常,用IP搜索工具或中心管理软件搜索不到摄
    在使用网络摄像机进行监控时,确保摄像机能够被正确识别和连接至网络至关重要。然而,有时即使摄像机供电正常,使用IP搜索工具或中心管理软件仍然无法找到其IP地址。这种情况可能由多种因素引起,包括网络连接问题、IP设置不当或设备故障等。为了帮助用户快速定位和解决这些问题,以下是一......
  • ip地址解析-纯真(CZ88.net) 中国IP地理位置数据库首创者
    纯真(CZ88.NET)自2005年起一直为广大社区用户提供社区版IP地址库,只要获得纯真的授权就能免费使用,并不断获取后续更新的版本。如果有需要免费版IP库的朋友可以前往纯真的官网进行申请。纯真除了免费的社区版IP库外,还提供数据更加准确、服务更加周全的商业版IP地址查询数据。纯真......