在C中的Socket中读取输入并保存在共享内存中

read input & save in shared memory in Socket in C

我在 Linux 的服务器端 Socket(使用 Telnet 客户端)工作。客户端输入一行命令(GET/PUT/DEL、键和关联值(中间用空格分隔)。然后将此键值对相应地传递给函数(GET/PUT/DEL),从而保存共享内存 (keyValueStore) 中的数据。

预期的客户端:(> 是服务器的输出)

GET key1
    > GET:key1:key_nonexistent 
PUT key1 value1
    > PUT:key1:value1 
PUT key2 value2
    > PUT:key2:value2 
DEL key2
    > DEL:key2:key_deleted

问题:

1/我尝试使用strtok()和keyValueStore将token分离并保存在一个普通的c文件中,但是我应该如何将其转换(或转换)为服务器和客户端之间的数据传输通信?

2/ 我应该何时何地调用命令函数(例如 int put(char* key, char* value) )?在 server.c 读取输入之后但在给出输出之前?

欢迎任何建议。谢谢你的好意!

server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define BUFSIZE 1024 // Buffer Size
#define TRUE 1
#define PORT 5678

int main() {

    int rfd; // Create-Descriptor 
    int cfd; // Connection-Descriptor (accept)

    struct sockaddr_in client;
    socklen_t client_len;
    char in[BUFSIZE]; 
    int bytes_read; 

    // 1. socket() 
    rfd = socket(AF_INET, SOCK_STREAM, 0);
    if (rfd < 0 ){
        fprintf(stderr, "Error\n");
        exit(-1);
    }

 
    //Initialize the server address by the port and IP
    struct sockaddr_in server;
    memset(&server, '[=11=]', sizeof(server));
    server.sin_family = AF_INET; // Internet address family: v4 address
    server.sin_addr.s_addr = INADDR_ANY; // Server IP address
    server.sin_port = htons(PORT); // Server port

    // 2. bind() 
    int brt = bind(rfd, (struct sockaddr *) &server, sizeof(server));
    if (brt < 0 ){
        fprintf(stderr, "Error\n");
        exit(-1);
    }

    // 3. listen() = listen for connections
    int lrt = listen(rfd, 5);
    if (lrt < 0 ){
        fprintf(stderr, "Error\n");
        exit(-1);
    }

    while (1) {

        // 4. accept() 
        cfd = accept(rfd, (struct sockaddr *) &client, &client_len);
 
        // read() = read from a socket (Client's data)
        bytes_read = read(cfd, in, BUFSIZE);

        while (bytes_read > 0) {
            printf("sending back the %d bytes I received...\n", bytes_read);
            // write() = write data on a socket (Client's data)
            write(cfd, in, bytes_read);
            bytes_read = read(cfd, in, BUFSIZE);
        }

        close(cfd);
    }
    close(rfd);
}

Input.c

#include <stdio.h>
#include <string.h>
#include <stdio.h>
#define MAX_ARRAY 100

int main() {

    typedef struct Value_ {
        char key[MAX_ARRAY];
        char value[MAX_ARRAY];
    } KeyStorage;
    KeyStorage storageKey[MAX_ARRAY];

    char client_input[MAX_ARRAY];
    char *argv[3];
    char *token;
    int count = 0;
    while (1) {
        printf("Input: ");
        gets(client_input);

//get the first token
        token = strtok(client_input, " ");
        int i = 0;
//walk through other tokens
        while (token != NULL) {
            argv[i] = token;
            i++;
            token = strtok(NULL, " ");
        }
        argv[i] = NULL; //argv ends with NULL

        // arg[0] = command z.B. GET, PUT
        printf("Commend: %s\n", argv[0]);

        strcpy(storageKey[count].key, argv[1]);
        printf("Key: %s\n", storageKey[count].key);

        strcpy(storageKey[count].value, argv[2]);
        printf("Value: %s\n", storageKey[count].value);

        count++;

        if (strcmp(argv[0], "QUIT") == 0) {
            break;
        }
    }
    return 0;
}

您的代码中存在一些错误。我已经修复了所有问题以构建一个工作示例。当然,这不是您的完整应用,甚至还有很大的改进空间。

我在 Windows 下使用 MSVC2019 开发并测试了我的代码,但我使用 #define 隔离了 Windows 特定代码,因此它应该在 [=31= 下正确编译和 运行 ](我还没有测试过)。

您的代码存在的主要问题是对 TCP 连接的误解。这是一个面向流的连接,您必须自己assemble“命令行”,一次接收一个字符。

只有当一​​行完整时,您才能解析它以检测客户端发送的命令。我做的很简单:只有一个命令“exit”做某事(关闭连接)。其他一切都被忽略了。

我让线路组装变得简单。这意味着无法进行编辑。退格键、删除键、光标键等,并像输入任何其他字符一样输入,并且无法达到用户预期的效果。你应该注意这个。

最后,我使代码与您使用的代码接近。此代码是单用户。它接受一个连接,接受来自它的命令,并且只在第一个连接关闭后才接受一个新连接。这通常不是创建服务器程序的方法。要使其成为 多用户,您应该使用非阻塞套接字和 select() 或使用多线程。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #ifdef WIN32
    #include <WinSock2.h>
    #include <io.h>
    typedef int socklen_t;
    #pragma warning(disable : 4996)  // No warning for deprecated function names such as read() and write()
    #else
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #define closesocket close
    #endif
    
    #define BUFSIZE 1024 // Buffer Size
    #define TRUE 1
    #define PORT 5678
    
    
    int main(int argc, char *argv[])
    {
    #ifdef WIN32
        int iResult;
        WSADATA wsaData;
    
        // Initialize Winsock
        iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
        if (iResult != 0) {
            printf("WSAStartup failed: %d\n", iResult);
            return 1;
        }
    #endif
    
        int rfd; // Create-Descriptor 
        int cfd; // Connection-Descriptor (accept)
    
        struct sockaddr_in client;
        socklen_t client_len;
        char in[BUFSIZE];
        int bytes_read;
    
        // 1. socket() 
        rfd = socket(AF_INET, SOCK_STREAM, 0);
        if (rfd < 0) {
            fprintf(stderr, "Error\n");
            exit(-1);
        }
    
        // Initialize the server address by the port and IP
        struct sockaddr_in server;
        memset(&server, '[=10=]', sizeof(server));
        server.sin_family = AF_INET; // Internet address family: v4 address
        server.sin_addr.s_addr = INADDR_ANY; // Server IP address
        server.sin_port = htons(PORT); // Server port
    
        // 2. bind() 
        int brt = bind(rfd, (struct sockaddr*)&server, sizeof(server));
        if (brt < 0) {
            fprintf(stderr, "Error\n");
            exit(-1);
        }
    
        // 3. listen() = listen for connections
        int lrt = listen(rfd, 5);
        if (lrt < 0) {
            fprintf(stderr, "Error\n");
            exit(-1);
        }
    
        while (1) {
            client_len = sizeof(client);
            cfd = accept(rfd, (struct sockaddr*)&client, &client_len);
            if (cfd < 0) {
                fprintf(stderr, "accept failed with error %d\n", WSAGetLastError());
                exit(-1);
            }
            printf("Client connected\n");
    
            while (1) {
/*
                // Send prompt to client
                char* prompt = "> ";
                if (send(cfd, prompt, strlen(prompt), 0) <= 0) {
                    fprintf(stderr, "send() failed with error %d\n", WSAGetLastError());
                    exit(1);
                }
*/    
                // read a line from a socket (Client's data)
                int bytes_idx = -1;
                while (1) {
                    if (bytes_idx >= (int)sizeof(in)) {
                        fprintf(stderr, "input buffer overflow\n");
                        break;
                    }
                    // Receive on byte (character) at a time
                    bytes_read = recv(cfd, &in[++bytes_idx], 1, 0);
                    if (bytes_read <= 0)  // Check error or no data read
                        break;
    /*
                    printf("sending back the %d bytes I received...\n", bytes_read);
                    if (send(cfd, &in[bytes_idx], 1, 0) <= 0) {
                        fprintf(stderr, "send() failed with error %d\n", WSAGetLastError());
                        exit(1);
                    }
    */
                    if (in[bytes_idx] == '\n') {
                        // Received a complete line, including CRLF
                        // Remove ending CR
                        bytes_idx--;
                        if ((bytes_idx >= 0) && (in[bytes_idx] == '\r'))
                            in[bytes_idx] = 0;
                        break;
                    }
                }
                if (bytes_idx > 0) {   // Check for empty line
                    printf("Received \"%s\"\n", in);
                    // Check for client command
                    if (stricmp(in, "exit") == 0)
                        break;
                    else {
                        printf("Client sent unknown command\n");
                    }
                }
            }
            closesocket(cfd);
            printf("Client disconnected\n");
        }
        closesocket(rfd);
    
    #ifdef WIN32
        WSACleanup();
    #endif
    }