简体   繁体   中英

creating a new file descriptor in a multi threaded c server on linux

Applying what I learned in school to a hobby project, but I'm new to network programming. I'm designing a simple multi-user text game with a C server on Linux. I have a simple threaded C echo server using NPTL (Native POSIX Threads Library) for incoming connections. I'd like to avoid race and deadlock conditions by opening a new file descriptor each connection so they can access database items for equipment and so forth without having to use locks. Would I be able to use select() to poll each of my threaded connections? Does this make sense for best practices?

I'm trying to find an example in Beej's Guide to Network Programming but I'm coming up short.

Edit:

Here's the basics of my echo server:

// .. pick a port on startup
// initial socket creation
lsock = socket(AF_INET,SOCK_STREAM,0);
// ... error handling 
sname.sin_family = AF_INET;
sname.sin_addr.s_addr = INADDR_ANY;
sname.sin_port = htons( port );
// binding the socket
while(client_sock=accept(lsock,(struct sockaddr*)&client,(socklen_t*)&c))
{
    puts("New connection accepted");

    pthread_t cthread;
    new_sock = malloc(1);
    *new_sock = client_sock;

    if( pthread_create( &cthread , NULL ,  clientHandler , (void*) new_sock) < 0)
    {
        perror("Error: Thread creation");
        return 1;
    }

    puts("Handler assigned");
}
    
    void *clientHandler(void *socket_desc)
{
    //Get the socket descriptor
    int sock = *(int*)socket_desc;
    int n;

    char    sendBuff[100], client_message[2000];

    while((n=recv(sock,client_message,2000,0))>0)
    {
        send(sock,client_message,n,0);
    }
    close(sock);
    if(n==0) 
    {
        puts("Client disconnected");
    }
    else 
    {
        perror("recv failed");
    }
    return 0;
}

Here's what my plans were to implement ontop of my echo server on a basic room system and player system below:

struct pollfd pdata[MAXPLAY+1];

struct _player{
     obj o;
     int id,state,file_desc,roomNum;
     void(*ip)(plr *p, char ch);
     char name[16],op[MAXBUF],line[128];
     struct pollfd *p;
     int HP,ATK;
     plr *target;
};


void init_player(player *p){
     p->HP=10;
     p->ATK=1;
     p->target=NULL;
}

#define playerLloop(p) for(i=0,p=players;i<MAXPLAY;i++,p++)

if(p->state==0){
    init_plr(p);
    p->state=1;
    p->name[0]=0;
    p->file_desc=rsock; // file descriptor
    p->roomNum=0;
    // function to initialize names and add to linked list of players
    p->ip=new_connect;   
    // function to send clients a message
    sendp(p,"Welcome");
}

Once I initialize a player through my client handler, I'd add them into a linked list with their file descriptor. My question would come in here; what's the best way to avoid any type of race or deadlock condition when I have two separate threads trying to access an object on the server? Could I resolve this by handling it through my parsing function to get client input?

If player one tries to open a door in a room, player two tries to open the same door; when I read the file descriptor for both clients through the threaded connections, how do I assign priority to those sockets? Could this potentially cause a race or deadlock condition?

I'm just having a hard time understanding the concept of reading information across a threaded socket.

Would I be able to use select() to poll each of my threaded connections?

select() is for use in serving multiple I/O channels efficiently via a single thread. You clarified in comments that you intend to have a separate thread for each client. In that case, you do not need select to manage the client connections: each such connection has a whole thread dedicated to it!

You seem to be proposing that each of those threads would additionally obtain all the other resources it needs, too, such as connections to the underlying database. In that case, yes, to the extent that those external resources inherently support the kind of concurrent access you need, you can avoid using locks in your own code to protect them.

Does this make sense for best practices?

The thread-per-client model is a reasonably common one. It should be fine as long as the number of concurrent clients is not too large. You will want to ensure that there is a way to clean up the threads of clients that disappear without notice. How large is "too large" depends on details of the work each thread actually performs, and on the characteristics of the machine on which this runs.

If the number of concurrent clients grows large enough then you will need to change your approach, but I don't think you need to worry too much about that just yet. Keeping your code clean and modular and avoiding reliance on thread-local data will facilitate switching to a different approach if ever you reach the point where you need to do.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM