c - C中通过消息队列发送文件出错?

标签 c linux windows message-queue

我为我的文件服务器(在 Linux 上运行)创建了一个消息队列,当我通过客户端(从 Windows 客户端)将文件上传到服务器时,一切似乎都很顺利。文件上传后,我在服务器端得到了所有这些模糊的符号。

我在客户端有如下代码

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stddef.h>
#include "mailbox.h"                                                            
#define MAXCLIENTS 5
#define PORTNR 5002
#define MAXUSERS 1024

/*
int inet_aton(const char *cp, struct in_addr *inp);
char *inet_ntoa(struct in_addr in);
void *memset(void *s, int c, size_t n);
int close(int fd);
 */
//Prototyping van de functies
void *childThread(void *ad);
void uploadFile(int nsockfd);
void downloadFile(int nsockfd);
void establishConnection();
int login(char *username, char *password);

int get_queue_ds( int qid, struct msqid_ds *qbuf);
int change_queue_mode(int qid, char *mode);

//Upload files= 0, Download files= 1
int serverState = 0;

int numberLoggedInUsers = 0;

struct sockaddr_in client; // Struct for Server addr 

struct message req;
struct msqid_ds mq_id = {0};
int clientUpload;
ssize_t msgLen;
char *username;
char *password;

int main(int argc, char** argv) {
    // create message queue key
    key_t key;
    if((key = ftok("/home/MtFS/Iteraties/ftokDing", 13)) < 0) {
        perror("ftok");
        exit(1);
    }

    // create queue, if not succesfull, remove old queue
    // and try to make a new one.
    while ((clientUpload = msgget(key, 0666 | IPC_CREAT| IPC_EXCL)) < 0) { //| S_IRGRP | S_IWUSR
        perror("msgget");

        // delete message queue if it exists
        if (msgctl(clientUpload, IPC_RMID, &mq_id) == -1) {
            perror("msgctl1");
            exit(1);
        }
    }

    change_queue_mode(clientUpload, "0666");

/*
    if (msgctl(clientUpload, IPC_STAT, &mq_id) == -1) {
        perror("msgctl2");
        exit(1);
    }

    if (msgctl(clientUpload, IPC_SET, &mq_id) == -1) {
        perror("msgctl3");
        exit(1);
    }
*/

    establishConnection();

    return 0;
}

int get_queue_ds(int qid, struct msqid_ds *qbuf) {
    if (msgctl(qid, IPC_STAT, qbuf) == -1) {
        perror("msgctl IPC_STAT");
        exit(1);
    }

    return 0;
}

int change_queue_mode(int qid, char *mode) {
    struct msqid_ds tmpbuf;

    /* Retrieve a current copy of the internal data structure */
    get_queue_ds(qid, &tmpbuf);

    /* Change the permissions using an old trick */
    sscanf(mode, "%ho", &tmpbuf.msg_perm.mode);

    /* Update the internal data structure */
    if (msgctl(qid, IPC_SET, &tmpbuf) == -1) {
        perror("msgctl IPC_SET");
        exit(1);
    }

    return (0);
}
void establishConnection() {
    pthread_t child;                                                            //Thread ID of created thread
    int sockfd;                                                                 //Integer for socket
    int nsockfd;                                                                //Integer for client socket
    socklen_t sizeAddr;                                                      //Length of socket
    struct sockaddr_in addr;                                                    //Struct for client addr
    int optValue = 1;                                                           //Int for setsockoptions
    char ipAdres[32] = "192.168.80.2";                                          //IP-adres of server
    sizeAddr = sizeof (struct sockaddr_in);

    // create socket and errorhandling    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("[socket()]");
        exit(1);
    } else {
        printf("================================================================\n\n");
        printf("Socket created succesfully.\n\n");
    }

    // Fill socket with portno and ip address
    addr.sin_family = AF_INET;                                                  // Protocol Family
    addr.sin_port = htons(PORTNR);                                             // Portnumber
    inet_aton(ipAdres, &addr.sin_addr);                                         // Local IP- adres
    bzero(&(addr.sin_zero), 8);                                                 // empty rest of struct
    // int setsockopt (int fd, int level, int optname, const void *optval, socklen_t optlen)
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optValue, sizeof (int)) == -1) {
        perror("[setsockopt()]");
        exit(1);
    }

    // Fil socket with portnr and ip adress, also implemented error handling
    if (bind(sockfd, (struct sockaddr*) &addr, sizeof (struct sockaddr)) == -1) {
        perror("[bind()]");
        exit(1);
    } else {
        printf("================================================================\n\n");
        printf("Portnr %d is succesfully connected %s to.\n\n", PORTNR, ipAdres);
    }

    // Listen to incoming connections and errorhandling
    if (listen(sockfd, MAXCLIENTS) == -1) {
        perror("[listen()]");
        exit(1);
    } else {
        printf("================================================================\n\n");
        printf("Listen to port %d successfull.\n\n", PORTNR);
    }

    //Connectionloop to process connection requests from clients
    while (1) {

        //Accept incoming clients with error handling.
        if ((nsockfd = accept(sockfd, (struct sockaddr *) &client, &sizeAddr)) == -1) {
            perror("[accept()]");
            exit(1);
        } else {
            //create child thread
            pthread_create(&child, NULL, childThread, (void *) nsockfd);
            /*
                        // wait untill other child is ready
                        pthread_join(child, &status);
             */
        }
    }
}
void *childThread(void *nsockfd) {
    int sizeReceivedFile = 0;
    char receiveBuffer[PACKETSIZE]; //Buffer voor de ontvangen bestanden
    //char sendBuffer[PACKETSIZE]; //Buffer voor de te zenden bestanden
    //int sizeSendFile = 0; //Grootte van het te zenden bestand
    //char yolocol[PACKETSIZE];
    char *clientRequest;           //Char pointer voor het type request client, permissie en bestandsnaam
    int loginStatus = 0; // 0 = uitgelogd, 1 = ingelogd
    char *loggedInAs;

    printf("================================================================\n\n");
    printf("Connected with a client on IP-Adres: %s.\n\n", inet_ntoa(client.sin_addr));

    bzero(receiveBuffer, PACKETSIZE);


    while ((sizeReceivedFile = recv((int) nsockfd, receiveBuffer, PACKETSIZE, 0)) > 0) {
        // receive from client
        printf("Ontvangen buffer: %s\n",receiveBuffer);
        if (sizeReceivedFile == 0) {
            break;
        }
        // flags
        // retreive flag with strtok
        clientRequest = strtok(receiveBuffer, "#");
        printf("packet type: %s\n", clientRequest);

        // 2 = list
        // 3 = download
        // 4 = upload
        // 5 = login
        // 6 = logout

        if (strcmp(clientRequest, "2") == 0) {
            printf("execute list on current directory!\n");
        } else if (strcmp(clientRequest, "3") == 0) {
            downloadFile((int) nsockfd);
        } else if (strcmp(clientRequest, "4") == 0) {
            uploadFile((int) nsockfd);
        } else if (strcmp(clientRequest, "5") == 0){

            username = strtok(NULL,"#");
            password = strtok(NULL,"#");

            printf("Username = %s \n password = %s \n",username,password);

            int test;
            if((test= login(username,password))== 1){
                printf("login success, %i\n", test);
                loginStatus = 1;
            }
            else{
                printf("Inloggen mislukt, %i\n", test);
                loginStatus = 0;
            }

        } else if (strcmp(clientRequest, "6")== 0) {
            loginStatus = 0;
            printf("%s logged out\n", loggedInAs);
            loggedInAs = "";
        }
    }
    return 0;
}

