1

I am trying to implement client-server communication via sockets. The main tasks are:

  1. Sending commands from clients to server

  2. Sending data from server to clients

  3. Sending data from clients to server

Commands should come via port1, data via port2.

I got it working without without multi-threading but I have some problems with understanding how do I need to handle sockets.

Current scenario:

1.Server starts (socket, bind, listen for both command and info sockets) and goes for infinite loop with this function:

void FTPServer::waitForConnection()
{
  sockaddr_in client;
  int clientsize = sizeof(client);

  SOCKET clientSocket = accept(_infoSocket, (struct sockaddr*)&client, &clientsize);

  if (clientSocket == INVALID_SOCKET)
  {
    cout << " Accept Info Error" << endl;
  }
  else
  {
    cout << " Accept Info OK" << endl;

    char* buff = new char[CHUNK_SIZE];
    string fullRequest;
    int rc = recv(clientSocket, buff, CHUNK_SIZE, 0);
    if (rc == SOCKET_ERROR)
    {
      cout << " Recieve Info Error" << endl;
    }
    else
    {
      buff[rc] = NULL;
      fullRequest.append(buff);
      cout << " Recieve Info OK" <<endl;
      if (executeCommand(fullRequest, clientSocket))
      {
        logOperation(client, fullRequest.c_str());
      }
    }

    delete buff;
  }
}

2.Client starts (socket, connect), creates 2 sockets on same ports, waits for user input.

3.User types "LIST", clients checks that it's a valid command and sends it.

bool FTPClient::sendToServer(string data, const bool verbose)
{
  int n = 0;

  while (data.size() > CHUNK_SIZE)
  {
    string s = data.substr(CHUNK_SIZE).c_str();
    n += send(_infoSocket, data.substr(CHUNK_SIZE).c_str(), CHUNK_SIZE, 0);
    data = data.substr(CHUNK_SIZE+1);
  }
  n+=send(_infoSocket, data.c_str(), data.size(), 0);
  cout<<n<<endl;
  if(n<0)
  {
    cout<<"Error: sending"<<endl;
    return 0;
  }

  if (verbose)
  cout<<"Send "<<n<<" bytes"<<endl;

  return true;
}

4.Servers receives it, accepts on _dataSocket and sends the list of available files.

5.Client receives the list:

string FTPClient::getDataFromServer(const bool verbose)
{
  char data[CHUNK_SIZE];
  int size = recv(_dataSocket, data, strlen(data), 0);
  if (size > 0)
  {
    int n = 0;
    string res;
    while (size > CHUNK_SIZE)
    {
      int buff = recv(_dataSocket, data, CHUNK_SIZE, 0);
      res.append(data);
      size -= buff;
      n += buff;
    }

    n+= recv(_dataSocket, data, CHUNK_SIZE, 0);
    res.append(data);
    if (verbose)
    cout<<"Recevied "<<n<<" bytes"<<endl;

    res.resize(n);
    return res;
  }
  else
  {
    return "";
  }
}

Till this, it works. But if try to execute same command again, I got nothing. I think, problem is that for each connect we need an accept on server side. In main loop server gets only one connect from client. Is closing client command socket and reconnecting it on every request only option here? Any other advices (except for "Google it") are highly appreciated.

alk
  • 69,737
  • 10
  • 105
  • 255
Pavel Oganesyan
  • 6,774
  • 4
  • 46
  • 84
  • If you don't use threads, you have to use `select` or `poll` to wait for input on multiple socket. The server needs to be in a loop that waits for new connections and also waits for input on old connections. – Barmar Dec 15 '13 at 10:49
  • @barmar Thanks a lot. So, the idea is to store all connected sockets on server in array and check them for readability? Should `accept` be removed from main loop? – Pavel Oganesyan Dec 15 '13 at 11:12
  • This `int clientsize` should be `socklen_t clientsize` to avoid issues when passing an address to it and `int` and `socklen_t` are of different size. – alk Dec 15 '13 at 11:16
  • Yes, that's the idea. If the listening socket becomes readable, you call accept then, not in the main loop. – Barmar Dec 15 '13 at 11:16
  • Also the code misses error checking of `recv()`/`send()` calls in general, as well as to check `recv()` for having returned `0` in particular, which would indicated the other side closed the connection. – alk Dec 15 '13 at 11:19
  • @alk Thanks, error checking indeed needs to be done. – Pavel Oganesyan Dec 15 '13 at 11:21
  • I'd always start with it, as it's essential for debugging. – alk Dec 15 '13 at 11:22
  • I suggest store all sockets in one struct, use some wait functions (select, poll, epoll) for getting events and when latter occurs proceed based on socket type, ie read event for listen socket is new connection incoming. You can use one loop for this. – sim Dec 15 '13 at 11:28
  • @sim Thanks. Can you provide any code parts for better understanding? For example, do I need to manually re-assemble that structure on every step of cycle? – Pavel Oganesyan Dec 15 '13 at 12:48
  • @Pavel I suggest you to look at libevent library it's simplify development of event driven application, especially with sockets. For libevent examples see https://github.com/libevent/libevent/tree/master/sample, epoll http://stackoverflow.com/questions/27247/could-you-recommend-some-guides-about-epoll-on-linux – sim Dec 15 '13 at 15:07
  • @sim That's nice, but i am just curios to find out how to use sockets "as is". I think, i almost figured it out. – Pavel Oganesyan Dec 15 '13 at 15:37
  • @Pavel see second link there is example with epoll without 3rd party libs. To create good non blocking net service you need to use events with sockets. Use 3rd party libs for work with events or not it's your choice. – sim Dec 15 '13 at 16:17
  • @sim sure, i agree with it. – Pavel Oganesyan Dec 15 '13 at 16:26

0 Answers0