套接字编程疑问:为何connect()需使用sizeof(addr)而accept()可使用(socklen_t)&addr?
为什么
accept()和connect()的最后一个参数用法不一样? 这个问题问到点子上了!本质是这两个套接字函数对最后一个参数的设计逻辑完全不同,咱们掰开揉碎了说:
先搞懂accept()的socklen_t *addr_len
accept()的最后一个参数是输入输出指针,它承担两个核心作用:
- 调用前:你得把指针指向的变量赋值为
addr结构体的实际大小(比如sizeof(struct sockaddr_in)),告诉内核“我给你的地址缓冲区就这么大,别写超了”; - 调用后:内核会把实际接收到的客户端地址的真实长度写回这个变量里(毕竟不同地址类型长度不一样,比如IPv6比IPv4长)。
你之前写的(socklen_t *)&addr其实是个错误用法,只是编译器没拦你而已!你把addr结构体的地址强转成socklen_t*,相当于让内核去修改addr结构体的前几个字节(socklen_t通常是4或8字节),这会直接破坏addr的结构,属于未定义行为,运气好没崩,运气差直接程序崩溃。正确的写法应该是:
struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); accept(m_sock, (struct sockaddr *)&addr, &addr_len);
再看connect()的socklen_t addr_len
connect()的最后一个参数是纯输入值,它的作用很简单:告诉内核“我传入的目标地址结构体addr的大小是多少”,内核不需要给你返回任何长度相关的信息,所以直接传值就行。
你尝试写的(socklen_t)&addr犯了两个明显的错误:
- 类型不匹配:
connect()要的是socklen_t类型的数值,你传的是把结构体指针强转成socklen_t——指针是内存地址(通常64位系统是8字节,32位是4字节),而socklen_t一般是4字节的unsigned int,强制转换会截断高位,直接丢失信息,这就是编译器报错的原因; - 逻辑错误:内核需要的是地址结构体的大小,不是结构体的内存地址,你传地址过去完全不是内核要的东西。
所以connect()的正确写法就是直接传sizeof(addr):
struct sockaddr_in addr; // 填充addr的IP、端口等目标地址信息 connect(m_sock, (struct sockaddr *)&addr, sizeof(addr));
一句话总结
accept()用指针是因为需要内核回传实际地址长度,connect()用值是因为只需要告诉内核传入地址的大小——两者的参数设计完全是由各自的功能需求决定的。你之前在accept()里的写法其实是个隐患,别再那么用啦!
内容的提问来源于stack exchange,提问作者user15747807
相关产品推荐
相关产品推荐

