套接字编程 - recv()没有正确接收数据

时间:2015-06-04 22:05:27

标签: c sockets

我看过类似的线程,但似乎无法找到任何可以解决我问题的东西。

我正在编写一个服务器,可以从客户端发送给它的路径发送图像(jpg)文件。我在C中使用send / recv函数。

我一次读取一大块数据并将其发送到接收内容的客户端,并将它们写在某个位置以构建文件。

问题是'recv'没有收到'send'发送的字节数。

作为调试的一部分,我尝试了不同的缓冲区大小,“128”缓冲区大小没有给我任何问题,文件已成功传输和构建。

但是,对于'32'和'64'位缓冲区,'recv'在最后一个块上接收'32'位或'64'位数据,即使服务器发送的数据小于'32'位或'64'位。并且,对于'256','512','1024'等等,'recv'在完全一个响应中仅返回'128'位,即使服务器发送完整的块,即'256'或'512',取决于缓冲区大小。

我很感激任何有关调试的建议。以下代码仅适用于相关部分,但如果有人需要,我可以提供更多内容。

//客户端代码

#define BUFFER_SIZE 4096

#define   HEADER_LEN 512

const char * const scheme = "GETFILE";
const char* const method = "GET";
const char * const end_marker = "\\r\\n\\r\\n";

struct gfcrequest_t
{
    int filelen;
    char cport[12];
    char servIP[50];
    gfstatus_t ret_status;
    char spath[1024];
    int tot_bytes;
    char filecontent[BUFFER_SIZE];

    void (*fl_handler)(void *, size_t, void *);
    void * fDesc;

    void (*head_handler)(void *, size_t, void *);
    void * headarg;

};

static pthread_mutex_t counter_mutex;

gfcrequest_t *gfc;

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET)
    {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }
    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

static char *stringFromError(gfstatus_t stat)
{
    static const char *strings[] = {"GF_OK", "GF_FILE_NOT_FOUND", "GF_ERROR", "GF_INVALID"};

    return strings[stat];
}


int getFileRequestHeader(char * req_header)
{
    return snprintf(req_header,HEADER_LEN, "%s%s%s%s", scheme, method,gfc->spath, end_marker);
   // return snprintf(req_header,HEADER_LEN, "%s%s%s%s", scheme, method,"/courses/ud923/filecorpus/yellowstone.jpg", end_marker);
}


gfcrequest_t *gfc_create()
{

    gfc = (gfcrequest_t*)malloc(1* sizeof(gfcrequest_t));

    return gfc;
}

void gfc_set_server(gfcrequest_t *gfr, char* server)
{
    strcpy(gfr->servIP, server);
}

void gfc_set_path(gfcrequest_t *gfr, char* path)
{
    strcpy(gfr->spath, path);
}

void gfc_set_port(gfcrequest_t *gfr, unsigned short port)
{
    snprintf(gfr->cport,12, "%u",port);
}

void gfc_set_headerfunc(gfcrequest_t *gfr, void (*headerfunc)(void*, size_t, void *))
{

    gfr->head_handler = headerfunc;

}

void gfc_set_headerarg(gfcrequest_t *gfr, void *headerarg)
{
/*have to change this...*/
    gfr->headarg = headerarg;

}

int isEndMarker(char *iheader, int start)
{
    char *marker = "\\r\\n\\r\\n";
    int i = 0; int ind=0;


    while (ind <= 7)
    {
        if (iheader[start++] == marker[ind++])
        {
            continue;
        }

        return 0;
    }

    return 1;

}

