Sockets and threads using C

20,588

There is also a different critical error.

You cast the int to (void*). This does not make sense. Also, you can't pass the address directly since the variable could be changed on the next accept() call before the thread can copy the variable to its local stack. One way to write it would be something like this:

while (1) {
    newsocket_fd = accept(socket_fd, 
                          (struct sockaddr *) &client_addr, 
                          &client_len);
    if (newsocket_fd < 0) {
      error("ERROR on accept");
    }
    pthread_t thread;
    int *newsock = malloc(sizeof(int));
    *newsock = newsocket_fd;
    pthread_create(&thread, NULL, run_thread, newsock);
    pthread_detach(thread);
  }

With this approach, the thread will make sure to free() the newsock. E.g a simple

void *handler(void *thread_data) {
   int fd = *(int *) thread_data;
   free(thread_data);
   ....
}

Also, I assume pthread_detach() is okay, if the main program doesn't care about syncing up with the thread later with pthread_join().

Share:
20,588
Admin
Author by

Admin

Updated on July 09, 2022

Comments

  • Admin
    Admin almost 2 years

    I am new to both sockets and threads. I have this code:

    listen(socket_fd, 20);
    
    /* Looooop */
      while (1) {
        newsocket_fd = accept(socket_fd, 
                              (struct sockaddr *) &client_addr, 
                              &client_len);
        if (newsocket_fd < 0) {
          error("ERROR on accept");
        }
        pthread_t thread;
        pthread_create(&thread, NULL, run_thread, (void *) newsocket_fd);
        pthread_join(thread, NULL);
      }
    

    How can I start a new thread for each new connection, rather than for each request? These threads should be started when a new connection comes in, and these threads should then wait for requests, handle those requests, and finally return when the connection is closed. There should be one thread for each connection. Here is the code for run_thread:

    void
    *run_thread(void *ptr) {
      char buffer[256];
      bzero(buffer, 256);
      int n;
      n = read((int) ptr, buffer, 255);
      if (n < 0) error("ERROR Reading from socket");
      printf("%s\n\n**********\n\n", buffer);
    
      /* Parse buffer and return result */
      char *result;
      {
        /* First, determine command, 4 characters */
        /* (much code) */
      }
    
      n = write((int) ptr, result, strlen(result));
      if (n < 0) error("ERROR Writing to socket");
    }
    

    Can anyone help me? Thanks.

  • bstpierre
    bstpierre over 13 years
    He's not passing the address of newsocket_fd, he's passing the value and then casting the arg from (void*) to int in the thread func.
  • Admin
    Admin over 13 years
    Size of the pointer is always equal or bigger than integer. So there is no need in dynamic memory allocation to pass integer.
  • Maister
    Maister over 13 years
    I still feel it's rather bad taste to cast integers to (void*) there. Does the standard say anything about int -> (void*) -> int conversion (or similar conversions) being guaranteed to work?
  • Steve Emmerson
    Steve Emmerson over 13 years
    Casting an "int" to a "void*" and back again is guaranteed to work.
  • caf
    caf over 13 years
    @Steve Emmerson: No, I don't believe it is. Converting an integer to a pointer type (other than an integer constant 0) has an implementation-defined result. Converting a pointer to an integer type has an implementation-defined result, unless the result is outside the range of the integer type, in which case the behaviour is undefined. It is also noted that "The result need not be in the range of values of any integer type."
  • Steve Emmerson
    Steve Emmerson over 13 years
    "An object of integral type may be explicitly converted to a pointer. The mapping always carries a sufficiently wide integer converted from a pointer back to the same pointer, but is otherwise implementation-dependent". The sense is reversed, but, in practice, this guarantees what I said. I know of no counterexamples. If you do, then please enlighten me.
  • caf
    caf over 13 years
    @Steve: I'm reading from the C standard - which document are you looking at? Also, your statement doesn't imply the reverse (even if you have a sufficiently-wide pointer type) - for example, with a segmented memory architecture the integer-to-pointer conversion may produce a canonicalised pointer, which converts back to a different integer.
  • Steve Emmerson
    Steve Emmerson over 13 years
    @caf: K&R2. I used to work on a segmented memory machine and the integer/pointer mapping was still one-to-one. There might be machines in which this isn't the case -- but I haven't encountered one in 31 years of programming in C.
  • caf
    caf over 13 years
    @Steve: I can't recall how most DOS compilers converted between integers and (far) pointers, but two schemes seem reasonable to me: 1) Treat the integer as a linear address, so that bits 4 to 19 are placed in the segment and bits 0 to 3 are placed in the offset; 2) Treat the integer as a segment:offset, so that bits 16 to 31 are placed in the segment and bits 0 to 15 are placed in the offset. Scheme 1) clearly loses the top 12 bits of the original integer, and scheme 2) will map many integers onto few if the resulting pointer is canonicalised (which would be reasonable to do for comparisons).
  • Steve Emmerson
    Steve Emmerson over 13 years
    @caf: Like I said, the integer->pointer->integer idiom worked even on the segmented memory system I used. The combination of three things constrain the integer->pointer->integer sequence to recover the original integer: 1) the requirement that pointer->integer->pointer must recover the original pointer; 2) an "int" is the "native" word size; and 3) compiler writers and chip designers want to keep things simple. It's not in the standard, but it's dependable.