簡體   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