C TCP套接字,带文件发送的echo服务器,在发送文件后挂起

时间:2016-09-22 09:26:42

标签: c file sockets tcp server

我想编写一个简单的TCP echo服务器应用程序。我设法做了回声部分,但在客户端和服务器之间发送文件时遇到了一些问题。这个想法很简单:尽管发送普通消息,客户端可以向服务器发送一个特殊命令(\ SENDFILE filename.txt),并且在收到这样的命令后,服务器应该向客户端询问该文件,并从客户端获取该文件。 (此外,我想从一个客户端获取一个文件,然后将其发送到另一个客户端)。

我认为"协议"这里很简单,但是在客户端输入\ SENDFILE之后,客户端挂断,并且不会从服务器接收任何进一步的消息。此外(服务器和客户端在不同的目录中)在服务器端,只有来自客户端的空文件,内部没有内容。

任何想法在这里可能出错?

client.c

#include<stdio.h> //printf
#include<string.h>    //
#include <sys/stat.h>
#include<sys/socket.h>    //socket
#include<arpa/inet.h> //inet_addr
#include <fcntl.h>
#define SERVER_PORT     9034
#define BUFF_SIZE       2000

int sendall(int s, char *buf, int len)
{
    int total = 0;
    int bytesleft = len;
    int n;

    while(total < len)
    {
        n = send(s, buf+total, bytesleft, 0);
        if (n == -1)
            break;
        total += n;
        bytesleft -= n;
    }

    return n==-1?-1:0;
}

void SendMsgToSender(char *msg, int connfd)
{
    write(connfd, msg, strlen(msg));
    memset(msg, 0, BUFF_SIZE);
}

int main(int argc , char *argv[])
{
    int sock;
    struct sockaddr_in server;
    char bufferOUT[BUFF_SIZE] , bufferIN[BUFF_SIZE];
    struct stat file_stat;

    memset(bufferOUT, 0, BUFF_SIZE);
    memset(bufferIN, 0, BUFF_SIZE);

    //Create socket
    sock = socket(AF_INET , SOCK_STREAM , 0);
    if (sock == -1)
    {
        printf("Could not create socket");
    }
    //  puts("Socket created");

    server.sin_addr.s_addr = inet_addr("127.0.0.1");
    server.sin_family = AF_INET;
    server.sin_port = htons( SERVER_PORT );

    //Connect to remote server
    if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0)
    {
        perror("Connect failed. Error");
        return 1;
    }

    // puts("Connected\n");
    int read_size = 10;

    //keep communicating with server
    while(1)
    {
        printf("> ");
        fgets(bufferOUT, BUFF_SIZE, stdin);

        //Send some data
        if( send(sock , bufferOUT , BUFF_SIZE , 0) < 0)
        {
            perror("Send failed");
            return 1;
        }

        //Receive a reply from the server
        if( (read_size = recv(sock , bufferIN , BUFF_SIZE , 0)) < 0)
        {
            perror("Recv failed");
            break;
        }

        if(read_size == 0)
            break;

        if(bufferIN[0] == '\\')
        {
            char tmp[BUFF_SIZE], filename[BUFF_SIZE], *param;
            memset(filename, BUFF_SIZE, 0);
            strcpy(tmp, bufferIN);

            param = strtok(tmp, " ");
            if(param != NULL)
            {
                if(!strcmp(param, "\\GIVEMEFILE"))
                {
                    param = strtok(NULL, " ");
                    if(param != NULL)
                    {
                        strcpy(filename, param);
                        FILE * fp;
                        int nBytes;
                        char buffer[BUFF_SIZE], *s;
                        memset(buffer, 0, BUFF_SIZE);

                        fp = fopen(filename, "r");
                        if(fp == NULL)
                        {
                            perror("fopen");
                            fflush(stdout);
                            break;
                        }

                        int remain_data = file_stat.st_size;

                        do
                        {
                            s = fgets(buffer, BUFF_SIZE, fp);
                            if(s != NULL && buffer[0] != EOF)
                            {
                                nBytes = sendall(sock, buffer, BUFF_SIZE);
                                remain_data -= nBytes;
                            }
                            else
                                break;
                        }
                        while((s != NULL) && (nBytes > 0) && (remain_data > 0));
                        fclose(fp);

                        memset(bufferOUT, 0, BUFF_SIZE);
                        memset(bufferIN, 0, BUFF_SIZE);

                        continue;
                    }
                }
            }
        }
        else
        {
            printf("%s\n", bufferIN);
            fflush(stdout);
        }

        memset(bufferOUT, 0, BUFF_SIZE);
        memset(bufferIN, 0, BUFF_SIZE);
    }

    close(sock);
    return 0;
}

server.c

    #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <fcntl.h>

#define SERVER_PORT     9034
#define BUFF_SIZE       2000

void StripNewline(char *s)
{
    while(*s != '\0')
    {
        if(*s == '\r' || *s == '\n')
        {
            *s = '\0';
        }
        s++;
    }
}

void SendMsgToSender(char *msg, int connfd)
{
    write(connfd, msg, strlen(msg));
    memset(msg, 0, BUFF_SIZE);
}

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

