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

《Linux C编程实战》笔记:套接字编程

套接字地址结构

结构struct sockaddr定义了通用的套接字地址,它在sys/socket.h中的定义代码如下:

#include<sys/socket.h>
struct sockaddr {sa_family_t sa_family;   // 地址族,例如 AF_INET(IPv4)、AF_INET6(IPv6)char        sa_data[14]; // 地址数据(具体内容依地址族而定)
};

其中,sa_family表示套接字的协议族类型;sa_data存储具体的协议地址。sa_data被定义成14个字节,因为有的协议族使用较长的地址格式。

不过一般在编程中也并不对该结构体进行操作,而是使用与它等价的数据结构:sockaddr_in(用于 IPv4 地址)

#include<netinet/in.h>
struct sockaddr_in {sa_family_t    sin_family; // 地址族,必须是 AF_INETin_port_t      sin_port;   // 端口号(网络字节序)struct in_addr sin_addr;   // IPv4 地址char           sin_zero[8];// 填充字段,无实际意义
};

 sockaddr_in6(用于 IPv6 地址)

#include<netinet/in.h>
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
};

下面暂且只介绍sockaddr_in。

其中的in_addr结构体定义如下

struct in_addr {in_addr_t s_addr;  // 实际保存 IPv4 地址,类型是 uint32_t,使用网络字节序
};

那为什么还要sockaddr呢?因为许多 socket API(如 connect(), bind(), accept(), recvfrom(), sendto() 等)接受的是 struct sockaddr * 指针参数,这样可以统一处理不同类型的地址结构体。而sockaddr和sockaddr_in的长度同为16字节。所以通常使用sockaddr_in来设置地址,然后强行转换为sockaddr类型。

以下代码是一个转化的过程:

#include<iostream>
#include<sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>// inet_pton(), inet_ntop()
#include<cstring>
int main(){struct sockaddr_in addr;addr.sin_family=AF_INET;//type:IPv4addr.sin_port=htons(8080);//port:8080//设置ip地址inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);  //书中写的inet_addr方法已经被废弃了memset(addr.sin_zero,0,sizeof(addr.sin_zero));//强制转化,用C风格的强转也可以struct sockaddr *sock=reinterpret_cast<struct sockaddr *>(&addr);
}

以下是为什么要这么做?

虽然 struct sockaddrstruct sockaddr_in 是两个不同的结构体类型,但它们被设计成内存布局兼容,因此可以安全地通过指针强制转换。这是一种 "有约定的、不安全但合法的" 技巧,用于传递参数给统一接口。