void uploadFile(int nsockfd) {
    /*
        printf("execute download!\n");
            fileToDownload = strtok(NULL,"#");
            printf("%s",fileToDownload);


    int sizeReceivedFile = 0;

    // if relcv() returns 0 then the connection is gone
    while (sizeReceivedFile != 0) {
        //Upload of files
        if (serverState == 0) {
            sizeReceivedFile = recv((int) nsockfd, req.pakket.buffer, PACKETSIZE, 0);
            if (sizeReceivedFile < 0) {
                perror("[receive()]");
                exit(0);
            } else if (sizeReceivedFile == 0) {
                printf("The client has dropped the connection \n");
                close((int) nsockfd);
                pthread_exit(NULL);
            }

            // put the packet in the mailbox
            req.mtype = RESP_MT_DATA;                                           // has to be positive
            req.pakket.clientID = clientUpload;
            if (msgsnd(clientUpload, &req, PACKETSIZE, 0) == -1) {
                perror("msgsnd");
            }
        }
    }
    req.mtype = RESP_MT_END;
    msgsnd(clientUpload, &req, 0, 0);
    close((int) nsockfd);

    printf("================================================================\n\n");
    printf("Connection with client has been lost. Server is waiting for new clients clients...\n\n");
}

void downloadFile(int nsockfd) {
    /*
               printf("execute download!\n");
                   fileToDownload = strtok(NULL,"#");
                   printf("%s",fileToDownload);

     */
    char sendBuffer[PACKETSIZE]; 
    int sizeSendFile = 0; 


    if (send((int) nsockfd, sendBuffer, sizeSendFile, 0) < 0) {
        perror("[send()]");
        //exit(1);
    }
    bzero(sendBuffer, PACKETSIZE);

}

这是服务器端。我做了一个处理连接的过程,它将所有在我的自定义协议(protocol)标志中说“上传”的传入数据包传输到消息队列。这是代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stddef.h>
#include "mailbox.h" // self made header file

#define MAXCLIENTS 5
#define PORT 5002
#define MAXUSERS 1024

//Prototyping
void *childThread(void *ad);
void uploadFile(int nSockfd);
void buildConnection();

int get_queue_ds( int qid, struct msqid_ds *qbuf);
int change_queue_mode(int qid, char *mode);

// Upload files= 0, Download files= 1
int serverState = 0;

struct sockaddr_in client;

struct bericht req;
struct msqid_ds mq_id = {0};
int messageQueue;
ssize_t msgLen;

int main(int argc, char** argv) {
    // message queue key aanmaken
    key_t key;
    if((key = ftok("/home/file", 13)) < 0) {
        perror("ftok");
        exit(1);
    }

    // queue aanmaken, als dit niet lukt de eventueel oude queue verwijderen
    // en queue opnieuw proberen aan te maken.
    while ((messageQueue = msgget(key, 0666 | IPC_CREAT| IPC_EXCL)) < 0) {
        perror("msgget");

        // message queue verwijderen als deze al bestaat
        if (msgctl(messageQueue, IPC_RMID, &mq_id) == -1) {
            perror("msgctl1");
            exit(1);
        }
    }

    change_queue_mode(messageQueue, "0666");

    buildConnection();

    return 0;
}

int get_queue_ds(int qid, struct msqid_ds *qbuf) {
    if (msgctl(qid, IPC_STAT, qbuf) == -1) {
        perror("msgctl IPC_STAT");
        exit(1);
    }

    return 0;
}

int change_queue_mode(int qid, char *mode) {
    struct msqid_ds tmpbuf;

    // Retrieve a current copy of the internal data structure
    get_queue_ds(qid, &tmpbuf);

    // Change the permissions using an old trick
    sscanf(mode, "%ho", &tmpbuf.msg_perm.mode);

    // Update the internal data structure
    if (msgctl(qid, IPC_SET, &tmpbuf) == -1) {
        perror("msgctl IPC_SET");
        exit(1);
    }

    return (0);
}

void buildConnection() {
    pthread_t child;
    int sockfd;
    int nSockfd;
    socklen_t sockaddrSize;
    struct sockaddr_in addr;
    int optValue = 1;
    char ipAdres[32] = "192.168.80.2";
    sockaddrSize = sizeof (struct sockaddr_in);

    // create socket
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("[socket()]");
        exit(1);
    } else {
        printf("================================================================\n\n");
        printf("Socket is succesfully created.\n\n");
    }

    // fill dat socket
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    inet_aton(ipAdres, &addr.sin_addr);
    bzero(&(addr.sin_zero), 8);

    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optValue, sizeof (int)) == -1) {
        perror("[setsockopt()]");
        exit(1);
    }

    if (bind(sockfd, (struct sockaddr*) &addr, sizeof (struct sockaddr)) == -1) {
        perror("[bind()]");
        exit(1);
    } else {
        printf("================================================================\n\n");
        printf("bind succesful");
    }

    if (listen(sockfd, MAXCLIENTS) == -1) {
        perror("[listen()]");
        exit(1);
    } else {
        printf("================================================================\n\n");
        printf("Listening on port %d\n\n", PORT);
    }

    // Connection loop
    while (1) {
        // accept incoming clients
        if ((nSockfd = accept(sockfd, (struct sockaddr *) &client, &sockaddrSize)) == -1) {
            perror("[accept()]");
            exit(1);
        } else {
            pthread_create(&child, NULL, childThread, (void *) nSockfd);
        }
    }
}

