首页 > 解决方案 > 在 C 中的 Socket 中读取输入并保存在共享内存中

问题描述

我在 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 将令牌分隔并保存在普通的 c 文件中,但我应该如何将其转换为服务器和客户端之间的数据传输通信?

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

任何建议都可以接受。谢谢你的好心!

服务器.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, '\0', 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);
}

输入.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;
}

标签: csocketsipc

解决方案


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

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

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

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

我用简单的方法组装线。这意味着无法进行编辑。退格键、删除键、光标键等,并像任何其他字符一样输入,并且不会像用户期望的那样起作用。你应该注意这一点。

最后,我使代码与您使用的代码接近。此代码是单用户。它接受一个连接,接受来自它的命令,并且只有在第一个连接关闭后才接受一个新连接。这通常不是创建服务器程序的方式。要使其成为multiuser,您应该使用非阻塞套接字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, '\0', 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
    }

推荐阅读