我目前正在测试我的网络代码。这涉及通过 IPv4 环回地址 (127.0.0.1) 建立连接。不幸的是,程序经常(并非总是)在发送数据时给出 EPIPE 错误。
我正在使用伯克利网络套接字和 libevent。我通过以下方式制作了一个非阻塞套接字:
CBSocketReturn CBNewSocket(uint64_t * socketID,bool IPv6){
*socketID = socket(IPv6 ? PF_INET6 : PF_INET, SOCK_STREAM, 0);
if (*socketID == -1) {
if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
return CB_SOCKET_NO_SUPPORT;
}
return CB_SOCKET_BAD;
}
// Stop SIGPIPE annoying us.
if (CB_NOSIGPIPE) {
int i = 1;
setsockopt(*socketID, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof(i));
}
// Make socket non-blocking
evutil_make_socket_nonblocking((evutil_socket_t)*socketID);
return CB_SOCKET_OK;
}
我通过以下方式进行连接事件:
bool CBSocketDidConnectEvent(uint64_t * eventID,uint64_t loopID,uint64_t socketID,void (*onDidConnect)(void *,void *),void * node){
CBEvent * event = malloc(sizeof(*event));
event->loop = (CBEventLoop *)loopID;
event->onEvent.ptr = onDidConnect;
event->node = node;
event->event = event_new(((CBEventLoop *)loopID)->base, (evutil_socket_t)socketID, EV_TIMEOUT|EV_WRITE, CBDidConnect, event);
if (NOT event->event) {
free(event);
event = 0;
}
*eventID = (uint64_t)event;
return event;
}
void CBDidConnect(evutil_socket_t socketID,short eventNum,void * arg){
CBEvent * event = arg;
if (eventNum & EV_TIMEOUT) {
// Timeout for the connection
event->loop->onTimeOut(event->loop->communicator,event->node,CB_TIMEOUT_CONNECT);
}else{
// Connection successful
event->onEvent.ptr(event->loop->communicator,event->node);
}
}
并通过以下方式添加:
bool CBSocketAddEvent(uint64_t eventID,uint16_t timeout){
CBEvent * event = (CBEvent *)eventID;
int res;
if (timeout) {
struct timeval time = {timeout,0};
res = event_add(event->event, &time);
}else
res = event_add(event->event, NULL);
return NOT res;
}
连接:
bool CBSocketConnect(uint64_t socketID,uint8_t * IP,bool IPv6,uint16_t port){
// Create sockaddr_in6 information for a IPv6 address
int res;
if (IPv6) {
struct sockaddr_in6 address;
memset(&address, 0, sizeof(address)); // Clear structure.
address.sin6_family = AF_INET6;
memcpy(&address.sin6_addr, IP, 16); // Move IP address into place.
address.sin6_port = htons(port); // Port number to network order
res = connect((evutil_socket_t)socketID, (struct sockaddr *)&address, sizeof(address));
}else{
struct sockaddr_in address;
memset(&address, 0, sizeof(address)); // Clear structure.
address.sin_family = AF_INET;
memcpy(&address.sin_addr, IP + 12, 4); // Move IP address into place. Last 4 bytes for IPv4.
address.sin_port = htons(port); // Port number to network order
res = connect((evutil_socket_t)socketID, (struct sockaddr *)&address, sizeof(address));
}
if (NOT res || errno == EINPROGRESS)
return true;
return false;
}
连接后,会发生 canSend 事件:
bool CBSocketCanSendEvent(uint64_t * eventID,uint64_t loopID,uint64_t socketID,void (*onCanSend)(void *,void *),void * node){
CBEvent * event = malloc(sizeof(*event));
event->loop = (CBEventLoop *)loopID;
event->onEvent.ptr = onCanSend;
event->node = node;
event->event = event_new(((CBEventLoop *)loopID)->base, (evutil_socket_t)socketID, EV_TIMEOUT|EV_WRITE|EV_PERSIST, CBCanSend, event);
if (NOT event->event) {
free(event);
event = 0;
}
*eventID = (uint64_t)event;
return event;
}
void CBCanSend(evutil_socket_t socketID,short eventNum,void * arg){
CBEvent * event = arg;
if (eventNum & EV_TIMEOUT) {
// Timeout when waiting to write.
event->loop->onTimeOut(event->loop->communicator,event->node,CB_TIMEOUT_SEND);
}else{
// Can send
event->onEvent.ptr(event->loop->communicator,event->node);
}
}
但是发送经常会出现 EPIPE 错误:
int32_t CBSocketSend(uint64_t socketID,uint8_t * data,uint32_t len){
ssize_t res = send((evutil_socket_t)socketID, data, len, CB_SEND_FLAGS);
printf("SENT (%li): ",res);
for (uint32_t x = 0; x < res; x++) {
printf("%c",data[x]);
}
printf("\n");
if (res >= 0)
return (int32_t)res;
if (errno == EAGAIN)
return 0; // False event. Wait again.
return CB_SOCKET_FAILURE; // Failure
}
它登陆return CB_SOCKET_FAILURE;
并且 errno 设置为 EPIPE。现在为什么会这样?如果设置了发送标志,则它只是 MSG_NOSIGNAL,因为 SIGPIPE 一直以这个错误中断程序。我希望 EPIPE 导致 CBSocketSend 返回 CB_SOCKET_FAILURE 并且不中断程序,但是 EPIPE 没有原因导致发送失败,那么为什么要这样做呢?
上次我收到错误时,我注意到连接的线程仍在 connect() 调用中。使连接事件由单独的线程而不是连接的线程处理是否有危险?
在这些地方查看网络代码:
https://github.com/MatthewLM/cbitcoin/blob/master/test/testCBNetworkCommunicator.c https://github.com/MatthewLM/cbitcoin/tree/master/src/structures/CBObject/CBNetworkCommunicator https://github。 com/MatthewLM/cbitcoin/tree/master/dependencies/sockets
谢谢你。
编辑:我再次运行它,在 connect() 完成后出现错误。
编辑 2:似乎连接事件是在没有对方接受的情况下给出的。