网络编程之bind函数

基本TCP客户端/服务器程序的套接字函数

1、bind函数把一个本地协议地址赋予一个套接字。对于网际协议,协议地址是32位的IPv4地址或是128位的IPv6地址与16位的TCP或UDP端口号的组合。

#include<sys/socket.h>

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

第二个参数是一个指向特定协议的地址结构的指针,第三个参数是该地址结构的长度。对于TCP,调用bind函数可以指定一个端口号,或指定一个IP地址,也可以两者都指定,还可以都不指定。

  • 服务器在启动时捆绑它众所周知的端口。如果一个TCP客户或服务器未曾调用bind绑定一个端口,当调用connect或listen时,内核就要为相应套接字选一个临时端口。让内核来选择临时端口对于TCP客户来说是正常的,除非应用需要一个预留端口。然而对于TCP服务器来说却极为罕见,因为服务器是通过它们的众所周知端口被大家认识的。
  • 进程可以把一个特定的IP地址捆绑到它的套接字上,不过这个IP地址必须属于其所在的网络接口之一。对于TCP客户,这就为在该套接字上发送的IP数据报指定了源IP地址;对于服务器,则限定了该套接字只能接收那些目的地为这个IP地址的客户连接。TCP客户端通常不把IP地址捆绑到它的套接字上。当连接套接字时,内核将根据所用的外出网络接口来选择源IP地址,而所用外出接口则取决于到达服务器所需的路径。如果TCP服务器没有吧IP地址绑定到它的套接字上,内核就把客户发送的SYN的目的IP地址作为服务器的源IP地址。

下图4-6汇总了如何根据预期的结果,设置sin_addr和sin_port或者sin6_addr和sin6_port的值

如果指定端口号为0,那么内核就在bind被调用时选择一个临时端口。然而如果指定IP地址为通配地址,那么内核将在套接字已连接或已在套接字上发出数据报时才选择一个本地IP地址。

对于IPv4来说,通配地址通常由INADDR_ANY来指定,其值一般为0。它告知内核去选择IP地址。

struct sockaddr_in servaddr;

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

如此赋值对IPv4是可行的,因为其IP地址是一个32位的值,可以用一个简单的数字常值表示,对于IPv6,我们就不能这么做了,因为128位的IPv6地址是存放在一个结构中的。

struct sockaddr_in6 serv;

serv.sin6_addr = in6addr_any;

系统预先分配in6addr_any 变量并将其初始化为常值IN6ADDR_ANY_INIT。头文件<netinet/in.h>中含有in6addr_any的extern声明。

无论是网络字节序还是主机字节序,INADDR_ANY的值(为0)都一样,因此使用htonl并非必需。不过既然头文件<netinet/in.h>中定义的所有INADDR_常值都是按照主机字节序定义的,我们应该对任何这些常值都使用htonl。

如果让内核来为套接字选择一个临时端口号,那么必须注意,函数bind并不返回所选择的值。实际上,由于bind函数的第二个参数有const限定词,它无法返回所选之值。为了得到内核所选择的这个临时端口值,必须调用函数getsockname来返回协议地址。

进程捆绑非通配符的IP地址到套接字上的常见例子是在为多个组织提供Web服务器的主机上。首先,每个组织都得有各自的域名,譬如这样的形式:www.baidu.com。其次,每个组织的域名都映射到不同的地址,不过通常在同一个子网上。举例来说,如果子网是198.69.10,那么第一个组织的IP地址可以是198.69.10.128,第二个的IP地址可以是198.69.10.129,等等。然后,把所有这些IP地址都定义成单个网络接口的别名(譬如在4.4BSD系统上就使用ifconfig命令的alias选项来定义),这么一来,IP层将接收所有目的地为任何一个别名地址的外来数据报。最后,为每个组织启动一个HTTP服务器的副本,每个副本仅仅捆绑相应组织的IP地址。

从bind函数返回的一个常见错误是EADDRINUSE(“Address already in use”地址已使用)。

