虽然没有来自客户端的连接操作,但服务器尝试接受连接并因“非套接字上的套接字操作”而失败

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了虽然没有来自客户端的连接操作,但服务器尝试接受连接并因“非套接字上的套接字操作”而失败相关的知识,希望对你有一定的参考价值。

我正在使用TCP客户端 - 服务器协议做一个非常简单的应用程序。首先,我为服务器设置套接字并开始侦听客户端请求。建立连接后,客户端向我发送文件,然后我读取文件但在关闭连接后,另一个accept()调用在服务器端发生,并且失败。我只在客户端做一次connect()。我看到第二个accept上的套接字描述符值与第一个不同。

谢谢。

编辑:似乎我通过调用utility.c中的Readline()函数来覆盖套接字描述符的值。我的猜测是函数中的某些函数可能会覆盖其堆栈上方的值。

server.c

#include <sys/socket.h>       /*  socket definitions        */
#include <sys/types.h>        /*  socket types              */
#include <arpa/inet.h>        /*  inet (3) funtions         */
#include <errno.h>
#include <unistd.h>           /*  misc. UNIX functions      */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>

#include "utility.h"           /*  our own utility functions  */


/*  Global constants  */

#define ECHO_PORT          (2002)
#define MAX_LINE           (1000)


int main(int argc, char *argv[]) {
    int       list_s;                /*  listening socket          */
    int       conn_s;                /*  connection socket         */
    short int port;                  /*  port number               */
    struct    sockaddr_in servaddr;  /*  socket address structure  */
    struct    sockaddr_in incoming_addr;
    char*     endptr;                /*  for strtol()              */


    /*  Get port number from the command line, and
        set to default port if no arguments were supplied  */

    if ( argc == 2 ) {
    port = strtol(argv[1], &endptr, 0);
    if ( *endptr ) {
        fprintf(stderr, "ECHOSERV: Invalid port number.
");
        exit(EXIT_FAILURE);
    }
    }
    else if ( argc < 2 ) {
    port = ECHO_PORT;
    }
    else {
    fprintf(stderr, "ECHOSERV: Invalid arguments.
");
    exit(EXIT_FAILURE);
    }


    /*  Create the listening socket  */

    list_s = socket(AF_INET, SOCK_STREAM, 0);
    if (list_s < 0) {
    fprintf(stderr, "ECHOSERV: Error creating listening socket.
");
    exit(EXIT_FAILURE);
    }
    else {
        fprintf(stdout, "Socket successfully created.
");
    }


    /*  Set all bytes in socket address structure to
        zero, and fill in the relevant data members   */

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port        = htons(port);


    /* Bind the socket descriptor "list_s" to the servaddr which defines the port number. */

    if ( bind(list_s, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) {
    fprintf(stderr, "ECHOSERV: Error calling bind()
");
    exit(EXIT_FAILURE);
    }
    else {
        fprintf(stdout, "Socket successfully binded to port no %d.
", port);
    }

    if ( listen(list_s, 1) < 0 ) {
    fprintf(stderr, "ECHOSERV: Error calling listen()
");
    exit(EXIT_FAILURE);
    }
    else {
        fprintf(stdout, "Now listening to port: %d.
", port);
    }


    /*  Enter an infinite loop to respond
        to client requests and echo input  */

    while (1) {
    /*  Wait for a connection, then accept() it  */
        socklen_t incoming_addr_len = sizeof(incoming_addr);
        int server_socket = list_s;
        printf("The value of list_s is: %d.
", list_s);
        conn_s = accept(server_socket, (struct sockaddr*)(&incoming_addr), &incoming_addr_len);
    if ( conn_s < 0 ) {
        fprintf(stderr, "ECHOSERV: Error calling accept(): %s.
", strerror(errno));
        exit(EXIT_FAILURE);
    }

        /* Read the data from the socket descriptor which in this case  */
        /* is the size of the file that is to be received. */

        int data_size;
        Readline(conn_s, &data_size, sizeof(data_size));


        /* Read data from the given socket descriptor conn_s and then
         * write the same data to the descriptor again to be sent as
         * a response. */

        char buffer[data_size];
        Readline(conn_s, buffer, data_size);
        printFile(buffer, data_size);


    /*  Close the connected socket  */

    if ( close(conn_s) < 0 ) {
        fprintf(stderr, "ECHOSERV: Error calling close()
");
        exit(EXIT_FAILURE);
    }
    }
}

client.c

#include <sys/socket.h>       /*  socket definitions        */
#include <sys/types.h>        /*  socket types              */
#include <arpa/inet.h>        /*  inet (3) funtions         */
#include <unistd.h>           /*  misc. UNIX functions      */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "utility.h"           /*  Our own utility functions  */


/*  Global constants  */

#define MAX_LINE           (1000)
#define DATA_IN_FILE       (4)

static const char* const kFileName = "/home/pb/Desktop/workspace/Networking/practice_project/practice_project_test_file_1";

/*  Function declarations  */

int ParseCmdLine(int argc, char *argv[], char **szAddress, char **szPort);


/*  main()  */

int main(int argc, char *argv[]) {

    int       conn_s;                /*  connection socket         */
    short int port;                  /*  port number               */
    struct    sockaddr_in servaddr;  /*  socket address structure  */
    char      buffer[MAX_LINE];      /*  character buffer          */
    char     *szAddress;             /*  Holds remote IP address   */
    char     *szPort;                /*  Holds remote port         */
    char     *endptr;                /*  for strtol()              */


    /*  Get command line arguments  */

    ParseCmdLine(argc, argv, &szAddress, &szPort);


    /*  Set the remote port  */

    port = strtol(szPort, &endptr, 0);
    if ( *endptr ) {
    printf("ECHOCLNT: Invalid port supplied.
");
    exit(EXIT_FAILURE);
    }


    /*  Create the listening socket  */

    if ( (conn_s = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
    fprintf(stderr, "ECHOCLNT: Error creating listening socket.
");
    exit(EXIT_FAILURE);
    }


    /*  Set all bytes in socket address structure to
        zero, and fill in the relevant data members   */

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_port        = htons(port);


    /*  Set the remote IP address  */

    if ( inet_aton(szAddress, &servaddr.sin_addr) <= 0 ) {
    printf("ECHOCLNT: Invalid remote IP address.
");
    exit(EXIT_FAILURE);
    }


    /*  connect() to the remote echo server  */
    if ( connect(conn_s, (struct sockaddr *) &servaddr, sizeof(servaddr) ) < 0 ) {
    printf("ECHOCLNT: Error calling connect()
");
    exit(EXIT_FAILURE);
    }

    FILE* f = fopen(kFileName, "rb");
    if (f == NULL) {
        perror("Error ");
        return -1;
    }
    fseek(f, 0, SEEK_END);
    int size_of_file = ftell(f);
    rewind(f);
    char file_buffer[size_of_file];
    int items_read = fread(file_buffer, 1, size_of_file, f);
    if (items_read != size_of_file) {
        printf("Read the rest.
");
    }

    /* First sending to the server the size of the data in the  */
    /* file in bytes as an integer. */

    sendData(conn_s, &size_of_file, sizeof(int));

    /* This is the actual file now that we have let the server  */
    /* know the size of the file to be send. */
    sendData(conn_s, file_buffer, size_of_file);

    return EXIT_SUCCESS;
}


int ParseCmdLine(int argc, char *argv[], char **szAddress, char **szPort) {

    int n = 1;

    while ( n < argc ) {
    if ( !strncmp(argv[n], "-a", 2) || !strncmp(argv[n], "-A", 2) ) {
        *szAddress = argv[++n];
    }
    else if ( !strncmp(argv[n], "-p", 2) || !strncmp(argv[n], "-P", 2) ) {
        *szPort = argv[++n];
    }
    else if ( !strncmp(argv[n], "-h", 2) || !strncmp(argv[n], "-H", 2) ) {
        printf("Usage:

");
        printf("    timeclnt -a (remote IP) -p (remote port)

");
        exit(EXIT_SUCCESS);
    }
    ++n;
    }

    return 0;
}

utility.c

#include "utility.h"

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>


int printFile(char *binary_buffer, int buffer_size) {
    int current_position = 0;
    while (current_position < buffer_size) {
        if(binary_buffer[current_position] == 0) {
            printf("
");
            printf("Type 1.
");
            current_position = read_first_type(binary_buffer, current_position);
        }
        else if(binary_buffer[current_position] == 1) {
            printf("
");
            printf("Type 2.
");
            current_position = read_second_type(binary_buffer, current_position);
        }
    }
    return 0;
}


int read_first_type(const char* binary, const int offset) {
    int size_of_units = 2;
    int no_of_units;
    short unit_buffer[1];

    // current position is always Type and no_of_units is amount which is a byte off of Type.
    no_of_units = binary[offset+1]; 
    printf("No of units is %d.
", no_of_units);
    for (int i=0; i<no_of_units; ++i) {
        // This is a Big-endian system so the values will bit off.
        memcpy(unit_buffer, (binary+offset+2)+i*size_of_units, 2);
        printf("Type 1 unit has number: %d.
", unit_buffer[0]);
    }
    return (offset + no_of_units*size_of_units + 2); 
}


int read_second_type(const char* binary, const int offset) {
    int no_of_units;
    int current_position = 4;
    char amount[4];
    memcpy(amount, binary+offset+1, 3);
    amount[3] = '';
    no_of_units = atoi(amount);
    printf("No of units is %d.
", no_of_units);
    char unit_buffer[5];
    int count;
    for(int i=0; i<no_of_units; ++i) {
        count = 0;
        while(1) {
            memcpy(unit_buffer+count, binary+offset+current_position+count, 1);
            if (unit_buffer[count] == 44) {
                unit_buffer[count] = '';
                current_position += count+1;
                break;
            }
            if (unit_buffer[count] == 0 || unit_buffer[count] == 1) {
                unit_buffer[count] = '';
                printf("Type 2 unit has number: %d.
",atoi(unit_buffer));
                return (offset+current_position+count);
            }
            count++;
        }
        printf("Type 2 unit has number: %d.
",atoi(unit_buffer));
    }
    return offset+current_position+count;
}

/*  Read a line from a socket  */

ssize_t Readline(int sockd, void *vptr, size_t maxlen) {
    ssize_t n, rc;
    char    c, *buffer;

    buffer = vptr;

    for ( n = 0; n < maxlen; n++ ) {
    if ( (rc = read(sockd, &c, 1)) == 1 ) {
        *buffer++ = c;
        if ( c == '
' )
        break;
    }
    else if ( rc == 0 ) {
        if ( n == 0 )
        return 0;
        else
        break;
    }
    else {
        if ( errno == EINTR )
        continue;
        return -1;
    }
    }

    *buffer = 0;
    return n;
}


/*  Write a line to a socket  */

ssize_t sendData(int sockd, const void *vptr, size_t n) {
    size_t      nleft;
    ssize_t     nwritten;
    const char *buffer;

    buffer = vptr;
    nleft  = n;

    while ( nleft > 0 ) {
    if ( (nwritten = write(sockd, buffer, nleft)) <= 0 ) {
        if ( errno == EINTR )
        nwritten = 0;
        else
        return -1;
    }
    nleft  -= nwritten;
    buffer += nwritten;
    }

    return n;
}
答案

如果在ReadLine中读取了完整的缓冲区(即maxlen在循环中到达),那么在最后一个字符读取后仍然会写入0x0,从而导致缓冲区超出:

ssize_t Readline(int sockd, void *vptr, size_t maxlen) {
 ...
    for ( n = 0; n < maxlen; n++ ) {
        if ( (rc = read(sockd, &c, 1)) == 1 ) {
           *buffer++ = c;
 ...
    *buffer = 0;         <<<<<<<<<<<< might be vptr[maxlen+1], i.e. buffer overflow
    return n;

这样做几乎总会发生

  Readline(conn_s, &data_size, sizeof(data_size));

只有当组成data_size的八位字节在缓冲区结束之前包含 时才会发生,但是那时你会读错了数字。

此缓冲区溢出可能会导致相邻内存损坏,这可能会导致您看到的问题。

以上是关于虽然没有来自客户端的连接操作,但服务器尝试接受连接并因“非套接字上的套接字操作”而失败的主要内容,如果未能解决你的问题,请参考以下文章

使用ServerSocket创建TCP服务器端

无法解码来自客户端的握手请求

如何在WebSocket的服务器侧检测客户端的断开连接

Python UDP服务器无法接受客户端连接

如何从 HttpServlet 关闭 HTTP 连接

为啥 ZeroMQ 服务器没有收到来自客户端的任何请求?