int getFileLen(char *resp_header, gfcrequest_t *gfr)
{
    char scheme[8];
    char status[4];
    int istatus;
    char filelen[12];

    int contentlen = 0;

    int fileindex = 0;
    char end_marker[12];
    int fexit=0;
    int end=0;

    sscanf(resp_header, "%7s%3s", scheme, status);


    istatus = atoi(status);

    if (istatus == 200)
    {
        gfr->ret_status = GF_OK;
    }
    else if (istatus == 400)
    {
        gfr->ret_status = GF_FILE_NOT_FOUND;
    }
    else if (istatus == 500)
    {
        gfr->ret_status = GF_ERROR;

    }

    if (!strcmp(scheme, "GETFILE") && (istatus == 200 || istatus == 400 || istatus == 500))
    {
        int index = 10;
        while(1)
        {

            if (resp_header[index] == '\\')
            {
                end = isEndMarker(resp_header, index);
            }

            if (end)
                break;

            filelen[fileindex++] = resp_header[index++];

        }

        filelen[fileindex] = '\0';
    }

    int head_len = strlen(scheme) + strlen(status) + strlen(filelen) + 8;

    return atoi(filelen);

}

void gfc_set_writefunc(gfcrequest_t *gfr, void (*writefunc)(void*, size_t, void *))
{

    gfr->fl_handler = writefunc;

}

void gfc_set_writearg(gfcrequest_t *gfr, void *writearg)
{
    gfr->fDesc = writearg;
}

int gfc_perform(gfcrequest_t *gfr){

    struct addrinfo hints, *servinfo, *p;

    int sockfd, rv, totalBytesRcvd;
    char req_header[HEADER_LEN];

    int bytesRcvd;

    int header_len;

    char s[INET6_ADDRSTRLEN];

    memset(&hints, 0, sizeof(hints));
    memset(req_header,0,sizeof(req_header));

    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE; //use my IP

    if ((rv = getaddrinfo(NULL, gfr->cport, &hints, &servinfo)) != 0)
    {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and connect to the first we can

    for(p = servinfo; p != NULL; p = p->ai_next)
    {
        if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
        {
            perror("client: socket");
            continue;
        }

        if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1)
        {
            close(sockfd);
            perror("client: connect");
            continue;
        }

        break;
    }

    if (p == NULL)
    {
        fprintf(stderr, "client: failed to connect\n");
        return 2;
    }

    //printf("connected...\n");

    inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof(s));

//Ahsan
   // printf("Before getFileRequestHeader...\n");
    header_len = getFileRequestHeader(req_header);

    //printf("Header Description:%s, Header Len: %u\n", req_header, header_len);

    if (send(sockfd, req_header, header_len, 0) != header_len)
        perror("send() sent a different number of bytes than expected");

    if ((bytesRcvd = recv(sockfd, gfr->filecontent, BUFFER_SIZE, 0)) <= 0)
        perror("recv() failed or connection closed prematurely");

    //printf("Header Received: %s\n", gfr->filecontent);

    gfr->filelen = getFileLen(gfr->filecontent, gfr);

    //printf("File Length: %d\n", gfr->filelen);

    /* Receive the same string back from the server */
    int req_no=1;
    gfr->tot_bytes = 0;

    while ( 1 )
    {
        printf("Request: %d ", req_no++);
        ssize_t nb = recv( sockfd, gfr->filecontent, BUFFER_SIZE, 0 );
        if ( nb == -1 ) err( "recv failed" );
        if ( nb == 0 ) {printf("zero bytes received...breaking");break;} /* got end-of-stream */

        gfr->fl_handler(gfr->filecontent, nb, gfr->fDesc);
        gfr->tot_bytes += nb;

        printf("Received Bytes: %zd Total Received Bytes: %d\n", nb, gfr->tot_bytes);
    }

    return 0;
}


/*
 * Returns the string associated with the input status
 */

char* gfc_strstatus(gfstatus_t status)
{
    return stringFromError(status);
}


gfstatus_t gfc_get_status(gfcrequest_t *gfr){
    return gfr->ret_status;
}

size_t gfc_get_filelen(gfcrequest_t *gfr)
{
    return gfr->filelen;
}

size_t gfc_get_bytesreceived(gfcrequest_t *gfr)
{

    return gfr->tot_bytes;

}

void gfc_cleanup(gfcrequest_t *gfr)
{
    free(gfr);
    gfr=NULL;
}