void *childThread(void *nSockfd) {
    int sizeOfRecvFile = 0;
    char recvBuffer[PACKETSIZE];
    char *clientCommand; // request type

    printf("================================================================\n\n");
    printf("connected to client with IP: %s.\n\n", inet_ntoa(client.sin_addr));

    bzero(recvBuffer, PACKETSIZE);

    // get dem datas
    while ((sizeOfRecvFile = recv((int) nSockfd, recvBuffer, PACKETSIZE, 0)) > 0) {
        if (sizeOfRecvFile == 0) {
            break;
        }
        printf("received buffer: %s\n", recvBuffer);

        // handle protocol flag
        // chop protocol into pieces to check packet data and flags
        clientCommand = strtok(recvBuffer, "#");
        printf("packet type: %s\n", clientCommand);

        // if clientCommand == 4
        // incoming file!
        if (strcmp(clientCommand, "4") == 0) {
            uploadFile((int) nSockfd);
        }
    }
    return 0;
}

void uploadFile(int nSockfd) {
    int sizeOfRecvFile = 0;

    // if recv() is 0 close connection
    while (sizeOfRecvFile != 0) {
        if (serverStaat == 0) {
            sizeOfRecvFile = recv((int) nSockfd, req.pakket.buffer, PACKETSIZE, 0);
            if (sizeOfRecvFile < 0) {
                perror("[receive()]");
                exit(0);
            } else if (sizeOfRecvFile == 0) {
                printf("Client disconnected\n");
                close((int) nSockfd);
                pthread_exit(NULL);
            }

            // send packet to message queue
            req.mtype = RESP_MT_DATA;
            req.pakket.clientID = messageQueue;
            if (msgsnd(messageQueue, &req, PACKETSIZE, 0) == -1) {
                perror("msgsnd");
            }
        }
    }
    req.mtype = RESP_MT_END;
    msgsnd(messageQueue, &req, 0, 0);
    close((int) nSockfd);

    printf("================================================================\n\n");
    printf("Disconnected, now waiting for other clients...\n\n");
}

上面的程序使用了一个定制的头文件:

#include <sys/types.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <stddef.h>
#include <limits.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>

#define PACKETSIZE 65535

struct message {
    long mtype;
    struct packet {
        int clientID;
        char buffer[PACKETSIZE];
    } packet;
};

#define REQ_MSG_SIZE (offsetof(struct message.pakket, buffer) - \
                      offsetof(struct message.pakket, clientID) - PACKETSIZE)

struct responseMsg {                                                            // Responses (server to client)
    long mtype;                                                                 // One of RESP_MT_* values below
    char data[PACKETSIZE];                                                      // File content / response message
};

// Types for response messages sent from server to client
#define RESP_MT_FAILURE 1                                                       // File couldn't be opened
#define RESP_MT_DATA 2                                                          // Message contains file data
#define RESP_MT_END 3                                                           // File data complete

我还制作了一个将上传的文件写入硬盘的过程。该进程从连接进程中创建的消息队列中获取数据。代码:

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <signal.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include "/home/CommProces/mailbox.h"

#define POORTNR 5002
#define MAXCLIENTS 5

// prototyping
void writeFiles();

struct message resp;
int uploadMessage;
ssize_t msgLen;

int main () {
    key_t key;
    if(key = ftok("/home/CommProces/ftokDing", 13) < 0) {
        perror("ftok");
        exit(1);
    }

    uploadMessage = msgget(key, 0666);
    if (uploadMessage == -1) {
        perror("msgget");
        exit(1);
    }

    while(1) {
        writeFiles();
    }
}