int GetFileFromClient(int connfd, char *filename)
{
    FILE * fp = NULL;
    int bytes;
    char buffer[BUFF_SIZE];
    memset(buffer, 0, BUFF_SIZE);

    fp = fopen(filename, "w");
    if(fp == NULL)
        return 0;

    memset(buffer, 0, BUFF_SIZE);
    sprintf(buffer, "\\GIVEMEFILE %s \r\n", filename);
    SendMsgToSender(buffer, connfd);

    while(1)
    {
        memset(buffer ,0 , BUFF_SIZE);
        if((bytes =  recv(connfd , buffer , BUFF_SIZE , 0) ) <= 0)
            return 0;
        else
            fprintf(fp, "%s\n", buffer);
    }

    fclose(fp);
    sleep(1);

    memset(buffer, 0, BUFF_SIZE);
    sprintf(buffer, "\r\n");
    SendMsgToSender(buffer, connfd);

    return 1;
}

int main(void)
{
    fd_set master;
    fd_set read_fds;
    int fdmax;

    int listener;
    int client_sock;
    struct sockaddr_storage remoteaddr;
    socklen_t addrlen;

    char bufferIN[BUFF_SIZE], bufferOUT[BUFF_SIZE], tmp[BUFF_SIZE], *datetime;
    int nbytes;

    char remoteIP[INET6_ADDRSTRLEN];

    int yes=1;
    int i, j, rv;

    struct addrinfo hints, *ai, *p;

    FD_ZERO(&master);
    FD_ZERO(&read_fds);

    memset(bufferIN, 0, BUFF_SIZE);
    memset(bufferOUT, 0, BUFF_SIZE);
    memset(tmp, 0, BUFF_SIZE);

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;

    char port[16] = "9034";
    if (getaddrinfo(NULL, port, &hints, &ai) < 0)
    {
        fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
        exit(1);
    }

    for(p = ai; p != NULL; p = p->ai_next)
    {
        listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
        if (listener < 0)
        {
            continue;
        }

        setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));

        if (bind(listener, p->ai_addr, p->ai_addrlen) < 0)
            continue;

        break;
    }

    if (p == NULL)
        exit(2);

    freeaddrinfo(ai);

    if (listen(listener, 10) == -1)
    {
        perror("listen");
        exit(3);
    }

    FD_SET(listener, &master);
    fdmax = listener;

    printf("Server is running ...\n\n");

    for(;;)
    {
        read_fds = master;
        if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)
        {
            perror("select");
            exit(4);
        }

        for(i = 0; i <= fdmax; i++)
        {
            if (FD_ISSET(i, &read_fds))
            {
                if (i == listener)
                {
                    addrlen = sizeof remoteaddr;
                    client_sock = accept(listener,
                                         (struct sockaddr *)&remoteaddr,
                                         &addrlen);

                    if (client_sock == -1)
                    {
                        perror("accept");
                    }
                    else
                    {
                        FD_SET(client_sock, &master);
                        if (client_sock > fdmax)
                            fdmax = client_sock;
                    }
                }
                else
                {
                    if ((nbytes = recv(i, bufferIN, BUFF_SIZE, 0)) <= 0)
                    {
                        if (nbytes == 0)
                            close(i);

                        else if(nbytes == -1)
                        {
                            perror("recv");
                            fflush(stdout);
                        }

                        close(i);
                        FD_CLR(i, &master);
                    }
                    else
                    {
                        bufferIN[nbytes-1] = '\0';
                        StripNewline(bufferIN);
                        strcpy(tmp, bufferIN);

                        if(bufferIN[0] == '\\')
                        {
                            char *command, *param;
                            command = strtok(bufferIN, " ");

                            if(!strcmp(command, "\\QUIT"))
                            {
                                close(i);
                                FD_CLR(i, &master);
                                break;
                            }

                            else if(!strcmp(command, "\\SENDFILE"))
                            {
                                param = strtok(tmp, " ");
                                if(param != NULL)
                                {
                                    param = strtok(NULL, " ");
                                    if(param != NULL)
                                    {
                                        printf("Client is sending me a file '%s'...\n", param);
                                        GetFileFromClient(i, param);
                                    }
                                }
                            }
                            else
                            {
                                SendMsgToSender(bufferIN, i);
                            }

                            memset(bufferIN, 0, BUFF_SIZE);
                            memset(bufferOUT, 0, BUFF_SIZE);
                        }
                        else
                        {
                            SendMsgToSender(bufferIN, i);
                        }
                    }
                } // END handle data from client
            } // END got new incoming connection
        } // END looping through file descriptors
    } // END for(;;)

    memset(bufferIN, 0, BUFF_SIZE);
    memset(bufferOUT, 0, BUFF_SIZE);

    return 0;
}

2 个答案:

答案 0 :(得分:12)

strcpy(tmp, bufferIN);

