C socket: recv and send all data

2019-03-13 08:30发布

I would like to obtain a behavior similar to this:

  1. Server run
  2. Client run
  3. Client type a command like "help" or other
  4. Server responds appropriately
  5. go to 3

The problem is that when my function excCommand("help") run just a little text is received and printed. My text file is this:

COMMAND HELP:

help - Display help
quit - Shutdown client

only COMMAND HELP is printed. Another problem is that when i type a command nothing is printed and after 2 command client exit. This is the piece in particular:

while (quit)
    {
        getLine("client> ", command, 10);
        if (strcmp(command, "quit") == 0)
            quit = 0;
        else
            excCommand(command);
    }

This is the server:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "common.h"

int main(int argc, char *argv[])
{
    if (argc != 2)
        ErrorWithUserMessage("Parameter(s)", "<Server Port>");

    char *service = argv[1];

    int servSock = SetupTCPServerSocket(service);
    if (servSock < 0)
        ErrorWithUserMessage("SetupTCPServerSocket() failed: ", "unable to establish");

    unsigned int childProcessCount = 0;
    while (1)
    {
        int clntSock = AcceptTCPConnection(servSock);

        pid_t processID = fork();
        if (processID < 0)
            ErrorWithSystemMessage("fork() failed");
        else if (processID == 0)
        {
            close(servSock);
            HandleTCPClient(clntSock);
            exit(EXIT_SUCCESS);
        }

        printf("with child process: %d\n", processID);
        close(clntSock);
        childProcessCount++;

        //clean up zombies
        while (childProcessCount)
        {
            processID = waitpid((pid_t) - 1, NULL, WNOHANG);
            if (processID < 0)
                ErrorWithSystemMessage("waitpid() failed");
            else if (processID == 0)
                break;
            else
                childProcessCount--;
        }

    }

}

Handler:

void HandleTCPClient(int clntSock)
{
    char buffer[BUFSIZE];
    ssize_t numBytesRcvd = recv(clntSock, buffer, BUFSIZE, 0);
    buffer[numBytesRcvd] = '\0';
    if (numBytesRcvd < 0)
        ErrorWithSystemMessage("recv() failed");
    if (strcmp(buffer, "help") == 0)
    {
        FILE *fp = fopen("help.txt", "r");
        if (fp)
        {
            char line[128];
            while (fgets(line, sizeof(line), fp) != NULL)
            {
                if (send(clntSock, line, sizeof(line), 0) < 0)
                    ErrorWithSystemMessage("send() failed");
            }
            fclose(fp);
        }
    }

    close(clntSock);
}

and this is my client:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/socket.h>

#include "common.h"

int sock;

void getLine(char *message, char *buf, int maxLen)
{
    printf("%s", message);
    fgets(buf, maxLen, stdin);
    buf[strlen(buf) - 1] = 0;
}

void excCommand(char *command)
{
    if ( send(sock, command, strlen(command), 0) < 0)
        ErrorWithSystemMessage("send() failed");

    char replyMessage[BUFSIZE];
    ssize_t numBytesRecv = 0;
    do
    {
        numBytesRecv = recv(sock, replyMessage, BUFSIZE, 0);
        if ( numBytesRecv < 0)
            ErrorWithSystemMessage("recv() failed");
        printf("%s\n", replyMessage);
        memset(&replyMessage, 0, sizeof(replyMessage));

    }
    while (numBytesRecv > 0);
}

void PrintFile(const char *filename)
{
    FILE *fp;
    fp = fopen(filename, "r");
    if (fp)
    {
        char line[128];
        while (fgets(line, sizeof(line), fp) != NULL)
            fputs(line, stdout);
        fputs("\n", stdout);
        fclose(fp);
    }
}