虽然没有继承关系(C 语言没有继承),但 sockaddr_in 的前两个字段与 sockaddr 的布局保持一致。这是 POSIX 的明确规定,用于保证强制转换后,sendto()bind() 等系统调用能正确读取必要字段(如 sa_family, port, addr

为什么系统调用使用 sockaddr*

因为系统调用如 bind()connect()accept() 要支持多种地址族(IPv4、IPv6、Unix Domain 等),所以它们都统一声明为接受:struct sockaddr *addr

只要你将它们强制转换成 (struct sockaddr*) 传进去,系统就知道怎么根据 sa_family 去解释后续字段。

可以说,这是C语言实现的继承功能。

创建套接字

socket函数:

#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain, int type, int protocol);
参数说明
domain指定协议族(如 IPv4、IPv6)
type指定套接字类型(如 TCP、UDP)
protocol通常填 0,由系统根据 domain 和 type 自动选择

domain(协议族)
含义
AF_INETIPv4 地址族
AF_INET6IPv6 地址族
AF_UNIX / AF_LOCAL本地通信(Unix域套接字)
type(套接字类型)
含义
SOCK_STREAM流式套接字(TCP)
SOCK_DGRAM数据报套接字(UDP)
SOCK_RAW原始套接字(自定义协议)
protocol(协议)

通常填 0,由系统根据 domaintype 自动匹配:

  • AF_INET + SOCK_STREAM ⇒ TCP

  • AF_INET + SOCK_DGRAM ⇒ UDP

但也可以显式写:

  • IPPROTO_TCP

  • IPPROTO_UDP

下面是创建套接字的示例:

#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>      // inet_pton(), inet_ntop(), etc.
#include<unistd.h>int sockfd=socket(AF_INET,SOCK_STREAM,0);//创建tcp套接字if(sockfd==-1){perror("socket creation failed");return 1;}
close(sockfd);

 建立连接

connect函数

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

参数说明

参数含义
sockfd套接字文件描述符(由 socket() 返回)
addr指向服务器地址结构体的指针(如 sockaddr_in* 强转而来)
addrlen地址结构体的大小(一般是 sizeof(struct sockaddr_in)
  • 对于 TCP socket

    • 发起连接请求(开始三次握手)

    • 成功时,socket 进入“已连接”状态,可用于 send()/recv()

    • 只能调用一次

  • 对于 UDP socket

    • 不建立连接,但设置“默认目标地址”,之后可用 send() 而不用指定地址

    • 可以多次调用,改变默认目标地址

 如果连接失败,connect() 返回 -1,并设置 errno。常见错误包括:

errno 错误码原因说明
ECONNREFUSED对方主机无监听服务或被拒绝连接
ETIMEDOUT超时未连接成功
ENETUNREACH网络不可达,目标 IP 无法连接
EADDRNOTAVAILIP 地址无效或本地不可用

示意程序1

该程序是connect的用法;尝试连接本机端口为8080的服务器,采用tcp连接

#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>      // inet_pton(), inet_ntop(), etc.
#include<unistd.h>
#include<cstring>
int main(){int sockfd=socket(AF_INET,SOCK_STREAM,0);//ipv4+tcpif(sockfd<0){perror("socket error");return 1;}sockaddr_in serv_addr;//ipv4 addressserv_addr.sin_family=AF_INET;//ipv4serv_addr.sin_port=htons(8080);//port:8080inet_pton(AF_INET,"127.0.0.1",&serv_addr.sin_addr);//设置ipv4地址memset(serv_addr.sin_zero,0,sizeof(serv_addr.sin_zero));if(connect(sockfd,reinterpret_cast<sockaddr*>(&serv_addr),sizeof(serv_addr))<0){perror("connect");close(sockfd);return 1;}std::cout<<"Connected to server"<<std::endl;//可以发送或接受了close(sockfd);return 0;
}

 绑定套接字

在网络编程中,bind() 用于将 socket 绑定到一个本地地址(IP 和端口)。这一步通常是服务器端所需的,它告诉操作系统:这个 socket 用于监听哪个地址和端口。

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

可以将my_addr的sin_addr 设置为INADDR_ANY而不是某个确定的IP地址就可以绑定到任意网络接口。对于多宿主主机(拥有多块网卡),INADDR_ANY表示本服务器程序将处理来自所有网络接口上相应端口的连接请求。

函数执行成功返回0,如果失败则返回 -1,并设置 errno。

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cstring>
#include <unistd.h>int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {perror("socket");return 1;}sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(8080);addr.sin_addr.s_addr = INADDR_ANY;  // 监听所有本地 IPmemset(addr.sin_zero, 0, sizeof(addr.sin_zero));if (bind(sockfd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {perror("bind");close(sockfd);return 1;}std::cout << "Listening on all interfaces, port 8080..." << std::endl;close(sockfd);return 0;
}

在套接字上监听

listen() 函数用于将一个 处于绑定状态(bind) 的 socket 设置为 监听状态,准备接受来自客户端的连接请求。

这是 TCP 服务器三部曲的第二步:

socket()bind()listen()accept()

#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
参数含义
sockfd已创建并绑定了地址的 socket(SOCK_STREAM 类型)
backlog连接请求的排队长度上限(等待队列长度);达到上限后,之后的请求会被拒绝
  • 成功:返回 0

  • 失败:返回 -1,并设置 errno

    int server_fd = socket(AF_INET, SOCK_STREAM, 0);if (server_fd < 0) {perror("socket");return 1;}sockaddr_in addr{};addr.sin_family = AF_INET;addr.sin_port = htons(8080);addr.sin_addr.s_addr = INADDR_ANY;memset(addr.sin_zero, 0, sizeof(addr.sin_zero));if (bind(server_fd, (sockaddr*)&addr, sizeof(addr)) < 0) {perror("bind");return 1;}if (listen(server_fd, 10) < 0) {perror("listen");return 1;}

 注意事项

情况说明
只用于 SOCK_STREAM(TCP)UDP 是无连接的,不能使用 listen()
在调用 accept() 前必须 listen()否则会报错
一般在服务器中使用客户端不需要 listen()

接收连接

accept() 用于在服务器端接收一个客户端的连接请求。它会从由 listen() 建立的等待队列中,取出一个客户端连接,创建一个新的 socket 文件描述符用于通信

原始 socket 继续监听,返回的新 socket 用于与客户端通信

#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数说明
sockfd监听 socket(通过 socket() + bind() + listen() 创建)
addr输出参数,保存客户端的地址信息(可选,可以设为 nullptr
addrlen输入/输出参数,指明 addr 的大小,调用后返回实际大小

返回值

  • 成功:返回新的 socket 文件描述符(用于后续和客户端通信

  • 失败:返回 -1,并设置 errno

示例程序2

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cstring>
#include <unistd.h>int main() {int server_fd = socket(AF_INET, SOCK_STREAM, 0);sockaddr_in addr{};addr.sin_family = AF_INET;addr.sin_port = htons(8080);addr.sin_addr.s_addr = INADDR_ANY;bind(server_fd, (sockaddr*)&addr, sizeof(addr));listen(server_fd, 5);std::cout << "Waiting for connection..." << std::endl;sockaddr_in client_addr{};socklen_t client_len = sizeof(client_addr);int client_fd = accept(server_fd, (sockaddr*)&client_addr, &client_len);if (client_fd < 0) {perror("accept");return 1;}std::cout << "Client connected!" << std::endl;// 此时可以通过 client_fd 与客户端进行 read/write 读写操作close(client_fd);close(server_fd);return 0;
}

accept() 的行为特点

  • 阻塞调用:如果没有连接,它会一直等待。

  • 一旦有连接,就返回一个新的 socket fd,原 server fd 继续监听

  • 可以在 while (true) 循环中不断接收多个客户端连接。

套接字文件描述符对比

socket fd用途
server_fd原始监听 socket,用于接收连接(accept()
client_fd新生成的 socket,用于与特定客户端通信

总结:四步法建立服务器连接

  1. socket() 创建 socket

  2. bind() 绑定 IP+端口

  3. listen() 设置监听状态

  4. accept() 接受客户端连接并获得新 socket

TCP发送接收

send()recv() 是最基本的 数据发送与接收接口,它们直接作用于 accept() 得到的 socket 文件描述符。下面是它们的详细介绍:

send() 函数 —— 发送数据

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数含义
sockfd与客户端连接的 socket(如 accept() 返回值)
buf指向要发送数据的缓冲区
len要发送的字节数
flags发送行为控制,一般为 0(详见下方)

返回值

  • 成功:返回实际发送的字节数

  • 失败:返回 -1,并设置 errno

如果要发送的数据太长而不能发送时,将出现错误,errno设置为EMSGSIZE;如果要发送的
数据长度大于该套接字的缓冲区剩余空间大小时,send一般会被阻塞,如果该套接字被设置为非
阻塞方式,则此时立即返回-1并将errno设为EAGAIN。

常用 flags 值

flags 值说明
0默认
MSG_NOSIGNAL防止 send() 在对方关闭时发出 SIGPIPE 信号
MSG_DONTWAIT非阻塞发送/接收(需要 socket 设置为非阻塞)

 注意:执行成功只是说明数据写入套接字的缓冲区内,不表示数据已经通过网络成功发送。

    const char send_buf[]{"hello"};int len=strlen(send_buf)+1;if(send(client_fd,send_buf,len,0)<0){perror("send");return 1;}

recv() 函数 —— 接收数据

#include <sys/types.h>
#include <sys/socket.h>ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数含义
sockfd与客户端连接的 socket(如 accept() 返回值)
buf接收数据的缓冲区
len最多接收的字节数
flags接收行为控制,一般为 0

 返回值

  • !=0:实际接收到的字节数

  • =0:表示 对端已关闭连接

  • -1:出错,设置 errno

flags说明
0默认阻塞接收,直到收到数据或对方关闭连接
MSG_PEEK窥探数据,查看缓冲区数据但不取走,下次 recv() 还会读到这部分
MSG_WAITALL等够指定长度才返回,否则继续阻塞(仅适合已知固定长度的消息)
MSG_DONTWAIT非阻塞接收,若没有数据立即返回 -1,errno=EAGAINEWOULDBLOCK
MSG_NOSIGNAL防止因对端关闭 socket 时 recv() 引发 SIGPIPE 信号(更常用于 send()

示例程序3:tcp建立连接以及发送数据程序

逻辑是连接后客户端发送数据,服务器接收 

服务器代码

#include<iostream>
#include<cstring>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<unistd.h>
int main(){int server_fd=socket(AF_INET,SOCK_STREAM,0);if(server_fd<0){perror("socket");close(server_fd);exit(1);}sockaddr_in addr;addr.sin_family=AF_INET;addr.sin_port=htons(8080);//监听8080端口addr.sin_addr.s_addr=INADDR_ANY;memset(addr.sin_zero,0,sizeof(addr.sin_zero));if(bind(server_fd,reinterpret_cast<sockaddr*>(&addr),sizeof(addr))<0){perror("bind");close(server_fd);exit(1);}if(listen(server_fd,10)<0){perror("listen");close(server_fd);exit(1);}//准备接收数据sockaddr_in client_addr;socklen_t client_len=sizeof(client_addr);int client_fd=accept(server_fd,reinterpret_cast<sockaddr*>(&client_addr),&client_len);if(client_fd<0){perror("accept");close(server_fd);exit(1);}char recv_buf[100];int len=recv(client_fd,recv_buf,100,0);//默认会发送\0if(len<=0){std::cout<<"receive error"<<std::endl;close(client_fd); close(server_fd);exit(1);}std::cout<<"Server receive data: "<<recv_buf<<std::endl;close(client_fd); close(server_fd);
}

客户端代码:

#include<iostream>
#include<cstring>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include <arpa/inet.h> 
#include<unistd.h>
//封装了出错的逻辑
void exit_with_error(const char *msg,std::initializer_list<int> fds = {}){perror(msg);for (int fd : fds) {if (fd >= 0) close(fd);}  exit(EXIT_FAILURE);
}
int main(){int sockfd=socket(AF_INET,SOCK_STREAM,0);//ipv4+tcpif(sockfd<0){exit_with_error("socket",{sockfd});}sockaddr_in serv_addr;//ipv4 addressserv_addr.sin_family=AF_INET;//ipv4serv_addr.sin_port=htons(8080);//port:8080inet_pton(AF_INET,"127.0.0.1",&serv_addr.sin_addr);//设置ipv4地址memset(serv_addr.sin_zero,0,sizeof(serv_addr.sin_zero));if(connect(sockfd,reinterpret_cast<sockaddr*>(&serv_addr),sizeof(serv_addr))<0){exit_with_error("connect",{sockfd});}const char send_buf[]{"hello"};int send_len=strlen(send_buf)+1;if(send(sockfd,send_buf,send_len,0)<0){exit_with_error("send",{sockfd});}close(sockfd);
}

UDP发送接收

sendto() 函数 —— 向某个地址发送 UDP 数据报

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
参数含义
sockfdsocket(AF_INET, SOCK_DGRAM, 0) 创建的 UDP socket
buf要发送的数据
len数据长度(单位:字节)
flags与send一致
dest_addr对方地址(sockaddr_in 强转为 sockaddr*
addrlen对方地址结构体的大小(用 sizeof(sockaddr_in)

 使用示例

int sockfd = socket(AF_INET, SOCK_DGRAM, 0);sockaddr_in dest{};
dest.sin_family = AF_INET;
dest.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &dest.sin_addr);const char* msg = "Hello UDP";
sendto(sockfd, msg, strlen(msg), 0,reinterpret_cast<sockaddr*>(&dest), sizeof(dest));

recvfrom() 函数 —— 从某个来源接收 UDP 数据报

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
参数含义
sockfd已绑定的 UDP socket(一般使用 bind()
buf用于存储接收的数据
len缓冲区长度
flags与recv一致
src_addr可选,保存发送方地址(可为 NULL)
addrlensrc_addr 对应的结构体大小变量(可为 NULL)

 使用示例 

char buffer[1024];
sockaddr_in sender{};
socklen_t sender_len = sizeof(sender);int n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0,reinterpret_cast<sockaddr*>(&sender), &sender_len);
if (n > 0) {buffer[n] = '\0'; // 添加字符串结束符std::cout << "Received: " << buffer << std::endl;
}

 示例程序4:udp发送接收数据程序

客户端发送,服务器接收

服务器代码:

int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;
bind(sockfd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));while (true) {char buf[100];sockaddr_in client;socklen_t client_len = sizeof(client);int len = recvfrom(sockfd, buf, sizeof(buf)-1, 0,reinterpret_cast<sockaddr*>(&client), &client_len);if (len > 0) {buf[len] = '\0';std::cout << "Client says: " << buf << std::endl;}
}

客户端:

int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
sockaddr_in server{};
server.sin_family = AF_INET;
server.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &server.sin_addr);std::string msg = "hello UDP server!";
sendto(sockfd, msg.c_str(), msg.size(), 0,reinterpret_cast<sockaddr*>(&server), sizeof(server));

UDP 客户端的 socket 通常不需要 connect()。这是因为:

UDP 是**无连接(connectionless)**协议

  • 每次通信都是独立的数据报

  • 不像 TCP 那样需要三次握手,也没有连接状态;

  • 所以你用 sendto()/recvfrom() 就能完成通信,无需 connect()

但是!UDP 其实也可以用 connect()(不常见,但有用)

虽然 UDP 天生是无连接的,但你仍然可以在 UDP socket 上调用 connect(),它会:

功能效果
设定目标地址后续的 send()/recv() 不再需要传目标地址
内核过滤非目标地址的包只接收来自你 connect() 指定地址的数据(recv()时自动屏蔽其他地址)
更简洁的编程可以用 send() / recv() 替代 sendto() / recvfrom()
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);sockaddr_in server{};
server.sin_family = AF_INET;
server.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &server.sin_addr);// 虽然是 UDP,也可以 connect()
connect(sockfd, reinterpret_cast<sockaddr*>(&server), sizeof(server));// 然后就能用 send / recv,而不用每次都写目标地址
send(sockfd, "Hello", 5, 0);
recv(sockfd, buffer, sizeof(buffer), 0);

关闭套接字

close

int close(int sockfd);

含义:

  • 关闭整个 socket,释放其占用的所有资源(文件描述符、缓冲区等);

  • TCP 情况下,它会向对端发送 FIN 包,优雅断开连接

  • 若是 UDP,直接释放资源,没有通知对端;

  • 强制关闭,无论当前读写状态如何。

返回值:

  • 成功返回 0;

  • 失败返回 -1(例如关闭两次),并设置 errno

适用于:

  • 所有 socket,TCP 和 UDP 都可;

  • 简单直接,用完就 close(),非常常用。

shutdown

int shutdown(int sockfd, int how);
how含义
SHUT_RD关闭方向(recv 将立即返回 0)
SHUT_WR关闭方向(send 将报错)
SHUT_RDWR同时关闭读和写,相当于完全断开

特点:

  • 常用于 TCP,适合逐步“关闭连接”;

  • 允许只断一半,如只发送 FIN,不马上收 FIN

  • 有助于实现半关闭连接的协议逻辑,例如:

    • 我发完数据,但还要接收对方数据(只关写);

    • 我不想再接收任何数据(只关读);

  • 最终仍然要用 close() 来释放 socket

系统调用函数

主机字节序(Host Byte Order)和网络字节序(Network Byte Order)之间进行转换

为什么需要这些函数?

不同计算机架构的**字节序(Endian)**可能不同:

  • 小端序(如 x86):低位字节在前;

  • 大端序(网络字节序):高位字节在前。

网络协议(如 TCP/IP)统一使用大端序(Big Endian)。因此,在主机和网络通信之间要进行字节序转换。

四个函数介绍(来自 <arpa/inet.h>

函数名全称功能说明
htons()Host to Network Short主机字节序 → 网络字节序(16位)
htonl()Host to Network Long主机字节序 → 网络字节序(32位)
ntohs()Network to Host Short网络字节序 → 主机字节序(16位)
ntohl()Network to Host Long网络字节序 → 主机字节序(32位)
函数名用于哪个方向处理的数据类型常用场景
htons主机 → 网络uint16_t设置端口号
htonl主机 → 网络uint32_t设置 IPv4 地址(整型)
ntohs网络 → 主机uint16_t接收端口号
ntohl网络 → 主机uint32_t接收 IPv4 地址(整型)

注意:如果你是用字符串 inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr),就不需要自己调用 htonl(),因为 inet_pton 已经内部处理过了。 

转换示例:

#include <arpa/inet.h>
#include <iostream>int main() {uint16_t port = 8080;uint16_t net_port = htons(port);std::cout << "网络字节序端口: " << net_port << std::endl;uint16_t host_port = ntohs(net_port);std::cout << "主机字节序端口: " << host_port << std::endl;return 0;
}