void gfc_global_init()
{
;
//  pthread_mutex_lock(&counter_mutex);
//
//  gfc = (gfcrequest_t*)malloc(1* sizeof(gfcrequest_t));
//
//  pthread_mutex_unlock(&counter_mutex);


}

void gfc_global_cleanup()
{
;
//    pthread_mutex_lock(&counter_mutex);
//
//    free(gfc);
//
//    pthread_mutex_unlock(&counter_mutex);

}

//服务器代码

struct gfcontext_t
{
    int sockfd;
    int clntSock;
};

struct gfserver_t
{
    char port[12];
    unsigned short max_npending;
    char fpath[256];
    ssize_t (*fp_handler)(gfcontext_t *ctx, char *, void*);
    int *handler_arg;
};


/*Variable decalation*/


static gfserver_t *gfserv;

static gfcontext_t *gfcontext;

int isEndMarker(char *iheader, int start)
{
    char *marker = "\\r\\n\\r\\n";
    int i = 0; int ind=0;


    while (ind <= 7)
    {
        if (iheader[start++] == marker[ind++])
        {
            //printf("Header Char:%c Marker:%c\n", iheader[start], marker[ind]);
            //start++;
            continue;
        }

        return 0;
    }
    //printf("Its a marker!!!\n");
    return 1;

}

int parseHeader(char *iheader, gfserver_t *gfs, int hlen)
{
//"GETFILEGET/courses/ud923/filecorpus/road.jpg\r\n\r\n"

    char scheme[8];
    char method[4];
//    char path[256];
    int pathindex = 0;
    char end_marker[12];

    int end = 0;
    int fexit=0;

    sscanf(iheader, "%7s%3s", scheme, method);

    int i=10;
        if (iheader[i] == '/')
        {
//            printf("Path has started...\n");
            if (!strcmp(scheme, "GETFILE") && !strcmp(method, "GET"))
            {

                while(1)
                {

                    if (iheader[i] == '\\')
                    {
                        end = isEndMarker(iheader, i);
                    }

                    if (end)
                        break;

                    gfs->fpath[pathindex++] = iheader[i++];

                }

                gfs->fpath[pathindex] = '\0';
            }
        }


    printf("Scheme: %s Method:%s Path:%s\n", scheme, method, gfs->fpath);

    return 0;
}


