찾다

 >  Q&A  >  본문

c++ - linux下accept之前异常终止连接问题?

服务端代码:


#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdint.h>
#include <assert.h>
#include <fcntl.h>
#include <string.h>
#include <bits/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>

const char* ip = "192.168.16.105";
uint16_t port = 8888;
char buff[1024];

void make_sock_noblocking(int sock_fd)
{
    int flags = fcntl(sock_fd, F_GETFL, 0);
    flags |= O_NONBLOCK;
    fcntl(sock_fd, F_SETFL, flags);
}

int main()
{
    int listen_fd = socket(PF_INET, SOCK_STREAM, 0);

    struct sockaddr_in sock_addr;
    memset(&sock_addr, 0, sizeof(sock_addr));
    sock_addr.sin_family = AF_INET;
    sock_addr.sin_port = htons(port);
    inet_pton(AF_INET, ip, &(sock_addr.sin_addr));

    int optval = 1;
    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR,
               (const void *)&optval , sizeof(int));
    int ret = bind(listen_fd, (sockaddr*)&sock_addr, sizeof(sock_addr));

    listen(listen_fd, 1024);

    make_sock_noblocking(listen_fd);

    sleep(10);

    struct sockaddr_in client_addr;
    memset(&sock_addr, 0, sizeof(client_addr));
    socklen_t len = sizeof(client_addr);
    int conn_fd = accept(listen_fd, (sockaddr*)&client_addr, &(len));

    printf("%d--------%s\n", conn_fd, strerror(errno));

    int read_bytes = read(conn_fd, buff, sizeof(buff));

    printf("%d %s\n", read_bytes, strerror(errno));

    return 0;
}

客户端代码:


#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdint.h>
#include <assert.h>
#include <fcntl.h>
#include <string.h>
#include <bits/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <errno.h>

const char* ip = "192.168.16.105";
uint16_t port = 8888;

int main()
{
    int sock_fd = socket(PF_INET, SOCK_STREAM, 0);

    struct sockaddr_in sock_addr;
    memset(&sock_addr, 0, sizeof(sock_addr));
    sock_addr.sin_family = AF_INET;
    sock_addr.sin_port = htons(port);
    inet_pton(AF_INET, ip, &(sock_addr.sin_addr));

    int ret = connect(sock_fd, (sockaddr*)&sock_addr, sizeof(sock_addr));

    if(ret < 0)
    {
        printf("%s\n", strerror(errno));
    }

    struct linger my_linger;
    memset(&my_linger, 0, sizeof(my_linger));
    my_linger.l_onoff = 1;
    my_linger.l_linger = 0;

    setsockopt(sock_fd, SOL_SOCKET, SO_LINGER, &my_linger, sizeof(my_linger));

    close(sock_fd);

    return 0;
}

问题:
accpet之前sleep 10秒钟,这期间客户端向服务端发送RST终止连接,但是服务端这边还是会成功接受连接。按unp讲的不应该是返回一个ECONNABORTED(POSIX.1)错误吗?如果我把套接字的非阻塞去掉变成阻塞的话,依然可以成功接受连接,accept并不会阻塞,但是accept取出的这个socket去读的话就会出现Connection reset by peer错误了。莫非linux的实现是不管accept之前是不是被RST了,只管取出来,但取出来的这个socket再操作的话就会报错?

巴扎黑巴扎黑2867일 전790

모든 응답(2)나는 대답할 것이다

  • 阿神

    阿神2017-04-17 15:36:59

    수신 후 시스템은 해당 대기열을 유지하고 이 청취 소켓에 대한 모든 연결을 처리합니다. Accept는 완료된 연결을 대기열에서 제거합니다. 따라서 accept 호출 여부에 관계없이 클라이언트의 TCP 연결은 3방향 핸드셰이크를 완료할 수 있습니다.
    귀하의 코드에 따르면 먼저 서버가 수신을 시작한 다음 절전 모드로 전환됩니다. 이 기간 동안 클라이언트가 실행되고 먼저 연결을 호출하여 3방향 핸드셰이크를 완료합니다. 그런 다음 클라이언트는 close를 호출하여 연결을 닫고 FIN을 보냅니다. TCP는 전이중 프로토콜이므로 클라이언트가 스스로 연결을 닫아야 합니다. 자체 연결이 있고 서버 연결이 여전히 존재합니다. 따라서 연결을 수락할 수 있지만 소켓에 대한 후속 작업에서는 오류가 반환되므로 적극적으로 닫아야 합니다.
    이전에는 연결을 종료하기 위해 RST가 사용되었다는 사실을 몰랐습니다. 2.6.32의 커널 코드를 살펴보니 RST를 받은 후 서버가 모든 상태를 정리하고 연결을 직접 끊게 됩니다. 하지만 요청한 리소스는 재활용되지 않습니다. 이 프로세스는 종료됩니다. 따라서 수락할 때 청취 대기열의 구조는 여전히 존재하며 제거될 수 있습니다. 수락 자체는 실제로 검색 프로세스일 뿐입니다.

    회신하다
    0
  • PHP中文网

    PHP中文网2017-04-17 15:36:59

    sleep10 여부에 관계없이 클라이언트가 연결을 차단합니다. 서버가 소켓을 읽으려고 오면 클라이언트가 닫히고 피어에 의해 연결 재설정 오류가 보고됩니다.

    회신하다
    0
  • 취소회신하다