第二次recv调用没有接收数据,停止在C中执行

时间:2016-08-23 20:26:52

标签: c linux sockets send recv

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

#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()调用,它应该接收文件的内容,什么都不显示。程序暂停,但服务器程序显示它已发送文件内容。客户没有收到它。

2 个答案:

答案 0 :(得分:1)

您的错误检查是随意的,因此您肯定错过了在您观察的行为之前发生的问题。我建议你遵循RW Steven的习语:

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

测试每个函数调用,并处理每个错误。对于简单程序,只需在出错时调用错误(3)。一贯地做到这一点,程序的行为将不那么神秘(如果仍然偶尔令人惊讶)。并且不要害怕空格键!它很容易被击中,而且更容易阅读。

如果可以的话,我的其他建议是关注

int send_file_name,rec_msg;

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

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

他们可能会认为send发送(2)以外的某种功能,或者提出问题的人是粗心的。

答案 1 :(得分:1)

我看到的主要问题是客户端和服务器一般都没有正确处理套接字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;
}