使用示例:

sockaddr_in addr;
addr.sin_port = htons(8080);  // 正确:16位端口号 -> 网络序
addr.sin_addr.s_addr = htonl(0x7F000001); // 相当于 127.0.0.1

inet系列

inet 系列函数是用于 IP地址和文本字符串之间转换 以及 基本验证 的一组函数,常用于处理 IPv4 地址。它们来自 <arpa/inet.h> 头文件,常见的有:

函数名功能简介支持协议常用吗备注
inet_addr()IP字符串 → 整数(网络序)IPv4❌已废弃不推荐使用
inet_ntoa()整数(网络序)→ IP字符串(静态返回)IPv4❌不推荐线程不安全
inet_aton()IP字符串 → in_addr 结构(更安全)IPv4✅推荐用于转换
inet_ntop()网络序地址(IPv4/IPv6)→ 字符串IPv4/IPv6✅推荐新接口
inet_pton()字符串 → 网络序地址(IPv4/IPv6)IPv4/IPv6✅推荐新接口
int inet_aton(const char *cp, struct in_addr *inp);
  • cp:IP地址字符串(如 "127.0.0.1"

  • inp:输出参数,保存转换结果

int inet_pton(int af, const char *src, void *dst);
  • af:地址族(AF_INET = IPv4,AF_INET6 = IPv6)

  • src:IP地址字符串

  • dst:输出缓冲区,IPv4用 struct in_addr*,IPv6用 struct in6_addr*

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
  • af:地址族(AF_INETAF_INET6

  • src:输入网络字节序地址(struct in_addr*struct in6_addr*

  • dst:目标字符串缓冲区

  • sizedst 缓冲区大小(IPv4推荐用 INET_ADDRSTRLEN,IPv6用 INET6_ADDRSTRLEN

套接字属性

int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
参数说明
sockfd套接字描述符
level选项所在的协议层,常用值有: - SOL_SOCKET:套接字层(大多数选项) - IPPROTO_TCP:TCP 层的选项
optname要设置/获取的选项名(如 SO_REUSEADDR, SO_RCVBUF 等)
optval设置:传入的值地址;获取:传出的值地址
optlen设置:值的字节大小;获取:传入地址大小,返回实际大小

 常见选项

选项名所属层说明
SO_REUSEADDRSOL_SOCKET是否允许重用本地地址
SO_RCVBUFSOL_SOCKET接收缓冲区大小
SO_SNDBUFSOL_SOCKET发送缓冲区大小
SO_KEEPALIVESOL_SOCKET是否启用 TCP 保活机制
SO_LINGERSOL_SOCKET控制 close() 行为,是否等待数据发送完
TCP_NODELAYIPPROTO_TCP是否关闭 Nagle 算法(即是否立即发送)
http://www.xdnf.cn/news/10039.html

相关文章:

  • day41 python图像识别任务
  • 【多线程初阶】线程状态 线程安全
  • 进阶智能体实战九、图文需求分析助手(ChatGpt多模态版)(帮你生成 模块划分+页面+表设计、状态机、工作流、ER模型)
  • Ubuntu 安装 FSL 及多模态脑MRI的去颅骨处理(含 HD-BET 深度学习方法)
  • 区域未停留检测算法AI智能分析网关V4打造铁道/工厂/机场等场景应用方案
  • mysql隐式转换会造成索引失效的原因
  • 软件评测机构如何保障质量?检测资质、技术实力缺一不可
  • 历年浙江大学计算机保研上机真题
  • JavaScript 性能优化实战研讨
  • antDesignVue中a-upload上传组件的使用
  • Ubuntu开机自动运行Docker容器中的Qt UI程序
  • redis持久化策略
  • ansible自动化playbook简单实践
  • 从监控到告警:Prometheus+Grafana+Alertmanager+告警通知服务全链路落地实践
  • 湖北理元理律师事务所:债务优化中的生活保障实践
  • Java—— 多线程 第二期
  • 新松机械臂 2001端口服务的客户端例程
  • UI自动化测试中的元素等待机制解析
  • 山海鲸轻 3D 渲染技术深度解析:预渲染如何突破多终端性能瓶颈
  • 【Netty系列】核心概念
  • 【Unity博客节选】Playable系统 UML类图与结构分析
  • window10下docker方式安装dify步骤
  • 动态IP与区块链:重构网络信任的底层革命
  • RK3399 Android7.1增加应用安装白名单机制
  • Android 开发 Kotlin 全局大喇叭与广播机制
  • 2025 年 Solana 生态全景分析:它如何从以太坊「高速替代方案」成长为成熟的基础设施?
  • [CSS3]响应式布局
  • 多卡训练核心技术详解
  • TreeMap、TreeSet和HashMap、HashSet
  • PCB设计实践(三十一)PCB设计中机械孔的合理设计与应用指南