im writing a multithreaded server, how ever i have tried to debug this but for some reason it doesn't actually go into the client thread called by pthread_create(), bottom of main. Any idea why pthread_create() is failing ?
Also i was wondering if it would be a good idea to send a struct to the client and the client send a struct back to the server as the main communication between the server and client? or should send() and recv() be a better way to implement?
int main(int argc, char* argv[])
{
int fdServer;
int accept_fd;
int optVal = 1;
struct sockaddr_in fromAddr;
struct sockaddr_in serverAddr;
struct pollfd* pServerPollFd;
pthread_t threadId;
socklen_t fromAddrSize;
/*
* Check user input and assign values
*/
if(argc != 4)
errorServer(WRONG_CLI);
char* greeting = calloc(100,sizeof(char*));
char* file_name = calloc(100,sizeof(char*));
greeting = argv[2];
file_name = argv[3];
/*
* Check pthread_key_create != 0 -> ERROR
*/
if(pthread_key_create(&threadSpecificKey, dataDestructor))
errorServer(SYSTEM_ERROR);
int port_num = atoi(argv[1]);
if(!((port_num <= 65535)&&(1<=port_num)))
errorServer(INVALID_PORT);
/*
* Set up the server socket
*/
fdServer = socket(AF_INET, SOCK_STREAM, 0);
if(fdServer < 0)
errorServer(SYSTEM_ERROR);
if(setsockopt(fdServer, SOL_SOCKET, SO_REUSEADDR, &optVal, sizeof(int)) < 0)
errorServer(SYSTEM_ERROR);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port_num);
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(fdServer, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr_in)) < 0)
errorServer(PORT_ERROR);
/*
* Setup poll pool
*/
pMessageBuffer = fifo_buffer_create(-1);
poll_pool* pPool = poll_pool_create(MAX_CONNECTIONS + 1);
pServerPollFd = poll_pool_allocate(pPool, fdServer);
pServerPollFd->events |= POLLIN;
int flags = fcntl(fdServer, F_GETFL);// Get the file access mode and the file status flags;
if(fcntl(fdServer, F_SETFL, flags | O_NONBLOCK) == -1)// Set the file descriptor flags to the value specified by 3rd arg.
errorServer(SYSTEM_ERROR);
if(listen(fdServer, 4) != 0)//Allow only 4 connections to the server
errorServer(SYSTEM_ERROR);
while(1) {
fromAddrSize = sizeof(struct sockaddr_in);
/* Block, waiting for a connection request to come in and accept it.
* fromAddr structure will get populated with the address of the client
*/
while((accept_fd = accept(fdServer, (struct sockaddr*)&fromAddr, &fromAddrSize)) != -1){
printf("Someone connected \n");
client_connection *pClientConnection = client_connection_create(accept_fd);
client_details *pClientDetails = client_details_create();
client_session *pClientSession = client_session_create(pClientConnection,pClientDetails);
pthread_mutex_lock(&game_state_mutex);
//SEARCH THE LINKEDLIST FOR THE GAME NAME - IF FALSE CREATE NEW LINKED ELEMENT
C_lst requestedGame;
clst_init(&requestedGame,NULL);
clst_insert_next(&requestedGame,NULL,pClientSession);
pthread_mutex_unlock(&game_state_mutex);
write(accept_fd, greeting, strlen(greeting));
/* Start a thread to deal with client communication - pass the
* connected file descriptor as the last argument.
*/
pthread_create(&threadId, NULL, client_thread, (void*)pClientSession);
pthread_detach(threadId);
}
}
free(greeting);
free(file_name);
return 0;
}
And this is the beginning of the client_thread
void * client_thread(void* arg)
{
ssize_t numBytesRead;
char buffer[MAXBUFFER];
client_session* pClientSession = (client_session*)arg;
if(pthread_setspecific(threadSpecificKey, pClientSession))
errorServer(SYSTEM_ERROR);
/* below read fails because of the nonblocking fdServer from fnctl*/
while((numBytesRead = read(fd,buffer,MAXBUFFER))>0){
//loop code here
}
Why you not checked for pthread_create return value and not print corresponding error message?
s = pthread_create(...);
if (s != 0) {
errno = s;
perror("pthread_create");
exit(EXIT_FAILURE);
}
Source: pthread_create(3)
UPD. Also, for socket setting you can try getaddrinfo(3) instead of doing all by hand: http://www.beej.us/guide/bgnet/output/html/multipage/syscalls.html#getaddrinfo
UPD 2. What do you mean by 'sending struct'?
Related
I am attempting to make a game of Minesweeper where multiple players can access the server to play the game. I am struggling with using Pthreads to allow multiple connections to the game at the same time. The program starts in my main method, it then calls setup_listener then calls get_clients. I am new to pthreads and socket programming, my understanding is that for every new client added a new thread should be created. The method authenticate_process is used to check the clients username and password. Currently once i use pthread_create my game logic begins with the function menu_option passing it the current clients socket. When i run my first new client my program works perfectly however once i start a second client my program is stuck idling. Could someone please show or explain to me where/what needs to be done to fix my problem.
/* Global Variables */
int num_conn = 0;
int player_count = 0;
pthread_mutex_t mutexcount;
/*
* Main Program
*/
int main(int argc, char *argv[])
{
int port;
/* Make sure a port was specified. */
if (argc < 2) {
fprintf(stderr,"No port provided using port 12345\n");
port = 12345;
}
else {
port = atoi(argv[1]);
}
int lis_sockfd = setup_listener(port); /* Listener socket. */
pthread_mutex_init(&mutexcount, NULL);
while (1) {
int *cli_sockfd = (int*)malloc(2*sizeof(int)); /* Client sockets */
memset(cli_sockfd, 0, 2*sizeof(int));
/* Get clients connected. */
get_clients(lis_sockfd, cli_sockfd);
pthread_t thread; /* Don't really need the thread id for anything in this case, but here it is anyway. */
int result = pthread_create(&thread, NULL, menu_option, (void *)cli_sockfd); /* Start a new thread for this game. */
if (result){
printf("Thread creation failed with return code %d\n", result);
exit(-1);
}
printf("[DEBUG] New game thread started.\n");
}
close(lis_sockfd);
pthread_mutex_destroy(&mutexcount);
pthread_exit(NULL);
}
/* Sets up the listener socket. */
int setup_listener(int portno)
{
int sockfd;
struct sockaddr_in serv_addr;
/* Get a socket to listen on */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening listener socket.");
/* Zero out the memory for the server information */
memset(&serv_addr, 0, sizeof(serv_addr));
/* set up the server info */
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
/* Bind the server info to the listener socket. */
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("ERROR binding listener socket.");
//printf("[DEBUG] Listener set.\n");
/* Return the socket number. */
return sockfd;
}
/* Sets up the client sockets and client connections. */
void get_clients(int lis_sockfd, int * cli_sockfd)
{
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
/* Listen for clients. */
listen(lis_sockfd, 253 - player_count);
/* Zero out memory for the client information. */
memset(&cli_addr, 0, sizeof(cli_addr));
clilen = sizeof(cli_addr);
/* Accept the connection from the client. */
cli_sockfd[num_conn] = accept(lis_sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (cli_sockfd[num_conn] < 0)
/* Horrible things have happened. */
error("ERROR accepting a connection from a client.");
/* Send the client it's ID. */
write(cli_sockfd[num_conn], &num_conn, sizeof(int));
/* Authentication Process */
bool isauthenticated = authenticate_process(*cli_sockfd);
if (isauthenticated){
printf("Authenticated\n");
}
else {
printf("Not Authenticated\n");
}
/* Increment the player count. */
num_conn++;
pthread_mutex_lock(&mutexcount);
player_count++;
printf("Number of players is now %d.\n", player_count);
pthread_mutex_unlock(&mutexcount);
}
/* Runs a game clients. */
void *menu_option(void *thread_data)
{
int *cli_sockfd = (int*)thread_data; /* Client sockets. */
int gameoption = 0;
while(1){
gameoption = recv_int(*cli_sockfd);
printf("[DEBUG] User menu option: %d\n",gameoption);
while (gameoption == 1){
running_game(*cli_sockfd); // Run Game Logic
break;
}
while (gameoption == 2){
show_leaderboard(*cli_sockfd);
// Need to notify client to break and send menu option again
break;
}
}
printf("\nGame over.\n");
close(*cli_sockfd);
free(cli_sockfd);
pthread_exit(NULL);
}
I am having trouble getting my TCP/IP connection between my client and server working.
Here is the server code -
int main() {
int servSock; /* Socket descriptor for server */
int clntSock; /* Socket descriptor for client */
unsigned short echoServPort; /* Server port */
struct sockaddr_in echoServAddr; /* Local address */
struct sockaddr_in echoClntAddr; /* Local address */
pid_t processID; /* Process ID from fork()*/
unsigned int childProcCount = 0; /* Number of child processes */
unsigned int clntLen;
unsigned int recvMsgSize;
echoServPort = 22;
if ((servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){
printf("Socket failed");
}
echoServAddr.sin_family = AF_INET; //Internet address family
echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); //Any incoming interface
echoServAddr.sin_port = htons(echoServPort); // Local port
if (bind(servSock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0){
printf("bind failed");
}
if (listen(servSock, MAXPENDING) < 0){
printf("listen() failed");
}
clntLen = sizeof(echoClntAddr);
if ((clntSock=accept(servSock,(struct sockaddr *)&echoClntAddr,&clntLen))<0){
printf("accept() failed");
}
if ((recvMsgSize = recv(clntSock, buf, 1024, 0)) < 0){
printf("recv() failed");
}
/* Send received string and receive again until end of transmission */
while (recvMsgSize > 0) { /* zero indicates end of transmission */
printf("%d", recvMsgSize);
if (send(clntSock, buf, recvMsgSize, 0) != recvMsgSize){
//printf(“send() failed”);
}
if ((recvMsgSize = recv(clntSock, buf, 1024, 0)) < 0){
//printf(“recv() failed”);
}
}
sleep(60);
}
}
return EXIT_SUCCESS;
}
And the client code, which is a CGI application.
int main(void) {
int servSock; /* Socket descriptor for server */
int clntSock; /* Socket descriptor for client */
unsigned short echoServPort; /* Server port */
struct sockaddr_in echoServAddr; /* Local address */
struct sockaddr_in echoClntAddr; /* Local address */
struct sockaddr_in {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
/*pid_t processID; Process ID from fork()
unsigned int childProcCount = 0; Number of child processes
unsigned int clntLen;*/
//char echoservIP = "10.0.0.2";
printf("Content-type: text/html\n\n");
puts("<HTML>");
puts("<BODY>");
echoServPort = 22;
servSock = 22;
clntSock = 22;
puts("<br>");
if ((clntSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){
printf("socket() failed");
}
echoServAddr.sin_family = AF_INET; //Internet address family
echoServAddr.sin_addr.s_addr = inet_addr("10.0.0.2"); //Server IP address
echoServAddr.sin_port = htons(echoServPort); //Server port
echoClntAddr.sin_addr.s_addr = inet_addr("10.0.0.1");
if (connect(clntSock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0){
printf("connect() failed\n");
}
int clntLen;
clntLen = sizeof(echoClntAddr);
if ((clntSock=accept(servSock,(struct sockaddr *)&echoClntAddr,&clntLen))<0){
printf("accept() failed\n");
}
char echoString[4] = "helo";
int echoStringLen;
echoStringLen = strlen(echoString); //Determine input length
//Send the string to the server
if (send(clntSock, echoString, echoStringLen, 0) != echoStringLen){
printf("send() sent a different number of bytes than expected");
}
puts("<p>Hello <b>CGI</b</p>");
puts("</BODY>");
puts("</HTML>");
return EXIT_SUCCESS;
}
When debugging, the problem occurs on the client side at the line
if ((clntSock=accept(servSock,(struct sockaddr
*)&echoClntAddr,&clntLen))<0){
printf("accept() failed\n");
}
And at
if(send(clntSock, echoString, echoStringLen, 0) != echoStringLen){
printf("send() sent a different number of bytes than expected");
}
I get the output
<HTML>
<BODY>
<br>
accept() failed
send() sent a different number of bytes than expected<p>Hello
<b>CGI</b</p>
Need help fixing this!, thanks.
You don't need to call accept() on the client - you just need to connect.
And
char echoString[4] = "helo";
is wrong. It's not NUL-terminated. Just do
char echoString[] = "helo";
If accept fails you shouldn't be proceeding with other operations on socket like recv. TCP connections is not in place for you to proceed in data exchange between server and client. You need to handle error conditions with out fail.
On the client side you have a custom error message on send. That does not help. Usually, partial sends are not common. Hence your print `sent a different number of bytes than expected' can be misleading. You need to find the real reason.
Calling accept in client code is not needed. Its the server which accepts and clients the ones which connect
Use errno and perror like - perror("Accept Failed") on ALL of your system calls for easier debugging and remove custom prints
I have a simple C program that's supposed to listen for connections and start a new thread for each client that connects. The thread simply prints what messages it receives (for now). I followed two tutorials while making this.
It works, but I tried connecting and disconnecting repeatedly with netcat without sending any messages. Each time I connect, the program takes 8KB of memory, but it only releases 4KB when I disconnect. But I can't find the cause of the leak. It ends the thread and closes the socket every time the user disconnects. Here is all of the code involved:
void* clientFunction(void* arg) {
char receiveBuffer[RECEIVE_BUFFER_SIZE];
long receiveSize;
int clntSocket = * ((int*) arg);
while (true) {
//receive messages
receiveSize = recv(clntSocket, receiveBuffer, RECEIVE_BUFFER_SIZE, 0);
if (receiveSize <= 0) {
close(clntSocket);
return NULL;
}
printf("Received message: %s", receiveBuffer);
memset(&receiveBuffer, 0, sizeof(receiveBuffer));
}
return 0;
}
int main(int argc, const char * argv[]) {
//FOR LISTENING SOCKET =====
int servSock; /* Socket descriptor for server */
int clntSock; /* Socket descriptor for client */
struct sockaddr_in serverAddress; /* Local address */
struct sockaddr_in clientAddress; /* Client address */
unsigned int clntLen; /* Length of client address data structure */
// =======
/* Create socket for incoming connections */
if ((servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
printf("Socket creation failed!\n");
return SOCKET_ERROR;
}
memset(&serverAddress, 0, sizeof(serverAddress)); /* Zero out structure */
serverAddress.sin_family = AF_INET; /* Internet address family */
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
serverAddress.sin_port = htons(PORT); /* Local port */
if (bind(servSock, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0) {
printf("Socket binding failed!\n");
return SOCKET_ERROR;
}
if (listen(servSock, MAXPENDING) < 0) {
printf("Socket listening failed!\n");
return SOCKET_ERROR;
}
isListening = true;
int* arg = &clntSock;
while (isListening) { //should have a timer?
/* Set the size of the in-out parameter */
clntLen = sizeof(clientAddress);
/* Wait for a client to connect */
if ((clntSock = accept(servSock, (struct sockaddr *) &clientAddress, &clntLen)) >= 0) { //??????
/* clntSock is connected to a client! */
pthread_t clientThread;
pthread_create(&clientThread, NULL, &clientFunction, (void*) arg);
}
}
return 0;
}
I put in pthread_detach(pthread_self()) right after the socket closing line, and it doesn't have the problem anymore. – sudo
/* SEND FUNC. */
int mysend(unsigned char *buffer, int len) {
int sock,ret;
int status,flags;
struct sockaddr_in6 servaddr;
int opt = 1;
char *addr = "1101::1";
sock = socket(AF_INET6,SOCK_DGRAM,0);
if (sock < 0)
return -1;
if( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 )
return -1;
flags = fcntl(sock, F_GETFL, 0);
fcntl(sock, F_SETFL, flags|O_NONBLOCK);
servaddr.sin6_family = AF_INET6;
servaddr.sin6_port = htons(61616);
status = inet_pton(AF_INET6, addr, &servaddr.sin6_addr);
if (status <= 0) {
perror("inet_pton");
return -1;
}
/* send message to server */
status = sendto(sock, buffer, len, 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
if (status < 0) {
perror("sendto");
return -1;
}
close(sock);
printf("MESSAGE SENT SUCCESSFULLY\n");
return 0;
}
/* RECEIVE FUNC. */
int myrcv() {
int sock,ret;
int status,len,rx_bytes;
int timeout,nfds =1;
struct sockaddr_in6 servaddr;
struct timeval wait;
unsigned char rxbuff[1024];
char *rcv;
char *addr = "1101::1";
fd_set rd;
struct pollfd *fds;
sock = socket(AF_INET6,SOCK_DGRAM,0);
if (sock < 0)
return -1;
servaddr.sin6_family = AF_INET6;
servaddr.sin6_port = htons(61616);
status = inet_pton(AF_INET6, addr, &servaddr.sin6_addr);
if (status <= 0)
return -1;
bind(sock,(struct sockaddr *)&servaddr,sizeof(servaddr));
timeout = (1* 1000);
wait.tv_sec = 10;
wait.tv_usec = 0;
len = sizeof(servaddr);
fds->fd = sock;
fds->events = POLLIN;
for(;;) {
//FD_ZERO(&rd);
//FD_SET(sock,&rd);
printf("Waiting for data....\n");
ret = poll(fds,nfds,timeout);
//ret = select(1,&rd,NULL,NULL,&wait);
if(ret < 0)
break;
if(fds->revents == 0)
printf("revents 0 %d\n",ret);
if(ret == 0)
continue;
memset(rxbuff,0,1024);
//if(FD_ISSET(sock,&rd)) {
printf("receiving message\n");
rx_bytes = recvfrom(sock,rxbuff,1024,0,(struct sockaddr *)&servaddr,&len);
memcpy(rcv,rxbuff,rx_bytes);
//}
}
close(sock);
return 0;
}
int main()
{
/* call mysend() periodically using sigaction() */
/* create a thread that continuously monitors(calls myrcv()) for incoming data */
return 0;
}
I'm unable to receive the packets from the server, but I could see the packets in the tcpdump output. Above are the sample client code snippets, which tries to receive and send the data from/to the server. The scenario is: the client needs to send data periodically to server and should also be able to receive any data from the server.
I have tried using both poll and select methods but failed to receive. Please let me know if I'm missing anything. Thanks for your support.
The problem you have with receiving is that you need to bind the receiving socket to the local port.
You also have other things that can be improved, like creating a single socket for both sending and receiving and using SO_REUSEADDR on the sending socket (not needed on a write-only socket).
What you should do is:
Create socket
Set socket options
Bind to local address (Use IN6ADDR_ANY_INIT to bind to all interfaces)
Write to server
Poll for reply
Several things:
Your receive function (myrcv) isn't specifying a listen port via the bind() call. That's the most likely problem. Ditto for your send function, although a port is chosen randomly for you.
In you myrcv() function, I don't see where you have actually initialized fds or nfsd prior to calling poll().
Re-opening and closing the socket on each call to mysend() looks problematic. If you are expecting the server to send back to the same client on the same port it received the message on, chances are high you have already closed the socket. You should just open one socket for both sending and receiving. You can share the same socket between your send thread and your receive thread.
After #cnicutar answers me on this question, I tried to send a file-descriptor from the parent process to its child. Based on this example, I wrote this code:
int socket_fd ,accepted_socket_fd, on = 1;
int server_sd, worker_sd, pair_sd[2];
struct sockaddr_in client_address;
struct sockaddr_in server_address;
/* =======================================================================
* Setup the network socket.
* =======================================================================
*/
if((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket()");
exit(EXIT_FAILURE);
}
if((setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on))) < 0)
{
perror("setsockopt()");
exit(EXIT_FAILURE);
}
server_address.sin_family = AF_INET; /* Internet address type */
server_address.sin_addr.s_addr = htonl(INADDR_ANY); /* Set for any local IP */
server_address.sin_port = htons(port); /* Set to the specified port */
if(bind(socket_fd, (struct sockaddr *) &server_address, sizeof(server_address)) < 0)
{
perror("bind()");
exit(EXIT_FAILURE);
}
if(listen(socket_fd, buffers) < 0)
{
perror("listen()");
exit(EXIT_FAILURE);
}
if(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair_sd) < 0)
{
socketpair("bind()");
exit(EXIT_FAILURE);
}
server_sd = pair_sd[0];
worker_sd = pair_sd[1];
/* =======================================================================
* Worker processes
* =======================================================================
*/
struct iovec iov[1];
struct msghdr child_msg;
char msg_buffer[80];
int pass_sd, rc;
/* Here the parent process create a pool of worker processes (its children) */
for(i = 0; i < processes; i++)
{
if(fork() == 0)
{
// ...
/* Loop forever, serving the incoming request */
for(;;)
{
memset(&child_msg, 0, sizeof(child_msg));
memset(iov, 0, sizeof(iov));
iov[0].iov_base = msg_buffer;
iov[0].iov_len = sizeof(msg_buffer);
child_msg.msg_iov = iov;
child_msg.msg_iovlen = 1;
child_msg.msg_name = (char *) &pass_sd;
child_msg.msg_namelen = sizeof(pass_sd);
printf("Waiting on recvmsg\n");
rc = recvmsg(worker_sd, &child_msg, 0);
if (rc < 0)
{
perror("recvmsg() failed");
close(worker_sd);
exit(-1);
}
else if (child_msg.msg_namelen <= 0)
{
printf("Descriptor was not received\n");
close(worker_sd);
exit(-1);
}
else
{
printf("Received descriptor = %d\n", pass_sd);
}
//.. Here the child process can handle the passed file descriptor
}
}
}
/* =======================================================================
* The parent process
* =======================================================================
*/
struct msghdr parent_msg;
size_t length;
/* Here the parent will accept the incoming requests and passed it to its children*/
for(;;)
{
length = sizeof(client_address);
if((accepted_socket_fd = accept(socket_fd, NULL, NULL)) < 0)
{
perror("accept()");
exit(EXIT_FAILURE);
}
memset(&parent_msg, 0, sizeof(parent_msg));
parent_msg.msg_name = (char *) &accepted_socket_fd;
parent_msg.msg_namelen = sizeof(accepted_socket_fd);
if((sendmsg(server_sd, &parent_msg, 0)) < 0)
{
perror("sendmsg()");
exit(EXIT_FAILURE);
}
}
But unfortunately, I got this error:
sendmsg(): Invalid argument
What should I do to fix this problem? and am I using the msghdr structure correctly? because in the example I mentioned above, they use msg_accrights and msg_accrightslen and I got some error when I use them so I had to use msg_name and msg_namelen instead.
The problem is you are passing the file descriptor in a msg_name field. This is an address field, and it is not intended to pass arbitrary data.
In fact, the file descriptors should be passed in a special way so the kernel could duplicate the file descriptor for the receiving process (and maybe the descriptor will have another value after the duplicating). That's why there is a special ancillary message type (SCM_RIGHTS) to pass file descriptors.
The following would work (I omitted some of the error handling).
In client:
memset(&child_msg, 0, sizeof(child_msg));
char cmsgbuf[CMSG_SPACE(sizeof(int))];
child_msg.msg_control = cmsgbuf; // make place for the ancillary message to be received
child_msg.msg_controllen = sizeof(cmsgbuf);
printf("Waiting on recvmsg\n");
rc = recvmsg(worker_sd, &child_msg, 0);
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&child_msg);
if (cmsg == NULL || cmsg -> cmsg_type != SCM_RIGHTS) {
printf("The first control structure contains no file descriptor.\n");
exit(0);
}
memcpy(&pass_sd, CMSG_DATA(cmsg), sizeof(pass_sd));
printf("Received descriptor = %d\n", pass_sd);
In server:
memset(&parent_msg, 0, sizeof(parent_msg));
struct cmsghdr *cmsg;
char cmsgbuf[CMSG_SPACE(sizeof(accepted_socket_fd))];
parent_msg.msg_control = cmsgbuf;
parent_msg.msg_controllen = sizeof(cmsgbuf); // necessary for CMSG_FIRSTHDR to return the correct value
cmsg = CMSG_FIRSTHDR(&parent_msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(accepted_socket_fd));
memcpy(CMSG_DATA(cmsg), &accepted_socket_fd, sizeof(accepted_socket_fd));
parent_msg.msg_controllen = cmsg->cmsg_len; // total size of all control blocks
if((sendmsg(server_sd, &parent_msg, 0)) < 0)
{
perror("sendmsg()");
exit(EXIT_FAILURE);
}
See also man 3 cmsg, there are some examples.
This is extremely hard to get right. I'd recommend just using a library that does it for you. One of the simplest is libancillary. It gives you two functions, one to send a file descriptor over a UNIX-domain socket and one to receive one. They are absurdly simple to use.
You cannot send file descriptors over AF_INET. Use a UNIX domain socket.