简体   繁体   English

第二次recv调用不接收数据,暂停C中的执行

[英]Second recv call doesn't receive data, halts the execution in C

This is my client program that requests files from the server: 这是我的客户端程序,它从服务器请求文件:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define SERVER_PORT 5959
#define MAX_LINE 512

void setstring(char *str){
    str[MAX_LINE-1]='\0';
}

int main(int argc, char * argv[]){
    FILE *fp;
    struct hostent *hp;
    struct sockaddr_in sin;
    char *host;
    char filename[MAX_LINE],buf[MAX_LINE],reply[MAX_LINE],rec_line[MAX_LINE];
    int s;
    char msg[MAX_LINE];
    int len,new_len,rec_file;
    if (argc==2) {
        host = argv[1];
    }
    else {
        fprintf(stderr, "usage: simplex-talk host\n");
        exit(1);
    }

    /* translate host name into peer's IP address */
    hp = gethostbyname(host);
    if (!hp) {
        fprintf(stderr, "simplex-talk: unknown host: %s\n", host);
        exit(1);
    }
    else
        printf("Client's remote host: %s\n", argv[1]);

    /* build address data structure */  
    bzero((char *)&sin, sizeof(sin));
    sin.sin_family = AF_INET;
    bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
    sin.sin_port = htons(SERVER_PORT);

    /* active open */
    if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
        perror("simplex-talk: socket");
        exit(1);
    }
    else
        printf("Client created socket.\n");

    int send_file_name,rec_msg;
    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
    {
        perror("simplex-talk: connect");
        close(s);
        exit(1);
    }
    else{
        printf("Client connected.\n");
        /* main loop: get and send lines of text */
    printf("Hello from server\n");
    while(!(strcmp(reply,"bye")==0)){
            printf("Enter the file name:\n");
            scanf("%s",filename);
            setstring(filename);
            send_file_name=send(s,filename,strlen(filename)+1,0);
            if(send_file_name<0)
                fputs("Error sending filename",stdout);
            rec_msg=recv(s,msg,sizeof(msg),0);
            if(strcmp(msg,"File not found")==0)
                printf("File not found\n");
            else{
                printf("%s\n",msg);
                fp=fopen(filename,"w");
                printf("CP1\n");
                if(rec_file=recv(s,rec_line,sizeof(rec_line),0)>0){
                    printf("CP2");
                    printf("String recieved:%s\n",rec_line);
                    if(len=fwrite(rec_line,1,rec_file+1,fp)>0)
                        printf("Recieved file\n");
                    else
                        printf("Error writing to file\n");
                }
                else
                    printf("Not recieved\n");
            }
            printf("Enter 'bye' to terminate requesting files\n");
            scanf("%s",reply);
        }
    }
    return 0;
}

This is my server program that accepts request for files from the client: 这是我的服务器程序,它接受来自客户端的文件请求:

 #include <stdio.h>
 #include <stdlib.h> 
 #include <string.h>
 #include <strings.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netdb.h>

 #define SERVER_PORT 5959
 #define MAX_PENDING 5
 #define MAX_LINE 256

 void setstring(char* str){
    str[MAX_LINE-1]='\0';
 }

 int main(){
    FILE *fp;
    struct sockaddr_in sin;
    char buf[MAX_LINE],msg[MAX_LINE],*rec_line;
    int len;
    int s, new_s,count;
    char str[INET_ADDRSTRLEN];
    int error_file,send_msg,read_line,send_file;

    bzero((char *)&sin, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr("0.0.0.0");
    sin.sin_port = htons(SERVER_PORT);

    /* setup passive open */
    if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
        perror("simplex-talk: socket");
        exit(1);
    }

    inet_ntop(AF_INET, &(sin.sin_addr), str, INET_ADDRSTRLEN);
    printf("Server is using address %s and port %d.\n", str, SERVER_PORT);

    if ((bind(s, (struct sockaddr *)&sin, sizeof(sin))) < 0) {
        perror("simplex-talk: bind");
        exit(1);
    }
    else
        printf("Server bind done.\n");

    listen(s, MAX_PENDING);
    /* wait for connection, then receive and print text */
    while(1) {
        if ((new_s = accept(s, (struct sockaddr *)&sin, &len)) < 0) {
            perror("simplex-talk: accept");
            exit(1);
        }

        printf("Server Listening.\n");
        printf("Greetings\n");
        int rec_file_name=recv(new_s,buf,sizeof(buf),0);
        if(rec_file_name>0)
            printf("File requested:%s\n",buf);
            fp=fopen(buf,"r");
            if(fp==NULL)
            {
                fputs("File not found\n",stdout);
                strcpy(buf,"File not found");
                if(error_file=send(new_s,buf,strlen(buf)+1,0)>0)
                    fputs("Successfully send error message to client\n",stdout);
            }
            else{
                bzero(buf,MAX_LINE);
                printf("File found :) \n");
                strcpy(buf,"OK");
                if(send_msg=send(new_s,buf,strlen(buf)+1,0)>0)
                    fputs("File found message sent to client\n",stdout);

                fseek(fp,0,SEEK_END);
                int file_size=ftell(fp);
                fseek(fp,0,SEEK_SET);
                printf("File size:%d\n",file_size);
                rec_line=(char *)malloc(sizeof(char)*(file_size));
                read_line=fread(rec_line,1,file_size+1,fp);
                printf("File read: %s\n",rec_line);

                if(send_file=send(new_s,rec_line,strlen(rec_line)+1,0)>0)
                    printf("File string sent to client\n");
            }
        }

        close(new_s);   
    } 