这里假设读取的内容为空终止。

            param = strtok(tmp, " ");
            if(param != NULL)
            {
                if(!strcmp(param, "\\GIVEMEFILE"))

这里假设已收到整个邮件。

                strcpy(filename, param);

同上。

                        memset(buffer, 0, BUFF_SIZE);

毫无意义的。删除。

                        do
                        {
                            s = fgets(buffer, BUFF_SIZE, fp);

这里假设文件由行组成。

                            if(s != NULL && buffer[0] != EOF)

测试buffer[0] !=EOF毫无意义。如果你已经达到EOF,s将为空,假设文件由行组成,但没有任何关于一行说明它的第一个字符可能是什么,除了它不是&#39; ta行终结者。

                        memset(bufferOUT, 0, BUFF_SIZE);
                        memset(bufferIN, 0, BUFF_SIZE);

两者毫无意义。删除。

        memset(bufferOUT, 0, BUFF_SIZE);
        memset(bufferIN, 0, BUFF_SIZE);

同上。

void StripNewline(char *s)

这种方法看起来毫无意义。删除。

void SendMsgToSender(char *msg, int connfd)
{
    write(connfd, msg, strlen(msg));

在这里,您要向对等发送一个字符串,而没有尾随空,对等方正在strlen()处查找该字符串。好好考虑一下你的应用程序协议实际需要什么。

    memset(msg, 0, BUFF_SIZE);

毫无意义的。删除。

int GetFileFromClient(int connfd, char *filename)
{
    FILE * fp = NULL;
    int bytes;
    char buffer[BUFF_SIZE];
    memset(buffer, 0, BUFF_SIZE);

毫无意义的。删除。

    memset(buffer, 0, BUFF_SIZE);

同上。

    sprintf(buffer, "\\GIVEMEFILE %s \r\n", filename);
    SendMsgToSender(buffer, connfd);

    while(1)
    {
        memset(buffer ,0 , BUFF_SIZE);

毫无意义的。删除。

        if((bytes =  recv(connfd , buffer , BUFF_SIZE , 0) ) <= 0)
            return 0;

在这里,您需要区分(1)bytes == 0,这意味着对等方断开连接,以及(2)byte == -1,它表示错误,您需要 log ,通过errnostrerror()和朋友。

        else
            fprintf(fp, "%s\n", buffer);

更改为fprintf(fp, "%.*s\n", bytes, buffer)。您假设所有消息都由TCP以空值终止。他们不是。

    sleep(1);

毫无意义的。删除。

    memset(buffer, 0, BUFF_SIZE);

同上。

    sprintf(buffer, "\r\n");
    SendMsgToSender(buffer, connfd);

向对等点发送行终止符似乎完全没有意义。

    memset(bufferIN, 0, BUFF_SIZE);
    memset(bufferOUT, 0, BUFF_SIZE);
    memset(tmp, 0, BUFF_SIZE);

一切都毫无意义。删除。

        if (bind(listener, p->ai_addr, p->ai_addrlen) < 0)
            continue;

在这里,您需要打印错误消息而不是忽略条件。

        if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)

您还没有将侦听套接字置于非阻止模式。因此使用select()毫无意义。

                        bufferIN[nbytes-1] = '\0';
                        StripNewline(bufferIN);

为什么?

                        strcpy(tmp, bufferIN);

为什么呢?继续使用bufferIN会有什么问题?

                        if(bufferIN[0] == '\\')
                        {
                            char *command, *param;
                            command = strtok(bufferIN, " ");

这里再次假设您收到了一个完整的命令,并且尾随空格。

                            memset(bufferIN, 0, BUFF_SIZE);
                            memset(bufferOUT, 0, BUFF_SIZE);

两者毫无意义。去掉。这是一个突然的货物崇拜节目。 recv()返回一个长度。使用它。

    memset(bufferIN, 0, BUFF_SIZE);
    memset(bufferOUT, 0, BUFF_SIZE);

同上,在黑桃中。

基本上你有一个应用程序协议问题。具体来说,您没有拥有应用程序协议。只是一大堆无根据的假设。如果你想要一个尾随空值,(a)发送一个尾随空值,(b)循环读数,直到你收到它。您还假设正在发送的文件的内容,这是完全没有必要的。只需从文件中读取字节并将其发送到服务器即可。没有关于线路或线路终结器的假设。如果您通过同一连接发送多个文件,则需要在文件之前发送 size 文件,因此接收方将确切知道要读取多少字节并复制到该文件。

实质上,你需要完全重新考虑这一点。

答案 1 :(得分:1)

在client.c中,您必须在获取文件file_stat的大小之前初始化stat(filename, &file_stat); 由于此错误,remain_data将始终具有错误的值。

在Server.c中 由于EJP指出的while循环中的错误,您将覆盖客户端发送的文件。基本上是空的。 使用“r”选项打开客户端文件名。 在服务器中打开另一个文件并接收该文件的数据。 接收BUFF_SIZE内文件数据的小例子。您可以使用某些逻辑并将其扩展为更大的文件,就像在Client.c中完成的那样。

     fd = fopen(<new_file_path>, "w");
        while(1)
        {
            memset(buffer ,0 , BUFF_SIZE);
            if((bytes =  recv(connfd , buffer , BUFF_SIZE , 0) ) == BUFF_SIZE)
                break;
        }
        fprintf(fd, "%s\n", buffer);
fclose(fd);