PseudoPsyche
PseudoPsyche

Reputation: 4592

Send data to specific client via server

I'm writing a server and two clients in C. One client is a "slave" type of client that receives commands and the other is a "master" type client that sends commands. I want to have multiple instances of the slave connected to the server and be able to send a command to a specific slave from the master via the server.

My question is how do I specify the client I want to send the command to?

Here is a very basic example of my server (without error checking) My actual server has error checking and such, but it's too long to post (and wouldn't compile anyway for anyone because there are dependencies on other files in my project)

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

int server_portnumber = 51739;

int main() {
  int listenFD;
  int connectFD;
  socklen_t length;
  struct sockaddr_in s1;
  struct sockaddr_in s2;

  listenFD = socket( AF_INET , SOCK_STREAM , 0 );
  memset( &s1, 0, sizeof( s1 ) );
  s1.sin_family = AF_INET;
  s1.sin_addr.s_addr = INADDR_ANY;
  s1.sin_port = server_portnumber;

  bind( listenFD , (struct sockaddr*) &s1 , sizeof( s1 ) );

  length = sizeof( s1 );
  getsockname( listenFD ,  (struct sockaddr*) &s1 , &length );
  listen( listenFD , 512 );

  signal( SIGCHLD , SIG_IGN );

  while(1) {
    length = sizeof( s2 );
    connectFD = accept( listenFD , (struct sockaddr*) &s2 , &length );
    if( !fork() ){
      close( listenFD );
      int select = 0;
      while( read( connectFD , &select, sizeof( int ) ) ) {
        switch( select ) {
          case 10:
            // opcode
            break;
          case 20:
            // command
            break;
          default:
            break;
        }
      }
      close( connectFD );
      exit(0);
    }
  }
}

Upvotes: 0

Views: 1901

Answers (1)

abarnert
abarnert

Reputation: 366073

You need to assign each client an ID, give the master some way of knowing about those IDs, and give the server some way to map from IDs to sockets.

That "some way" is pretty vague… because there are endless possibilities, all with different design implications. For example, you could refer to clients by:

  • Socket fd.
  • Peer address (host and port).
  • Auto-numbered index.
  • Connection time.
  • Randomly-generated key.
  • Key received from client during some login step in your protocol.

For most of these, you'll have to tell the master that a new client is available with the relevant ID. When the master wants to send a message to a particular client, it'll send that ID back, which means you'll need some kind of map (just an array if it's something simple like the fd or an auto-index; probably a hash table or balanced tree if it's some arbitrary string) to look up IDs and get fds.

To make this more concrete, consider a chat protocol, like IRC but a lot simpler.

When you first connect, nobody can talk to you, and you can't talk to anyone else.

The server maintains a hash table to map nicknames to socket fds.

When you send a /nick NICKNAME command, where NICKNAME is a valid nickname (say, any sequence of up to 20 letters and numbers that nobody else is using yet), the server adds an entry to the hash table mapping your nickname to your fd, and sends a message to everyone saying "NICKNAME joined".

When you send a /msg NICKNAME MESSAGE command, the server looks up NICKNAME in the hash table. If it finds it, it sends MESSAGE to the socket with the corresponding fd.

Upvotes: 1

Related Questions