void *get_in_addr(struct sockaddr *sa)
{

    if (sa->sa_family == AF_INET)
    {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}



ssize_t gfs_sendheader(gfcontext_t *ctx, gfstatus_t stat, size_t file_len)
{

    char resp_header[MAX_REQUEST_LEN];
    char end_marker[12] = "\\r\\n\\r\\n";

    sprintf(resp_header, "GETFILE%d%zd%s",stat, file_len, end_marker);

    printf("Response: %s\n", resp_header);

    if (send(ctx->clntSock, resp_header, MAX_REQUEST_LEN, 0) != MAX_REQUEST_LEN)
        perror("send() failed");

    return 0;
}

ssize_t gfs_send(gfcontext_t *ctx, void *data, size_t len)
{
    size_t total = 0;
    size_t bytesLeft = len;
    size_t n;

    int debug_req=1;

    while (total < len)
    {
         n = send(ctx->clntSock, data+total, bytesLeft, 0);
         if (n == -1) { printf("Nothing to send...\n"); break; }

         fprintf(stderr, "Tries: %d Bytes Sent: %zu\n", debug_req++, n);

         total += n;
         bytesLeft -= n;
    }

   // if ( shutdown( ctx->clntSock, SHUT_WR ) == -1 ) err( "socket shutdown failed" );

    return total;
}

void gfs_abort(gfcontext_t *ctx){

    close(ctx->clntSock);
    close(ctx->sockfd);
    free(ctx);
    free(gfserv);

    perror("aborting...");

    exit(1);
}

gfserver_t* gfserver_create()
{

    gfserv = (gfserver_t*) malloc(1 * sizeof(gfserver_t));
    gfcontext = (gfcontext_t*) malloc(1*sizeof(gfcontext_t));

    return gfserv;
}

void gfserver_set_port(gfserver_t *gfs, unsigned short port)
{
    //set port number in gfs structure
    snprintf(gfs->port,12, "%u",port);
}

void gfserver_set_maxpending(gfserver_t *gfs, int max_npending)
{
    //set maxpending connections
    gfs->max_npending = max_npending;
}

void gfserver_set_handler(gfserver_t *gfs, ssize_t (*handler)(gfcontext_t *, char *, void*))
{
    gfs->fp_handler = handler;
}

void gfserver_set_handlerarg(gfserver_t *gfs, void* arg)
{
    gfs->handler_arg = (int *)arg;
}

void gfserver_serve(gfserver_t *gfs)
{

    struct addrinfo hints, *servinfo, *p;
    struct sockaddr_storage clntAddr; //connectors address information
    socklen_t clntSize;
    int yes = 1;
    char s[INET6_ADDRSTRLEN];
    int rv;

    int rcvMsg = 0;
    char recvBuff[MAX_REQUEST_LEN];
    char *req_path;

    memset(&hints, 0, sizeof(hints));

    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM; //using stream socket instead of datagrams
    hints.ai_flags = AI_PASSIVE; //use my IP

    if ((rv = getaddrinfo(NULL, gfs->port, &hints, &servinfo)) != 0)
    {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and bind to the first we can
    for(p = servinfo; p != NULL; p = p->ai_next)
    {

        if ((gfcontext->sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
        {
            perror("server: socket");
            continue;
        }

        //get rid of 'address already in use' error.
        if (setsockopt(gfcontext->sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
        {
            perror("setsockopt");
            exit(1);
        }

        if (bind(gfcontext->sockfd, p->ai_addr, p->ai_addrlen) == -1)
        {
            close(gfcontext->sockfd);
            perror("server: bind");
            continue;
        }

        break;
    }

    if (p == NULL)
    {
        fprintf(stderr, "server: failed to bind.\n");
        return 2;
    }

    freeaddrinfo(servinfo); // no need of servinfo structure anymore

    if (listen(gfcontext->sockfd, gfs->max_npending) == -1)
    {
        perror("listen");
        exit(1);
    }


    //printf("server: waiting for connetions...\n");

    while(1)
    {
        clntSize = sizeof(clntAddr);
        gfcontext->clntSock = accept(gfcontext->sockfd, (struct sockaddr *)&clntAddr, &clntSize);

        if (gfcontext->clntSock == -1)
        {
            perror("accept");
            continue;
        }

        inet_ntop(clntAddr.ss_family, get_in_addr((struct sockaddr *)&clntAddr), s, sizeof(s));

        //printf("server: got connection from %s\n", s);

        if (!fork())
        { // this is the child process
            if ((rcvMsg = recv(gfcontext->clntSock, recvBuff, MAX_REQUEST_LEN, 0)) < 0)
            {
                perror("recv() failed");
                exit(1);
            }

            /*Still to parse received request...*/
            //printf("Recd Header: %s, Recd %d bytes\n",recvBuff, rcvMsg);

            /*Parse the received header...*/

            int len = parseHeader(recvBuff, gfs, rcvMsg);

            //printf("Requested Path: %s\n", gfs->fpath);

            if (gfs->fp_handler(gfcontext, gfs->fpath, NULL) < 0)
            {
                printf("some problem...\n");

            }

            if ( shutdown( gfcontext->clntSock, SHUT_WR ) == -1 ) err( "socket shutdown failed" );
            //close(gfcontext->clntSock);
        }

    }

}

//Server gf_send is being called from following function handler function:

Handler:
ssize_t handler_get(gfcontext_t *ctx, char *path, void* arg){
    int fildes;
    size_t file_len, bytes_transferred;
    ssize_t read_len, write_len;
    char buffer[BUFFER_SIZE];

    printf("Path: %s\n", path);

    if( 0 > (fildes = content_get(path)))
        return gfs_sendheader(ctx, GF_FILE_NOT_FOUND, 0);

    /* Calculating the file size */
    file_len = lseek(fildes, 0, SEEK_END);

    gfs_sendheader(ctx, GF_OK, file_len);

    /* Sending the file contents chunk by chunk. */
    int req=1;
    bytes_transferred = 0;
    while(bytes_transferred < file_len){
        read_len = pread(fildes, buffer, BUFFER_SIZE, bytes_transferred);
        if (read_len <= 0){
            fprintf(stderr, "handle_with_file read error, %zd, %zu, %zu", read_len, bytes_transferred, file_len );
            gfs_abort(ctx);
            return -1;
        }

        printf("Request No: %d ", req++);
        write_len = gfs_send(ctx, buffer, read_len);
        if (write_len != read_len){
            fprintf(stderr, "handle_with_file write error");
            gfs_abort(ctx);
            return -1;
        }

        bytes_transferred += write_len;
    }

    printf("Total Bytes sent to client: %zu\n", bytes_transferred);

    return bytes_transferred;
}

3 个答案:

答案 0 :(得分:5)

你没有指定,所以我假设你在这里使用TCP(发送/接收语义与UDP不同),

您遇到一种非常常见的误解,即TCP套接字一端的发送对应于另一端发送的字节数的一次接收。 这是错误的。

实际上,TCP套接字是双向字节流,没有消息概念。一次写入可以对应于另一端的许多读取,反之亦然。将其视为一条流。

您需要保留从发送和接收系统调用返回的发送和接收的字节数。

让对方了解您要发送的数据量也很重要,因此它会知道图像何时完全传输。这是应用程序级协议的工作,您必须提出或使用现有协议。

编辑0:

以下是在客户端和服务器之间设置任何有意义的协议之前所需的内容。 首先是发送代码:

size_t total = 0;

while ( total != len ) {
    ssize_t nb = send( s, data + total, len - total, 0 );
    if ( nb == -1 ) err( "send failed" );
    total += nb;
}
if ( shutdown( s, SHUT_WR ) == -1 ) err( "socket shutdown failed" );
/* also need to close client socket, see below */

接收代码:

char buffer[BUFFER_SIZE]; /* somewhere, might be static */
size_t total = 0; /* everything received */
while ( 1 ) {
    ssize_t nb = recv( s, buffer, BUFFER_SIZE, 0 );
    if ( nb == -1 ) err( "recv failed" );
    if ( nb == 0 ) break; /* got end-of-stream */
    if ( write( file_fd, buffer, nb ) == -1 ) err( "file write failed" );
    total += nb;
}
/* send an ack here */
if ( close( s ) == -1 ) err( "socket close failed" );
if ( close( file_fd )) err( "file close failed" );
printf( "received and saved total of %zu bytes\n", total );

那么您的应用程序级协议可能就像服务器发送一样简单,比如,在接受新的客户端连接后立即将64位文件长度(您需要确定要使用的TangoXYZij),然后发送那么多个字节到客户端并关闭在套接字上的写入,等待客户端确认成功接收数据。这可能是相同的数字,或者只是一个字节 - 由您决定,然后最终关闭套接字。这样,客户端就会预先知道预期的字节数,并且服务器知道传输成功。

使这个简单版本工作后,您可以将其扩展为允许每个连接进行多个文件传输,和/或使用endianness / select(2)进入IO多路复用。

希望这有帮助。

答案 1 :(得分:1)

首先:recv()并不总是接收send()发送的块中的数据,事实上它很少 - 因为缓冲(例如,你发送256字节)接收两个128字节的缓冲区)

现在你的错误:我问题是你没有调用带有FD_SET的select()来将你的套接字重置为“准备接收”状态,然后再调用recv()a第二次。

如果你想挖掘它,我在my site上有一个公吨的winsock / c-socket代码。

如果我可以扩展这个答案,请告诉我,我很乐意提供额外帮助!

答案 2 :(得分:0)

input:checked + label {
   background:yellow;
}

常见问题。您假设读取填充缓冲区。它应该是:

gfr->fl_handler(gfr->filecontent, BUFFER_SIZE, gfr->fDesc);