The problem is that in the client, my second recv() call, where it is supposed to receive the contents of a file, shows nothing. 问题在于,在客户端中,我的第二个recv()调用应该接收文件的内容,但没有显示任何内容。 The programs halts at that point, but the server programs displays that it has sent the file contents. 程序此时停止,但是服务器程序显示它已发送文件内容。 The client doesn't receive it. 客户没有收到。

The basic problem is that you're not checking the return values to see how much data you actually sent and received. 基本问题是您没有检查返回值以查看实际发送和接收的数据量。 So when the client calls: 因此,当客户致电时:

rec_msg=recv(s,msg,sizeof(msg),0);

it will receive up to sizeof(msg) (512) bytes, which is probably both the OK message the server is sending AND the file contents (after the NUL). 它最多将收到sizeof(msg) (512)字节,这可能是服务器正在发送的OK消息以及文​​件内容(在NUL之后)。 Which means when it does a second recv call to get the contents, it blocks, because it already read the contents in the first call and there's no more data waiting in the receive buffer. 这意味着当它进行第二次recv调用以获取内容时,它会阻塞,因为它已经在第一个调用中读取了内容,并且接收缓冲区中没有等待数据。

Your error-checking is haphazard, and consequently you're certainly missing a problem that occurs before the behavior you're observing. 错误检查是偶然的,因此,您肯定会漏掉在观察到的行为之前发生的问题。 I recommend you follow RW Steven's idiom: 我建议您遵循RW史蒂文的成语:

int n;
if( (n = recv(new_s, buf, sizeof(buf), 0)) < 0 ) {
    err(EXIT_FAILURE, "recv %d", __LINE__);
}

Test every function call, and handle every error. 测试每个函数调用,并处理每个错误。 For simple programs, just call err (3) on error. 对于简单的程序,只需在出错时调用err (3)。 Do that consistently, and the program's behavior will be much less mysterious (if still occasionally surprising). 始终如一地执行此操作,程序的行为就不会那么神秘了(如果偶尔仍然令人惊讶)。 And don't be afraid of the spacebar! 并且不要害怕空格键! It's easy to hit, and easier still to read. 它很容易命中,更容易阅读。

My other bit of advice, if I may, concerns 如果可以的话,我还有其他建议

int send_file_name,rec_msg;

Names like that are confusing. 这样的名字令人困惑。 A name is almost never an integer. 名称几乎永远不会是整数。 For I/O sizes, just use one simple name like n , len , or size . 对于I / O大小,只需使用一个简单的名称,例如nlensize Even if you don't care for yourself, you want to care before publishing your question in an open forum. 即使您不关心自己,也要在开放的论坛中发布问题之前先关心自己。 Otherwise, when people see 否则,当人们看到

send_file_name=send(s,filename,strlen(filename)+1,0);

they may think send is some function other than send (2), or that the person asking the question was careless. 他们可能认为sendsend (2)以外的其他功能,或者询问该问题的人很粗心。