void writeFiles() {
    char recvBuffer[PACKETSIZE];
    char *rights, *pathname, *filename, *temp;
    char *pathOfRecvFile;  // received files will go here

    FILE* theFile;
    bzero(recvBuffer, PACKETSIZE);
    int numMsgs, totBytes;
    int sizeOfRecvFile = 0;
    int nBytesToDisk = 0;

    totBytes = msgLen; // Count first message

    for (numMsgs = 1; resp.mtype == RESP_MT_DATA; numMsgs++) {
        msgLen = msgrcv(uploadMessage, &resp, PACKETSIZE, 0, 0);
        if (msgLen == -1) {
            perror("msgrcv");
            totBytes += msgLen;
        }
        *recvBuffer = *resp.pakket.buffer;
        //temp = strtok(recvBuffer,"#");
        rights = strtok(NULL,"#");
        if(strcmp(rights, "private") == 0) {
            temp = strtok(NULL,"#");
            pathname = strcat("/home/MtFS/UploadedFiles/private/", temp);
        } else {
            pathname = "/home/MtFS/UploadedFiles/public";
        }
        filename = strcat("/", strtok(NULL,"#"));
        pathOfRecvFile = strcat(filename, pathname);

        theFile = fopen(pathOfRecvFile, "wb");
        if(theFile == NULL) {
            printf("[Open_File] unable to create file %s\n", pathOfRecvFile);
        } else {
            nBytesToDisk = fwrite(recvBuffer, sizeof(char), sizeOfRecvFile, theFile);
            if(nBytesToDisk < sizeOfRecvFile) {
                perror("fwrite");
            }
            printf("=============================================================================================================================\n\n");
            printf("Files received and placed on HDD\n\n");
            bzero(recvBuffer, PACKETSIZE);
        }
        if (resp.mtype == RESP_MT_FAILURE) {
            printf("mtype = fail");
        } else if(resp.mtype == RESP_MT_END) {
            printf("mtype = end of data");
            fclose(theFile);
        }
    }
}

我一直在使用调试器筛选断点,但我无法确定导致问题的原因:(

最佳答案

对于初学者来说,msgrcv() 的第三个参数给出消息有效负载的大小。

所以这条线

msgLen = msgrcv(uploadMessage, &resp, PACKETSIZE, 0, 0);

应该是

msgLen = msgrcv(uploadMessage, &resp, sizeof(resp)-sizeof(resp.mtype), 0, 0);

msgLen = msgrcv(uploadMessage, &resp, sizeof(resp.packet), 0, 0);

同时调用 strtok()第一个参数设置为 NULL,没有意义。它最初需要用指向某个 0 终止的 char 数组的第一个 agrment 来调用。


另外^2:尝试连接到字符串文字 uinsg strcat()像这里:

pathname = strcat("/home/MtFS/UploadedFiles/private/", temp);

调用未定义的行为。

要解决此问题,请将 pathname 设为缓冲区,而不是指针:

char pathname[PATHMAX] = "";
...

    if(strcmp(rights, "private") == 0) {
        temp = strtok(NULL,"#");
        strcpy(pathname, "/home/MtFS/UploadedFiles/private/");
        strcat(pathname, temp);
    } else {
        strcpy(pathname, "/home/MtFS/UploadedFiles/public");
    }

您发布的代码描述了一个重要的系统,涉及一个 tcp-to-mq 代理(客户端)和一个 mq-server。我强烈建议单独调试这些组件。

关于c - C中通过消息队列发送文件出错?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21104091/

相关文章:

c - 使用关键字 'struct' 声明结构对象并在 malloc 中

c++ - 想要加载图像、处理它并将其保存回文件中?

c++ - 链接共享库依赖项未在 ldd 中列出

linux - Wayland 上的 libinput-gestures : can't get 'super' key

c - 如果 DB2 中存在当月和上个月的记录,如何选择它?

linux - 如何使用 Expect 自动化 telnet session ?

linux - 文件存在但在 Linux 中找不到该文件

windows - cmd.exe 上的 UTF-16

linux - 通过传递路径打开文件在 Linux 上有效,但在 Windows 上无效

c - 将 .Glade(或 xml)文件转换为 C 源代码的工具