Thread: multiple connections via sockets

  1. #1
    Registered User
    Join Date
    Jun 2007
    Posts
    7

    multiple connections via sockets

    Hi guys,

    I'm new to c++ and indeed socket programming but as I've said in a few other threads I've joined in on, I've been programming for a while.

    I'm a little stuck on how I can achieve multiple connections to a single port in c++. I understand that each connection needs to be stored in an array, but how to identify the connection when it talks to the server is another thing..

    Should each connection be given a unique id (hashed) and it gets passed through everytime it talks to the server? This seems a little unneeded as 1. it could be comprimised and hence a security issue, 2. each connection sits on its own unique (??) socket. Does the TCP/IP protocol remember the sockets for each connection or do I do that?

    Soooo many questions, I've been plugging away at some code I found for tcp server/client trying to get it to do multiple clients and Its just creating more and more questions.. Sorry if these are very basic questions, but I would appreciate any guidance anybody is willing to answer.. for those looking for some starter code here is what I have.

    from http://sage.mc.yu.edu/kbeen/teaching...s/sockets.html
    Code:
    #include <arpa/inet.h>
    
    #include <netdb.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #include <iostream>
    
    #define MAX_MSG 100
    #define LINE_ARRAY_SIZE (MAX_MSG+1)
    
    using namespace std;
    
    int main()
    {
      int listenSocket, connectSocket[50], i, connection;
      unsigned short int listenPort;
      socklen_t clientAddressLength;
      struct sockaddr_in clientAddress, serverAddress;
      char line[LINE_ARRAY_SIZE];
      
      cout << "Enter port number to listen on (between 1500 and 65000): ";
      cin >> listenPort;
    
      // Create socket for listening for client connection requests.
      listenSocket = socket(AF_INET, SOCK_STREAM, 0);
      if (listenSocket < 0) {
        cerr << "cannot create listen socket";
        exit(1);
      }
      
      // Bind listen socket to listen port.  First set various fields in
      // the serverAddress structure, then call bind().
      // htonl() and htons() convert long integers and short integers
      // (respectively) from host byte order (on x86 this is Least
      // Significant Byte first) to network byte order (Most Significant
      // Byte first).
      serverAddress.sin_family = AF_INET;
      serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
      serverAddress.sin_port = htons(listenPort);
      
      if (bind(listenSocket,
               (struct sockaddr *) &serverAddress,
               sizeof(serverAddress)) < 0) {
        cerr << "cannot bind socket";
        exit(1);
      }
    
      // Wait for connections from clients.
      // This is a non-blocking call; i.e., it registers this program with
      // the system as expecting connections on this socket, and then
      // this thread of execution continues on.
      listen(listenSocket, 5);
    
      while (1) {
    
        cout << "Waiting for TCP connection on port " << listenPort << " ...\n";
    
        // Accept a connection with a client that is requesting one.  The
        // accept() call is a blocking call; i.e., this thread of
        // execution stops until a connection comes in.
        // connectSocket is a new socket that the system provides,
        // separate from listenSocket.  We *could* accept more
        // connections on listenSocket, before connectSocket is closed,
        // but this program doesn't do that.
    	connection = 0;
    
        clientAddressLength = sizeof(clientAddress);
        connectSocket[connection] = accept(listenSocket,
                               (struct sockaddr *) &clientAddress,
                               &clientAddressLength);
        
    	cout << "Connect socket? " << connectSocket;
    	
    	if (connectSocket < 0) {
          cerr << "cannot accept connection ";
          exit(1);
        }
       
    	// Show the IP address of the client.
        // inet_ntoa() converts an IP address from binary form to the
        // standard "numbers and dots" notation.
        cout << "  connected to " << inet_ntoa(clientAddress.sin_addr);
    
        // Show the client's port number.
        // ntohs() converts a short int from network byte order (which is
        // Most Significant Byte first) to host byte order (which on x86,
        // for example, is Least Significant Byte first).
        cout << ":" << ntohs(clientAddress.sin_port) << "\n";
    
        // Read lines from socket, using recv(), storing them in the line
        // array.  If no messages are currently available, recv() blocks
        // until one arrives.
        // First set line to all zeroes, so we'll know where the end of
        // the string is.
        memset(line, 0x0, LINE_ARRAY_SIZE);
        while (recv(connectSocket[connection], line, MAX_MSG, 0) > 0) {
          cout << "  --  " << line << "\n";
    
          // Convert line to upper case.
          for (i = 0; line[i] != '\0'; i++) {
    		if( (i % 2) == 0) 
    		  line[i] = toupper(line[i]);
    	  }
    
          // Send converted line back to client.
          if (send(connectSocket[connection], line, strlen(line) + 1, 0) < 0)
            cerr << "Error: cannot send modified data";
    
          memset(line, 0x0, LINE_ARRAY_SIZE);  // set line to all zeroes
        }
      }
    }
    As you can see, I've started by creating an int array with max connections = 50.

  2. #2
    Massively Single Player AverageSoftware's Avatar
    Join Date
    May 2007
    Location
    Buffalo, NY
    Posts
    141
    Each socket has a numeric identifier, and that's what accept() returns to you, and what you are storing in the array. It looks like your problem is listening for input from multiple sockets. There are two ways to handle that:

    1. Use a non-blocking function to read from the connections, and keep polling them in sequence.

    2. Create a thread that listens to each connection and deals with them appropriately.

    I've written a few server programs, and option #2 is generally the best approach. Your main thread should be the one listening for new connections, and whenever it receives one, it needs to create a new thread that is dedicated to that connection. When the connection is lost, you simply let the thread die.

    Of course, multithreading is a completely different subject, but it's something you should consider.
    There is no greater sign that a computing technology is worthless than the association of the word "solution" with it.

  3. #3
    Registered User
    Join Date
    Jun 2007
    Posts
    7
    I haven't done any multithreading.... Time to hit the books I think.
    Maybe I'm in over my head for a "beginner project"

  4. #4
    Deathray Engineer MacGyver's Avatar
    Join Date
    Mar 2007
    Posts
    3,210
    Quote Originally Posted by cope View Post
    I haven't done any multithreading.... Time to hit the books I think.
    Maybe I'm in over my head for a "beginner project"
    I would say so, only because multithreading is a vast subject, and very difficult to really be able to harness the power of until you get the concepts down and perhaps some good experience along with it. In order to make the best of socket programming, you really should have an idea how to use threads, or even to synchronize processes together.

    If you try to learn both the socket stuff and the multithreading stuff at the same time, you'll be quite likely to confuse yourself imo.

  5. #5
    Massively Single Player AverageSoftware's Avatar
    Join Date
    May 2007
    Location
    Buffalo, NY
    Posts
    141
    Quote Originally Posted by MacGyver View Post
    I would say so, only because multithreading is a vast subject, and very difficult to really be able to harness the power of until you get the concepts down and perhaps some good experience along with it. In order to make the best of socket programming, you really should have an idea how to use threads, or even to synchronize processes together.

    If you try to learn both the socket stuff and the multithreading stuff at the same time, you'll be quite likely to confuse yourself imo.
    True, but on the other hand, he already has the most difficult part done, getting the sockets working in the first place.

    You may want to try a simple program that only accepts one or two connections. You're obviously using a Unix-like system, so you have pthreads, and simple pthreads really aren't too difficult.
    There is no greater sign that a computing technology is worthless than the association of the word "solution" with it.

  6. #6
    Malum in se abachler's Avatar
    Join Date
    Apr 2007
    Posts
    3,195
    I agree, sockets are much more obtuse than threads. Primarily because there is a lack of good tutorials, (oh god I just volunteered again), but also because its just a more complex subject imho. I found threads very easy to understand once you learn the basic functionality and a few caveats.

    There are basically 2 functions you need to get started on basic threads. Either _beginthread/_beginthreadex or CreateThread. Personally I prefer CreateThread, but they both do exactly the same thing and neither one is 'better' than the other. I will use CreateThread in my example simply because thats the one im most familiar with. (yes im from Illinois).

    first you need to know that a thread is basically just a function with some special requirements for its declaration. here is an example thread function:
    Code:
    DWORD WINAPI foo(LPVOID lParam){
     
         // do something to the data pointed to by lParam
     
         return 0;
         }
    as you can see the function itself is very simple and straigth forward. It takes a single argument, which is a pointer. This pointer can be to a variable, or more often to a struct. Once a thread starts it usually runs intil it returns, just like a normal function, except that threads get their own time slice, and can run in parallel with the main application if you have hyperthreading or multiple processors. Now lets see how we start our thread running-
    Code:
    HANDLE hThread;
    DWORD ThreadId;
    DWORD MyVar;
     
    hThread = CreateThread(NULL , 0 , &foo , &MyVar , 0 , &ThreadId);
    Seems simple enough right? well, lets look at what each argument does.
    The NULL is a pointer to a SECURITY_ATTRIBUTES structure, somethign that isnt used very often and is beyond the scope of this tutorial. Unless you have a specific need to restrict access to yrou thread, just use NULL. The 0 is the stack size, 0 means default, and in my experience this value is ignored even if tis set to some value. &foo is our thread function. &MyVar is a pointer to some value or structure you want to pass the thread. the next value, 0 here, can be etiher 0 to start the thread imediatly, or CREATE_SUSPENDED. In which case the thread will be created, but wont run until you tell it to. Honestly I can count on one finger how many times ive ever needed to create a suspended thread. The last argument is a pointer to the variable that will recieve the ID of the created thread. You have to provide this value, even if you just point to a garbage variable. This variable is useful for some of the advanced thread related functions.

    A few caveats about threads-
    1. Each new thread takes up space, typically 1MB per thread. So dotn go thinking you can spawn 50,000 threads your first time out. Theres a 2GB per process limit for 32bit applications (3GB if you read MSDN). So for most applications, you can only spawn around 2000 threads, assuming you have 2GB of ram.
    2. The pointers you pass the thread should not go out of context before the thread is finished using them or you will get (bad) application errors or (worse) corrupted data.
    3. when you have a hammer, every problem looks like a nail. Threads are incredibly useful and powerful tools, but they cant solve every problem. Too often I see fellow programmers throwing threads at compute bound problems when simple optimization would work better.
    4. just because you have learned to paint, doesnt make you Picasso. Multithreading, particularly on multiprocessor systems, is an incredibly deep and complex subject. There are many advanced methods that you will have to learn to fully use thier power, but what ive shown you here will get you started using simple threads.
    Last edited by abachler; 06-07-2007 at 09:56 AM.

  7. #7
    Massively Single Player AverageSoftware's Avatar
    Join Date
    May 2007
    Location
    Buffalo, NY
    Posts
    141
    Quote Originally Posted by abachler View Post
    There are basically 2 functions you need to get started on basic threads...
    Nice speech, but most of that was Windows thread stuff. This guy is on Unix.
    There is no greater sign that a computing technology is worthless than the association of the word "solution" with it.

  8. #8
    Malum in se abachler's Avatar
    Join Date
    Apr 2007
    Posts
    3,195
    heh, I suppose I should have noticed the *nix header files in his code.
    well, most of it still applies other than the specifics fo the function.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Multiple Connections to a Server
    By niqo in forum Networking/Device Communication
    Replies: 9
    Last Post: 04-03-2008, 09:36 AM
  2. TCP Sockets: multiple clients - one server
    By Printisor in forum C Programming
    Replies: 4
    Last Post: 11-01-2007, 10:34 AM
  3. Best programming practices for multiple connections
    By vasillalov in forum Networking/Device Communication
    Replies: 8
    Last Post: 10-09-2007, 10:14 PM
  4. multiple UDP sockets with select()
    By nkhambal in forum Networking/Device Communication
    Replies: 2
    Last Post: 01-17-2006, 07:36 PM
  5. Multiple Client Connections
    By (TNT) in forum Windows Programming
    Replies: 1
    Last Post: 04-06-2002, 11:04 PM