为什么 getsockopt optlen 为零?
Why getsockopt optlen is zero?
我使用非阻塞套接字和事件库。我只是注意到当我随机调用快速连接到我本地 ip 的端口时 getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &optlen);
的 optlen 变为 0.
我尝试制作能够显示问题的更小的代码。我在这段代码中使用了 epoll 但同样的问题也发生在其他事件库中。
#define nconnect_d 500
#define ebuffer_d 64
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
struct sockaddr_in initaddr(uint32_t ip, uint16_t port){
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(ip);
memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
return addr;
}
int getconnectfd(uint32_t sip, uint16_t sport, uint32_t dip, uint16_t dport){
int fd;
if((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
return -1;
if(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (int[]){1}, sizeof(int)) == -1)
return -1;
if(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) == -1)
return -1;
struct sockaddr_in saddr = initaddr(sip, sport);
if(bind(fd, (const struct sockaddr *)&saddr, (socklen_t)sizeof(struct sockaddr_in)) == -1)
return -1;
struct sockaddr_in daddr = initaddr(dip, dport);
if(connect(fd, (const struct sockaddr*)&daddr, (socklen_t)sizeof(struct sockaddr_in)) == -1 && errno != EINPROGRESS)
return -1;
return fd;
}
int epolltouch(int efd, int sfd, uint32_t flag){
struct epoll_event event;
event.data.fd = sfd;
event.events = flag;
if(epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event) == -1)
return -1;
return 0;
}
int epollrm(int efd, int sfd){
if(epoll_ctl(efd, EPOLL_CTL_DEL, sfd, NULL) == -1)
return -1;
return 0;
}
int main(){
int efd = epoll_create1(0);
assert(efd != -1);
for(uint32_t i = 0; i < nconnect_d; i++){
int sfd = getconnectfd(INADDR_ANY, 12420, 0x7f000001, 2048 + i);
assert(sfd != -1);
epolltouch(efd, sfd, EPOLLOUT | EPOLLET);
}
struct epoll_event events[ebuffer_d];
uint32_t iconnect = nconnect_d;
while(iconnect){
int n = epoll_wait(efd, events, ebuffer_d, -1);
assert(n != -1);
for(uint32_t i = 0; i < n; i++){
int evfd = events[i].data.fd;
int opt, optlen;
assert(getsockopt(evfd, SOL_SOCKET, SO_ERROR, &opt, &optlen) != -1);
assert(optlen == sizeof(int));
switch(opt){
case 0:{
/* connection has been established */
break;
}
case ECONNREFUSED:
case EHOSTUNREACH:
case ENETUNREACH:
/* can be more valid case isn't it? */
{
/* connection has failed */
break;
}
default:{
assert(0);
}
}
iconnect--;
epollrm(efd, evfd);
close(evfd);
}
}
return 0;
}
当我运行;
$ ./a.out
a.out: temp.c:72: main: Assertion `optlen == sizeof(int)' failed.
Aborted (core dumped)
optlen 0 是否意味着套接字因某种原因关闭?我们需要在它之后关闭套接字吗?
调用getsockopt()
需要您向系统提供缓冲区,这意味着您必须在调用前设置optlen
:
int opt;
socklen_t optlen = sizeof opt;
... getsockopt(evfd, SOL_SOCKET, SO_ERROR, &opt, &optlen);
这样,getsockopt()
知道允许写入“缓冲区”的量,在本例中它只是一个整数,它会用实际字节数更新 optlen
写了。
手册页指出 optlen
是一个 value/result,这意味着您将它设置进去并期望得到另一个值,但 opt
只是一个结果不关心输入的值是多少。
EDIT 将 optlen 的类型固定为 socklen_t
,将 h/t 固定为 prog-fh
使用getsockopt()
时最后一个参数必须是地址
初始化 socklen_t
。
在通话之前,您应该使用存储大小对其进行初始化
获取结果值(此处为sizeof(opt)
)。
它是按地址传递的,因为如果这个存储空间太大
对于实际结果,然后系统调用将其调整为
确切尺寸。
我使用非阻塞套接字和事件库。我只是注意到当我随机调用快速连接到我本地 ip 的端口时 getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &optlen);
的 optlen 变为 0.
我尝试制作能够显示问题的更小的代码。我在这段代码中使用了 epoll 但同样的问题也发生在其他事件库中。
#define nconnect_d 500
#define ebuffer_d 64
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
struct sockaddr_in initaddr(uint32_t ip, uint16_t port){
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(ip);
memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
return addr;
}
int getconnectfd(uint32_t sip, uint16_t sport, uint32_t dip, uint16_t dport){
int fd;
if((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
return -1;
if(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (int[]){1}, sizeof(int)) == -1)
return -1;
if(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) == -1)
return -1;
struct sockaddr_in saddr = initaddr(sip, sport);
if(bind(fd, (const struct sockaddr *)&saddr, (socklen_t)sizeof(struct sockaddr_in)) == -1)
return -1;
struct sockaddr_in daddr = initaddr(dip, dport);
if(connect(fd, (const struct sockaddr*)&daddr, (socklen_t)sizeof(struct sockaddr_in)) == -1 && errno != EINPROGRESS)
return -1;
return fd;
}
int epolltouch(int efd, int sfd, uint32_t flag){
struct epoll_event event;
event.data.fd = sfd;
event.events = flag;
if(epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event) == -1)
return -1;
return 0;
}
int epollrm(int efd, int sfd){
if(epoll_ctl(efd, EPOLL_CTL_DEL, sfd, NULL) == -1)
return -1;
return 0;
}
int main(){
int efd = epoll_create1(0);
assert(efd != -1);
for(uint32_t i = 0; i < nconnect_d; i++){
int sfd = getconnectfd(INADDR_ANY, 12420, 0x7f000001, 2048 + i);
assert(sfd != -1);
epolltouch(efd, sfd, EPOLLOUT | EPOLLET);
}
struct epoll_event events[ebuffer_d];
uint32_t iconnect = nconnect_d;
while(iconnect){
int n = epoll_wait(efd, events, ebuffer_d, -1);
assert(n != -1);
for(uint32_t i = 0; i < n; i++){
int evfd = events[i].data.fd;
int opt, optlen;
assert(getsockopt(evfd, SOL_SOCKET, SO_ERROR, &opt, &optlen) != -1);
assert(optlen == sizeof(int));
switch(opt){
case 0:{
/* connection has been established */
break;
}
case ECONNREFUSED:
case EHOSTUNREACH:
case ENETUNREACH:
/* can be more valid case isn't it? */
{
/* connection has failed */
break;
}
default:{
assert(0);
}
}
iconnect--;
epollrm(efd, evfd);
close(evfd);
}
}
return 0;
}
当我运行;
$ ./a.out
a.out: temp.c:72: main: Assertion `optlen == sizeof(int)' failed.
Aborted (core dumped)
optlen 0 是否意味着套接字因某种原因关闭?我们需要在它之后关闭套接字吗?
调用getsockopt()
需要您向系统提供缓冲区,这意味着您必须在调用前设置optlen
:
int opt;
socklen_t optlen = sizeof opt;
... getsockopt(evfd, SOL_SOCKET, SO_ERROR, &opt, &optlen);
这样,getsockopt()
知道允许写入“缓冲区”的量,在本例中它只是一个整数,它会用实际字节数更新 optlen
写了。
手册页指出 optlen
是一个 value/result,这意味着您将它设置进去并期望得到另一个值,但 opt
只是一个结果不关心输入的值是多少。
EDIT 将 optlen 的类型固定为 socklen_t
,将 h/t 固定为 prog-fh
使用getsockopt()
时最后一个参数必须是地址
初始化 socklen_t
。
在通话之前,您应该使用存储大小对其进行初始化
获取结果值(此处为sizeof(opt)
)。
它是按地址传递的,因为如果这个存储空间太大
对于实际结果,然后系统调用将其调整为
确切尺寸。