int main(int argc, char *argv[])
{
    int quit = 1;
    char command[10];

    if (argc < 2 || argc > 3)
    {
        ErrorWithUserMessage("Parameter(s)", "<Server Address> <Server Port>");
    }

    char *server = argv[1];
    char *service = argv[2];

    sock = SetupTCPClientSocket(server, service);
    if (sock < 0)
        ErrorWithUserMessage("SetupTCPClientSocket() failed: ", "unable to connect");

    printf("Connection established!\n\n");

    PrintFile("menu.txt");
    excCommand("help");

    while (quit)
    {
        getLine("client> ", command, 10);
        if (strcmp(command, "quit") == 0)
            quit = 0;
        else
            excCommand(command);
    }

    fputs("\n", stdout);
    close(sock);
    exit(EXIT_SUCCESS);
}

sorry for being so long-winded

3条回答
戒情不戒烟
2楼-- · 2019-03-13 09:09

I choose to send before each send() if i have to continue or not.

so i first have 3 define

#define BUFFSIZE 1024
#define CONT "CONT"
#define DONE "DONE"

Then to send my data

int     send_to_socket(int sock, char *msg)
{
    size_t  len;
    int     ret[2];

    len = strlen(msg);
    ret[0] = send(sock, (len <= BUFFSIZE) ? DONE : CONT, 4, 0);
    ret[1] = send(sock, msg, BUFFSIZE, 0);
    if (ret[0] <= 0 || ret[1] <= 0)
    {
        perror("send_to_socket");
        return (-1);
    }
    if (len > BUFFSIZE)
        return (send_to_socket(sock, msg + BUFFSIZE));
    return (1);
}

And to receive it :

char    *recv_from_socket(int cs)
{
    char    state[5];
    char    buff[BUFFSIZE+1];
    char    *msg;
    int     ret[2];

    msg = NULL;
    while (42)
    {
        bzero(state, 5);
        bzero(buff, BUFFSIZE+1);
        ret[0] = recv(cs, state, 4, 0);
        ret[1] = recv(cs, buff, BUFFSIZE, 0);
        if (ret[0] <= 0 || ret[1] <= 0)
        {
            perror("recv_from_socket");
            return (NULL);
        }
        // strfljoin() is selfmade
        // join the string and free the left argument to prevent memory leaks.
        // return fresh new string
        msg = (msg) ? ft_strfljoin(msg, buff) : strdup(buff);
        if (strncmp(state, DONE, 4) == 0)
            break ;
        i++;
    }
    return (msg);
}
查看更多
放我归山
3楼-- · 2019-03-13 09:28

The recv() and send() functions do not guarantee to send/recv all data (see man recv, man send)

You need to implement your own send_all() and recv_all(), something like

bool send_all(int socket, void *buffer, size_t length)
{
    char *ptr = (char*) buffer;
    while (length > 0)
    {
        int i = send(socket, ptr, length);
        if (i < 1) return false;
        ptr += i;
        length -= i;
    }
    return true;
}

The following guide may help you Beej's Guide to Network Programming

查看更多
神经病院院长
4楼-- · 2019-03-13 09:29

Usual problems.

void excCommand(char *command)
{
    if ( send(sock, command, strlen(command), 0) < 0)
        ErrorWithSystemMessage("send() failed");

    char replyMessage[BUFSIZE];
    ssize_t numBytesRecv = 0;
    do
    {
        numBytesRecv = recv(sock, replyMessage, BUFSIZE, 0);
        if ( numBytesRecv < 0)
            ErrorWithSystemMessage("recv() failed");
        printf("%s\n", replyMessage);

Invalid. numBytesRecv could have been zero, in which case there is no message at all, otherwise at this point must be positive, as you've already tested for negative, and it indicates the actual length of the message, which isn't necessarily null-terminated. Change to:

    if (numBytesRecv == 0)
        break;
    printf("%.*s\n", numBytesRecv, replyMessage);

and then:

        memset(&replyMessage, 0, sizeof(replyMessage));

Pointless. Remove.

    }
    while (numBytesRecv > 0);

At this point you should check for numBytesRecv < 0 and call perror() or one of its friends.

查看更多
登录 后发表回答