繁体   English   中英

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

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

这是我的客户端程序,它从服务器请求文件:

#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;
}

这是我的服务器程序,它接受来自客户端的文件请求:

 #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);   
    } 

问题在于,在客户端中,我的第二个recv()调用应该接收文件的内容,但没有显示任何内容。 程序此时停止,但是服务器程序显示它已发送文件内容。 客户没有收到。

基本问题是您没有检查返回值以查看实际发送和接收的数据量。 因此,当客户致电时:

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

它最多将收到sizeof(msg) (512)字节,这可能是服务器正在发送的OK消息以及文​​件内容(在NUL之后)。 这意味着当它进行第二次recv调用以获取内容时,它会阻塞,因为它已经在第一个调用中读取了内容,并且接收缓冲区中没有等待数据。

错误检查是偶然的,因此,您肯定会漏掉在观察到的行为之前发生的问题。 我建议您遵循RW史蒂文的成语:

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

测试每个函数调用,并处理每个错误。 对于简单的程序,只需在出错时调用err (3)。 始终如一地执行此操作,程序的行为就不会那么神秘了(如果偶尔仍然令人惊讶)。 并且不要害怕空格键! 它很容易命中,更容易阅读。

如果可以的话,我还有其他建议

int send_file_name,rec_msg;

这样的名字令人困惑。 名称几乎永远不会是整数。 对于I / O大小,只需使用一个简单的名称,例如nlensize 即使您不关心自己,也要在开放的论坛中发布问题之前先关心自己。 否则,当人们看到

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

他们可能认为sendsend (2)以外的其他功能,或者询问该问题的人很粗心。

我看到的主要问题是,客户端和服务器通常都无法正确处理套接字I / O。 他们没有处理读写操作传输的字节数少于请求的字节数的情况,您需要循环I / O。 而且客户端无论如何都从服务器读取了太多字节,这就是第二个recv()阻塞的原因。 而且您依赖于断开连接来指示已到达文件末尾,但这不允许客户端进行适当的错误检查以了解是否实际收到了完整文件。

另外,在发送文件内容时,服务器尝试将整个文件读入内存(不好!),未对文件I / O进行足够的错误检查,并且将文件内容视为文本,而不是二进制文件(不要对二进制数据使用strlen() !)。

尝试类似这样的方法:

#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