C套接字 - 消息仅发送一次

时间:2014-05-14 11:43:43

标签: c sockets

我正在尝试使用仅在共享内存中的文件创建一个小文件服务器。客户端应该向服务器发送命令,如CREATE,DELETE等。但是,我还没到那个阶段。

我准备了一个服务器和一个客户端。服务器接受套接字并为每个连接的客户端(需求)创建一个新线程。

当我启动客户端时,我可以成功连接到服务器并发送将收到的消息。但是,这只能工作一次。发送命令后,服务器将不会收到任何其他命令。

我尝试使用断路器捕获消息,但这似乎不起作用,所以任何帮助将不胜感激。

提前致谢。

服务器来源:

/*
 * main.c - the server file. 
 *
 *  Created on: Apr 26, 2014
 *      Author: fish-guts
 */

#include "main.h"

/* our main server buffer */
char serverbuf[4096];
static int client_sock;

void launch_app(char *argv[]) {
    if ((strcmp(argv[1], "start")) == 0)
        startup();
    else if ((strcmp(argv[1], "stop")) == 0)
        stop_server();
    else {
        fprintf(stderr,
                "Invalid Command: %s. Valid Commands are     ./fileserver         [start|stop]\n",
                argv[1]);
        exit(EXIT_FAILURE);
    }
}

int main(int argc, char* argv[]) {
if (argc > 1)
    launch_app(argv);
    else {
        fprintf(stderr,
                "No argument supplied. Valid Argument are [start|stop]\n");
        exit(EXIT_SUCCESS);
    }
    return 0;
}

void print_start_msg(void) {
    fprintf(stderr, "###############################\n");
    fprintf(stderr, "Welcome to Severin'ŝ FileServer\n");
    fprintf(stderr, "###############################\n");
}

void stop_server(void) {
    exit(EXIT_SUCCESS);
}

void startup(void) {
    print_start_msg();
    start_server();
}

void start_server(void) {
    int s, len, rc;
    int tid;
    long t;
    char buf[100000];

    struct sockaddr_in addr;
    struct sockaddr_in client;
    pthread_t client_thread;

    sock = socket(AF_INET, SOCK_STREAM, 0);

    unsigned short port = PORT;

    // clear the struct
    memset((char*) &addr, 0, sizeof(addr));

    fprintf(stderr, "\n\nStarting server...");
    // let's set some values

    /* type of socket created in socket() */
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons((unsigned short) PORT);

    inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
    /* bind the socket to the port specified above */

    if ((rc = bind(sock, (struct sockaddr *) &addr, sizeof(addr))) < 0) {
    fprintf(stderr, "Error binding address: %s\n", strerror(errno));
    exit(EXIT_FAILURE);
} else {
    fprintf(stderr, "Bind Successful\n");
}

if ((listen(sock, serverbuf) < 0)) {
    fprintf(stderr, "Listen Failed\n");
    exit(EXIT_FAILURE);
} else {
    fprintf(stderr, "Server started successfully, listening on port %d\n",
            PORT);
}
// the  main server loop
while (!quitting) {
    len = sizeof(struct sockaddr_in);

    client_sock = accept(sock, (struct sockaddr *) &client, &len);
    if (client_sock < 0) {
        fprintf(stderr, "Accept failed\n");
    } else {
        /* This is the client process */
        tid = pthread_create(&client_thread, NULL, doprocessing, (void*) t);
        if (tid) {
            fprintf(stderr, "Error creating thread: %d\n", tid);
        }
    }
}
}

void *doprocessing(void) {
    int n;

    int s, len, rc;
    char buf[100000];
    char *str = "Welcome\n";

    bzero(buf, 100000);
    n = write(client_sock, str, sizeof(str));

    if (n < 0) {
        fprintf(stderr, "ERROR writing to socket");
        exit(1);
    }
    s = recv(client_sock, buf, sizeof(serverbuf),0);
    if (s) {
        fprintf(stderr,"Bytes received: %i\n",s);
        buf[s] = 0;
        // we use CRLF as a line breaker, its easier to parse the commands
        char *pch = strtok(buf, "\n");
        while (pch != NULL) {
            strcpy(serverbuf, pch);
            fprintf(stderr, "Command: %s\n", buf);
            //TODO: add command handler
            //parse();
            serverbuf[s] = 0;
            pch = strtok(NULL, "\r\n");
            //addlog(1, serverbuf);
        }
    } else {
        fprintf(stderr,"No data received\n");
    }
}

客户来源:

#include "main.h"

#define PORT 8083
#define BUF_SIZE 1024

int quitting;

void start_client(const char *address);

int main(int argc, char* argv[]) {
    start_client("localhost");
}