The main problem I see is that neither the client nor the server are handling socket I/O correctly in general. 我看到的主要问题是,客户端和服务器通常都无法正确处理套接字I / O。 They are not handling the cases where reads and writes transfer fewer bytes then requested, you need to loop the I/O. 他们没有处理读写操作传输的字节数少于请求的字节数的情况,您需要循环I / O。 And the client is reading too many bytes from the server anyway, which is why your second recv() is blocking. 而且客户端无论如何都从服务器读取了太多字节,这就是第二个recv()阻塞的原因。 And you are relying on a disconnect to indicate the end of the file has been reached, but that does not allow the client to do adequate error checking to know if the full file was actually received or not. 而且您依赖于断开连接来指示已到达文件末尾,但这不允许客户端进行适当的错误检查以了解是否实际收到了完整文件。

Also, when sending the content of a file, the server is attempting to read the entire file into memory (bad!), not doing adequate error checking on the file I/O, and it is treating the file content as text instead of as binary (don't use strlen() on binary data!). 另外,在发送文件内容时,服务器尝试将整个文件读入内存(不好!),未对文件I / O进行足够的错误检查,并且将文件内容视为文本,而不是二进制文件(不要对二进制数据使用strlen() !)。

Try something more like this instead: 尝试类似这样的方法:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define SERVER_PORT 5959
#define MAX_LINE 512

int sendstring(int sock, const char *str) {
    if (!str) str = "";
    int len = strlen(str) + 1;

    do {
        int ret = send(sock, str, len, 0);
        if (ret <= 0) return -1;
        str += ret;
        len -= ret;
    }
    while (len > 0);

    return 0;
}

int readbuf(int sock, void *buf, int buflen) {
    char *pbuf = (char*) buf;

    while (buflen > 0) {
        int len = recv(sock, pbuf, buflen, 0);
        if (len <= 0) return -1;
        pbuf += len;
        buflen -= len;
    }

    return 0;
}

int readstring(int sock, char *str, int maxlen) {
    while (maxlen > 0) {
        if (recv(sock, str, 1, 0) <= 0) return -1;
        if (*str == '\0') return 0;
        ++str;
        --maxlen;
    }
    return -2;
}

int readfile(int sock, int fd) {
    int filesize;
    char buf[MAX_LINE];

    if (readbuf(sock, &filesize, sizeof(filesize)) < 0) return -1;
    filesize = ntohl(filesize);

    while (filesize > 0) {
        int len = readbuf(sock, buf, min(sizeof(buf), filesize));
        if (len < 0) return -1;
        if (fwrite(buf, len, 1, fp) != 1) return -2;
        filesize -= len;
    }

    return 0;
}

int main(int argc, char * argv[]) {
    char filename[MAX_LINE], reply[MAX_LINE];

    if (argc != 2) {
        fprintf(stderr, "usage: simplex-talk host\n");
        exit(1);
    }

    char *host = argv[1];

    /* translate host name into peer's IP address */
    struct hostent *hp = gethostbyname(host);
    if (!hp) {
        fprintf(stderr, "simplex-talk: unknown host: %s\n", host);
        exit(1);
    }

    if (hp->h_addrtype != AF_INET) {
        fprintf(stderr, "simplex-talk: unsupported address type %d for host: %s\n", hp->h_addrtype, host);
        exit(1);
    }

    /* build address data structure */  
    struct sockaddr_in sin;
    bzero((char *)&sin, sizeof(sin));
    sin.sin_family = AF_INET;
    bcopy(hp->h_addr, &sin.sin_addr, hp->h_length);
    sin.sin_port = htons(SERVER_PORT);

    printf("Host's remote IP: %s\n", inet_ntoa(&sin.sin_addr));

    /* active open */
    int s = socket(PF_INET, SOCK_STREAM, 0);
    if (s < 0) {
        perror("simplex-talk: socket");
        exit(1);
    }

    printf("Client created socket.\n");

    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
    {
        perror("simplex-talk: connect");
        close(s);
        exit(1);
    }

    printf("Client connected.\n");

    /* main loop: get and send lines of text */
    do {
        printf("Enter the file name ('bye' to quit):\n");

        if (scanf("%512s", filename) != 1) {
            printf("Error reading filename\n");
            break;
        }

        if (strcmp(filename, "bye") == 0) {
            sendstring(s, "bye");
            break;
        }

        if (sendstring(s, filename) < 0) {
            printf("Error sending filename\n");
            break;
        }

        if (readstring(s, reply, sizeof(reply)) < 0) {
            printf("Error reading reply\n");
            break;
        }

        if (strcmp(reply, "OK") != 0) {
            printf("%s\n", reply);
            if (strcmp(reply, "bye") == 0) break;
            continue;
        }

        FILE *fp = fopen(filename, "wb");
        if (!fp) {
            printf("Error opening file\n");
            break;
        }

        printf("Receiving file\n");

        int ret = readfile(s, fd);
        fclose(fp);

        if (ret < 0) {
            if (ret == -2)
                printf("Error writing file\n");
            else
                printf("Error reading file\n");

            break;
        }

        printf("Received file\n");
    }
    while (1);

    close(s);
    return 0;
}

#include <stdio.h>
#include <stdlib.h> 
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define SERVER_PORT 5959
#define MAX_PENDING 5
#define MAX_LINE 512

int sendbuf(int sock, void *buf, int buflen) {
    char *pbuf = (char*) buf;

    while (len > 0) {
        int len = send(sock, pbuf, buflen, 0);
        if (len <= 0) return -1;
        pbuf += len;
        buflen -= len;
    }

    return 0;
}

int sendstring(int sock, const char *str) {
    if (!str) str = "";
    return sendbuf(sock, str, strlen(str) + 1);
}

int sendfile(int sock, int fd) {
    char buf[MAX_LINE];

    struct stat s;
    if (fstat(fd, &s) < 0) return -2;

    int pos = ftell(fp);
    if (pos == -1) return -2;

    int file_size = s.st_size - pos;
    int tmp_file_size = htonl(file_size);
    if (sendbuf(sock, &tmp_file_size, sizeof(tmp_file_size)) < 0) return -1;

    while (file_size > 0) {
        int len = fread(buf, 1, min(sizeof(buf), file_size), fp);
        if (len < 1) return -2;
        if (sendbuf(sock, buf, len) < 0) return -1;
        file_size -= len;
    }

    return 0;
}

int readstring(int sock, char *str, int maxlen) {
    while (maxlen > 0) {
        if (recv(sock, str, 1, 0) <= 0) return -1;
        if (*str == '\0') return 0;
        ++str;
        --maxlen;
    }
    return -2;
}

int main() {
    char msg[MAX_LINE];

    struct sockaddr_in sin;
    bzero((char *)&sin, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    sin.sin_port = htons(SERVER_PORT);

    /* setup passive open */
    int s = socket(PF_INET, SOCK_STREAM, 0);
    if (s < 0) {
        perror("simplex-talk: socket");
        exit(1);
    }

    printf("Server is using address %s and port %d.\n", inet_ntoa(&(sin.sin_addr)), SERVER_PORT);

    if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
        perror("simplex-talk: bind");
        close(s);
        exit(1);
    }

    printf("Server bind done.\n");

    if (listen(s, MAX_PENDING) < 0) {
        perror("simplex-talk: listen");
        close(s);
        exit(1);
    }

    printf("Server Listening.\n");

    /* wait for connection, then receive and print text */
    do {
        int len = sizeof(sin);
        int cli_s = accept(s, (struct sockaddr *)&sin, &len);
        if (cli_s < 0) {
            perror("simplex-talk: accept");
            close(s);
            exit(1);
        }

        printf("Client connected\n");

        do {
            if (readstring(cli_s, msg, sizeof(msg)) < 0) {
                printf("Error reading request\n");
                break;
            }

            if (strcmp(msg, "bye") == 0) break;

            printf("File requested: %s\n", msg);

            FILE *fp = fopen(msg, "rb");
            if (!fp)
            {
                printf("Cannot open file\n");
                if (sendstring(cli_s, "Cannot open file") < 0) {
                    printf("Error sending reply\n");
                    break;
                }
                continue;
            }

            printf("File found :) \n");

            if (sendstring(cli_s, "OK") < 0) {
                printf("Error sending reply\n");
                fclose(fp);
                break;
            }

            ret = sendfile(cli_s, fp);
            fclose(fp);

            if (ret < 0) {
                printf("Error sending file\n");
                break;
            }

            printf("File sent to client\n");
        }
        while (1);

        close(cli_s);   
    } 
    while (1);

    close(s);
    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM