在recv之前关闭套接字而不调用close

时间:2016-12-30 04:13:52

标签: c sockets tcp network-programming

我正在尝试使用C程序接受来自Google Server的OAuth 2 authcode(以HTTP GET请求的形式)。如果我尝试使用recv()读取,则会抛出错误,传输端点未连接。这只是在我接受accept()调用后收到非负整数之后。我使用Java和Netcat实用程序尝试了相同的操作,对我来说都很好。

这是代码。

#include<stdio.h>
#include<string.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
#include<sys/select.h>
#include<fcntl.h>
#include<errno.h>

void *send_authcode_request(void *sockaddr_ptr);

int main() {
    char *bind_addr = "127.0.0.1";
    char *errmsg = (char *) malloc(1024);
    short int port = 0;

    setvbuf(stdout, NULL, _IONBF, 0);

    struct sockaddr_in local_addr;
    struct sockaddr_in client_addr;
    local_addr.sin_family = AF_INET;
    local_addr.sin_port = port;
    inet_aton(bind_addr, &(local_addr.sin_addr));
    memset(&(local_addr.sin_zero), '\0', 8);

    int sock_id = socket(AF_INET, SOCK_STREAM, 0);
    /*fcntl(sock_id, F_SETFL, O_NONBLOCK);*/

    if (bind(sock_id, (struct sockaddr*) &local_addr, sizeof(struct sockaddr)) == -1) {
        sprintf(errmsg, "Cannot bind socket to the port #%d", port);
        perror(errmsg);
        return -1;
    }
    if (listen(sock_id, 20) == -1) {
        sprintf(errmsg, "Cannot listen to connections on the socket");
        perror(errmsg);
        return -1;
    }
    int sin_size = sizeof(struct sockaddr_in);
    if(getsockname(sock_id, (struct sockaddr *) &local_addr, &sin_size) == -1) {
        sprintf(errmsg, "Unable to get the current address of the socket");
        perror(errmsg);
        return -1;
    }
    printf("\n Port no after binding #%d\n", ntohs(local_addr.sin_port));

    pthread_t authcode_req_thread;
    pthread_create(&authcode_req_thread, NULL, send_authcode_request, (void *) &local_addr);

    fd_set read_fs;
    FD_ZERO(&read_fs);
    FD_SET(sock_id, &read_fs);

    long accept_timeout = 60*10;

    struct timeval tv;
    tv.tv_sec = accept_timeout;
    tv.tv_usec = 0;

    char auth_code_info[1024];

    if (select(sock_id+1, &read_fs, NULL, NULL, &tv) > 0) {
        /*if(accept4(sock_id, (struct sockaddr *) &client_addr, &sin_size, SOCK_NONBLOCK) == -1) {*/
        if(accept(sock_id, (struct sockaddr *) &client_addr, &sin_size) >= 0) {
            /* Accept auth code */
            int bytesread;
            if ((bytesread = recv(sock_id, auth_code_info, 1023, 0)) == -1) {
                perror(errmsg);
                return -1;
            }
            printf("\n%s\n", auth_code_info);
            char *reply = "Done! You can now close the window";
            send(sock_id, reply, strlen(reply), 0);
        } else {
            sprintf(errmsg, "No client connected within timeout of %ld seconds", accept_timeout);
            perror(errmsg);
            return -1;
        }
    } else {
        sprintf(errmsg, "No client connected within timeout of %ld seconds", accept_timeout);
        perror(errmsg);
        return -1;
    }
    close(sock_id);
    printf("\n");
    return 0;
}

void *send_authcode_request(void *ptr) {
    char *scope = "https://www.googleapis.com/auth/drive";
    char *client_id = "1098482466406-c3h15ld90l03s0pl4rm7qq54fcb49t78.apps.googleusercontent.com";
    const char *temp_authcode_uri = "xdg-open \"https://accounts.google.com/o/oauth2/v2/auth?scope=%s&redirect_uri=http://127.0.0.1:%d&response_type=code&client_id=%s\"";

    struct sockaddr_in *socket_address = (struct sockaddr_in *) ptr;

    int max_authcode_uri_len = strlen(temp_authcode_uri) + strlen(client_id) + strlen(scope);
    char *authcode_uri = (char *) malloc(max_authcode_uri_len);
    sprintf(authcode_uri, temp_authcode_uri, scope, ntohs(socket_address->sin_port), client_id);
    printf("\n%s\n", authcode_uri);
    system(authcode_uri);
}

但是,当我使用 netcat 收听同一端口时,它会显示来自浏览器的HTTP请求。所以我使用Wireshark跟踪端口的TCP流量,发现一个流量与使用netcat时的流量不同。

对于我的程序

No.     Time           Source                Destination           Protocol Length Info
      1 0.000000000    127.0.0.1             127.0.0.1             TCP      74     48234 → 49927 [SYN] Seq=0 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=7268636 TSecr=0 WS=128
      2 0.000010715    127.0.0.1             127.0.0.1             TCP      74     49927 → 48234 [SYN, ACK] Seq=0 Ack=1 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=7268636 TSecr=7268636 WS=128
      3 0.000020978    127.0.0.1             127.0.0.1             TCP      66     48234 → 49927 [ACK] Seq=1 Ack=1 Win=43776 Len=0 TSval=7268636 TSecr=7268636
      4 0.000059901    127.0.0.1             127.0.0.1             TCP      74     48236 → 49927 [SYN] Seq=0 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=7268636 TSecr=0 WS=128
      5 0.000064624    127.0.0.1             127.0.0.1             TCP      74     49927 → 48236 [SYN, ACK] Seq=0 Ack=1 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=7268636 TSecr=7268636 WS=128
      6 0.000070536    127.0.0.1             127.0.0.1             TCP      66     48236 → 49927 [ACK] Seq=1 Ack=1 Win=43776 Len=0 TSval=7268636 TSecr=7268636
      7 0.000384302    127.0.0.1             127.0.0.1             TCP      66     49927 → 48234 [FIN, ACK] Seq=1 Ack=1 Win=43776 Len=0 TSval=7268636 TSecr=7268636
      8 0.000412860    127.0.0.1             127.0.0.1             TCP      66     49927 → 48236 [RST, ACK] Seq=1 Ack=1 Win=43776 Len=0 TSval=7268636 TSecr=7268636
      9 0.000997709    127.0.0.1             127.0.0.1             TCP      66     48234 → 49927 [FIN, ACK] Seq=1 Ack=2 Win=43776 Len=0 TSval=7268637 TSecr=7268636
     10 0.000974316    127.0.0.1             127.0.0.1             TCP      66     [TCP Keep-Alive] 48234 → 49927 [ACK] Seq=1 Ack=2 Win=43776 Len=0 TSval=7268637 TSecr=7268636
     11 0.001005630    127.0.0.1             127.0.0.1             TCP      66     49927 → 48234 [ACK] Seq=2 Ack=2 Win=43776 Len=0 TSval=7268637 TSecr=7268637
     12 0.001047174    127.0.0.1             127.0.0.1             TCP      74     48238 → 49927 [SYN] Seq=0 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=7268637 TSecr=0 WS=128
     13 0.001051855    127.0.0.1             127.0.0.1             TCP      54     49927 → 48238 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0
     14 0.237652758    127.0.0.1             127.0.0.1             TCP      74     48240 → 49927 [SYN] Seq=0 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=7268696 TSecr=0 WS=128
     15 0.237664760    127.0.0.1             127.0.0.1             TCP      54     49927 → 48240 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0
     16 5.243733213    127.0.0.1             127.0.0.1             TCP      74     48242 → 49927 [SYN] Seq=0 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=7269947 TSecr=0 WS=128
     17 5.243751969    127.0.0.1             127.0.0.1             TCP      54     49927 → 48242 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0

使用netcat

使用netcat,我得到包含身份验证令牌的所需HTTP Get请求。

No.     Time           Source                Destination           Protocol Length Info
      1 0.000000000    127.0.0.1             127.0.0.1             TCP      74     45064 → 2807 [SYN] Seq=0 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=7233868 TSecr=0 WS=128
      2 0.000008155    127.0.0.1             127.0.0.1             TCP      74     2807 → 45064 [SYN, ACK] Seq=0 Ack=1 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=7233868 TSecr=7233868 WS=128
      3 0.000015895    127.0.0.1             127.0.0.1             TCP      66     45064 → 2807 [ACK] Seq=1 Ack=1 Win=43776 Len=0 TSval=7233868 TSecr=7233868
      4 0.000043941    127.0.0.1             127.0.0.1             TCP      74     45066 → 2807 [SYN] Seq=0 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=7233868 TSecr=0 WS=128
      5 0.000047566    127.0.0.1             127.0.0.1             TCP      74     2807 → 45066 [SYN, ACK] Seq=0 Ack=1 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=7233868 TSecr=7233868 WS=128
      6 0.000050911    127.0.0.1             127.0.0.1             TCP      66     45066 → 2807 [ACK] Seq=1 Ack=1 Win=43776 Len=0 TSval=7233868 TSecr=7233868
      7 0.000074583    127.0.0.1             127.0.0.1             TCP      66     2807 → 45066 [RST, ACK] Seq=1 Ack=1 Win=43776 Len=0 TSval=7233868 TSecr=7233868
      8 0.000833585    127.0.0.1             127.0.0.1             HTTP     654    GET /?code=4/zuPKCQlOvIcJq5gAvPRGLQdkiICNzOmAQUAx9apK5go HTTP/1.1 
      9 0.000841060    127.0.0.1             127.0.0.1             TCP      66     2807 → 45064 [ACK] Seq=1 Ack=589 Win=44928 Len=0 TSval=7233868 TSecr=7233868
     10 45.005118503   127.0.0.1             127.0.0.1             TCP      66     [TCP Keep-Alive] 45064 → 2807 [ACK] Seq=588 Ack=1 Win=43776 Len=0 TSval=7245120 TSecr=7233868
     11 45.005166221   127.0.0.1             127.0.0.1             TCP      66     [TCP Keep-Alive ACK] 2807 → 45064 [ACK] Seq=1 Ack=589 Win=44928 Len=0 TSval=7245120 TSecr=7233868

如您所见,我的程序发送FIN标志,netcat中没有。我在哪里错了?

1 个答案:

答案 0 :(得分:0)

您正在从错误的套接字读取。

传递给listen的套接字只是一个侦听套接字。它不代表连接的套接字。它仅由本地IP和端口标识。

当您调用accept时,此函数返回已连接套接字的文件描述符。这些套接字由本地IP和端口以及远程IP和端口标识。因此,对于多个传入连接,可以多次调用此函数,每个传入连接都有自己的套接字。这样,您就可以处理来自多个端点的连接。

accept的返回值保存为新套接字,并将其用于sendrecv

}当你完成后,请务必close