void start_client(const char *address) {
    struct hostent *he;
    struct sockaddr_in server;
    int s;
    char buf[BUF_SIZE];
    char input[BUF_SIZE];
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if ((he = gethostbyname(address)) == NULL) {
        fprintf(stderr,"error resolving hostname..");
        exit(1);
    }

    /*
     * copy the network address part of the structure to the
     * sockaddr_in structure which is passed to connect()
     */
    memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
    server.sin_family = AF_INET;
    server.sin_port = htons((unsigned short)PORT);

    /* connect */
    if (connect(sock, (struct sockaddr *) &server, sizeof(server))) {
        puts("Error connecting...\n");
        exit(1);
    } else {
        fprintf(stderr,"Connected to server\n");
    }
    send(sock,"Hallo Server\n",BUF_SIZE,0);
    int i = 0;
    while(!quitting) {

        if(i<100) {
            send(sock,"yallo",6,0);
        }
        fgets(input, sizeof(input), stdin);
        if(send(sock,input,strlen(input),0)<0) {
            fprintf(stderr,"Error sending data: %s\n",errno);
        } else {
            fprintf(stderr,"Hello: %s\n",buf);
        }
        s = recv(sock, buf, sizeof(buf),0);
        if(s) {
            buf[s] = 0;
            fprintf(stderr,"Message from Server: %s\n",buf);
        } else {
            fprintf(stderr,"Error in recv: %s\n",errno);
        }
    }
}

1 个答案:

答案 0 :(得分:2)

处理套接字时几乎没有规则:

<强> 1。低级消息字段定义

始终记住您正在读写 字节流 。您必须记住的另一个规则是TCP / IP不能保证一次写入等于一次读取。但是,这些规则对你意味着什么?

上述规则的组合意味着您必须确切地知道在从套接字读取之前需要读取的字节数。所以:

  1. 案例1 ):如果您正在读取固定长度字段,则从流中读取与该字段中一样多的字节。
  2. 案例2 ):如果你想读取一个可变长度的字段,那么在发送前用字节数预先挂起该字段。
  3. 案例1 ): 例如,如果要发送/接收四字节整数,则代码应如下所示:

    int myInt = 12345;
    send(sockfd, &myInt, sizeof(myInt), my_flags);
    

    并在阅读方面:

    int myReceivedInt = 0;
    int received_count = recv(client_sock, &myReceivedInt, sizeof(myReceivedInt), 0);
    

    注意:不同的平台为数据类型定义了不同的大小。

    案例2 ):

    如果要发送可变长度字段(如字符串),则代码应如下所示:

    // get string length
    int str_size = strlen(my_str); 
    // send fixed-length data to pre-pend variable-length field with the latter's size
    send(sockfd, &str_size, sizeof(str_size), my_flags);  
    // send the variable-length field.
    send(sockfd, my_str, str_size, my_flags); 
    

    并在接收方:

    int string_size = 0;
    char *received_string = NULL;
    int received_count = recv(client_sock, &string_size, sizeof(string_size), 0);
    /*
      now we know how big the string is, so we allocate 
      memory for it or use a previously allocated buffer. This is omitted...
    */
    int received_count = recv(client_sock, &received_string, string_size, 0);
    received_string[string_size] = '\0';
    

    上述两条规则的另一个后果是,您可能需要从套接字中多次读取才能接收一个完整消息。对于较大的消息(例如文件传输)尤其如此。因此,当收到大型消息时,将recv()放在循环中并继续阅读,直到您阅读完整条消息。

    例如,如果您正在阅读文件:

    int file_size = 0, read_so_far = 0, ret = 0;
    recv(sockfd, &file_size , sizeof(file_size), 0); 
    // now we know how big is the file...allocate buffer (file_content) and read file fully
    while(read_so_far != file_size)
    {
       ret = recv(sockfd, file_content + read_so_far, file_size - read_so_far, 0); 
       if(ret < 0)
       {
          // handle error case, socket reset maybe?
          perror("Something bad happened with the socket");
          break;
       }
       read_so_far += ret;   
    }
    

    <强> 2。定义协议

    现在你知道了低级别的东西,你可以定义协议。该协议描述了通过连接接收的消息的格式。每条消息都由上面的一个或多个字段组成。

    第3。定义消息

    请记住,每条消息都应该为命令定义命令和参数。例如,删除文件的消息应该包含命令&#34; delete&#34;和&#34;文件名&#34;。因此,删除文件消息可以定义为:

    • 1个字节用于命令+ 4个字节用于name_size + variable-length file_name。

    对其他消息执行相同操作,您将最终获得该协议。代码可能如下所示:

    char command = 0;
    s = recv(client_sock, &command, 1,0); 
    
    if(command == LIST_NODES_COMMAND)
    {
     // read arguments for command and process it.
    } else if(command == CREATE_FOLDER_COMMAND)
    {
     // read arguments for command and process it.
    }
    

    <强> 4。消息处理循环

    服务器的最后一步是将消息处理放在循环中:

    while(1)
    {
        char command = 0;
        s = recv(client_sock, &command, 1,0);
        if(command == LIST_NODES_COMMAND)
        {
           // handle list command here.
        } else if(command == CREATE_FOLDER_COMMAND)
        {
           // read params like above and handle command.
    
        } else if (command == COMMAND_QUIT)
        {
           // do the stuff necessary before the client disconnects.
        }
    }