<think>好的,用户想了解的是sockaddr_in的定义和用法。首先,我需要回忆一下网络编程中socket相关的知识。sockaddr_in结构体用于IPv4地址的表示,属于套接字编程的基础部分。记得在C语言中,这个结构体通常包含地址家族、端口号和IP地址等信息。 首先,用户可能是在学习网络编程时遇到了这个结构体,所以需要解释清楚它的各个字段。比如,sin_family代表地址族,通常是AF_INET;sin_port是端口号,需要注意字节序转换;sin_addr是实际的IP地址,存储为in_addr结构。同时,用户可能对网络字节序和主机字节序的转换不太清楚,需要提到htons和inet_addr等函数的使用。 然后,用户可能想知道sockaddr_in和sockaddr的关系。这里需要说明sockaddr是通用结构,而sockaddr_in是专门用于IPv4的,使用时需要进行类型转换。例如,在调用bind函数时,需要将sockaddr_in指针转换为sockaddr指针,并指定结构体的大小。 用户的问题可能还涉及实际应用中的例子,比如如何初始化sockaddr_in结构体,设置端口和IP地址,以及绑定套接字的过程。给出一个代码示例会很有帮助,这样用户可以直观地看到各个步骤的实现。 另外,可能需要注意用户提到的引用,特别是引用[4]中提到sockaddr_in可以转换为sockaddr,并用于bind函数。需要确保解释部分与这些引用内容一致,正确引用标记。 用户可能还会有后续问题,比如IPv6的情况,这时候可以简要提到sockaddr_in6结构体,但重点还是在IPv4的sockaddr_in上。同时,字节序的问题也很关键,要强调网络字节序是大端模式,转换函数的使用是必须的,避免端口设置错误导致的绑定失败。 最后,确保回答结构清晰,分点说明定义、成员、用法和示例,并附上相关问题引导用户深入理解。检查LaTeX格式是否正确,行内公式使用$...$,独立公式用$$...$$,但在这个问题中可能涉及代码较多,注意代码块的正确展示。</think>### sockaddr_in结构体的定义与用法 #### 1. 定义 `sockaddr_in`是网络编程中用于表示**IPv4地址**的结构体,属于套接字编程的核心组成部分。其定义通常如下(以C语言为例): ```c struct sockaddr_in { sa_family_t sin_family; // 地址族(如AF_INET) in_port_t sin_port; // 16位端口号(需用网络字节序) struct in_addr sin_addr; // 32位IPv4地址(网络字节序) char sin_zero[8]; // 填充字段,通常置零 }; ``` #### 2. 核心成员解析 - **`sin_family`**: 地址族,固定为`AF_INET`(IPv4协议)[^4]。 - **`sin_port`**: 端口号,需通过`htons()`函数将主机字节序转换为网络字节序,例如`htons(8080)`。 - **`sin_addr`**: IPv4地址,存储为`struct in_addr`,可通过`inet_addr("192.168.1.1")`将字符串转换为网络字节序的32位整数。 #### 3. 用法 `sockaddr_in`主要用于以下场景: 1. **绑定套接字**:调用`bind()`时需指定本地地址和端口,例如: ```c struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); server_addr.sin_addr.s_addr = INADDR_ANY; // 绑定所有本地IP bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); // 强制转换为sockaddr ``` 2. **连接远程服务**:通过`connect()`指定目标地址时使用。 3. **接收/发送数据**:`accept()`或`recvfrom()`返回的地址信息。 #### 4. 与`sockaddr`的关系 `sockaddr`是通用套接字地址结构,而`sockaddr_in`是专用于IPv4的具体实现。两者内存布局兼容,使用时需强制类型转换: ```c struct sockaddr *addr_ptr = (struct sockaddr *)&server_addr; ``` #### 5. 示例:创建TCP服务器 ```c #include <sys/socket.h> #include <netinet/in.h> int main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in server_addr = { .sin_family = AF_INET, .sin_port = htons(8080), .sin_addr.s_addr = inet_addr("127.0.0.1") }; bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); listen(sockfd, 5); // ... accept连接并处理数据 return 0; } ``` ### 注意事项 - **字节序问题**:端口和IP地址必须使用网络字节序(大端模式),通过`htons()`/`htonl()`转换。 - **IPv6支持**:IPv6地址需使用`sockaddr_in6`结构体,地址族为`AF_INET6`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值