This is a sample program to transfer a socket from one process to another process using unix domain socket.
Content
- ListenerThis is a TCP listener server, which listen on a well-known port, to accept client's request. Whenever a client's connection request is coming, it launch a UpperServer process, and pass the FD which is used to communicate with client to UpperServer, so that the Client and UpperServer can communicate later without the Listener any more.
- UpperServerThis is a Upper echo server, its function is return the uppercase string back to client.
- ClientThis is client process
Usage
- ./Listener 12345Launch Listener server on port 12345
- ./Client 12345 UpperServerLaunch a client to connect to port 12345, and it will request Listener server to launch UpperServer as worker server.
- Loop communicate between Client and UpperServer; such as:Please enter the message(quit for quit): aaa
Receive message from server: AAA
Please enter the message(quit for quit): bbb
Receive message from server: BBB
Please enter the message(quit for quit): ccc
Receive message from server: CCC
Please enter the message(quit for quit):
Codes
Listener.c
/**
* Usage: Listener PORT
* for example, Listener 12345
*/
int main(int argc, char *argv[])
{
int userListenSock; /* Socket descriptor for server */
int workListenSock; /* Socket descriptor for server */
int userClientSock; /* Socket descriptor for client */
int workClientSock; /* Socket descriptor for client */
struct sockaddr_in userListenAddr; /* Local address */
struct sockaddr_un workListenAddr; /* Local address */
struct sockaddr_in userClientAddr; /* Client address */
struct sockaddr_un workClientAddr; /* Client address */
socklen_t size; /* Length of client address data structure */
char workerCMD[256];
unsigned char buffer[256];
int n;
assert (argc == 2);
/* Create socket for client connections */
userListenSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
memset(&userListenAddr, 0, sizeof(userListenAddr)); /* Zero out structure */
userListenAddr.sin_family = AF_INET; /* Internet address family */
userListenAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
userListenAddr.sin_port = htons(atoi(argv[1])); /* Local port */
bind(userListenSock, (struct sockaddr *) &userListenAddr, sizeof(userListenAddr));
listen(userListenSock, 1);
/* Create socket for worker connections */
workListenSock = socket(AF_UNIX, SOCK_STREAM, 0));
memset(&workListenAddr, 0, sizeof(workListenAddr)); /* Zero out structure */
workListenAddr.sun_family = AF_UNIX; /* Internet address family */
strcpy(workListenAddr.sun_path, "./socketfile" );
unlink("./socketfile");
bind(workListenSock, (struct sockaddr *) &workListenAddr, sizeof(workListenAddr));
listen(workListenSock, 1);
for (;;) /* Run forever */
{
/* Set the size of the in-out parameter */
size = sizeof(userClientAddr);
/* Wait for a client to connect */
printf("waiting client connection ...\n");
userClientSock = accept(userListenSock, (struct sockaddr *) &userClientAddr, &size));
printf("waiting client's request for a worker ...");
n = read(userClientSock, buffer, 255));
buffer[n]='\0';
printf("(%s)\n", buffer);
sprintf(workerCMD, "./%s", buffer);
printf("launch worker : %s\n",workerCMD);
popen(workerCMD, "r"); // lazy doing, popen is used here
/* Wait for a work process to connect */
printf("waiting worker to connect ...\n");
workClientSock = accept(workListenSock, (struct sockaddr *) &workListenAddr, &size));
printf("waiting READY message from worker ...\n");
n = read(workClientSock, buffer, 255));
if (n < 0 || strncmp((const char *)buffer, READY, strlen(READY)) != 0) {
printf("read() return %d=[%s]\n", n, buffer);
Error("read() data incorrect");
}
buffer[n]='\0';
printf("pass FD(%d) to worker(%s) ...\n", userClientSock, buffer);
send_fd(workClientSock, userClientSock);
//sleep (2);
close(userClientSock);
}
close(workClientSock);
/* NOT REACHED */
}
int send_fd(int fd, int sendfd)
{
char c = 0;
struct msghdr msg;
struct iovec iov[1];
union{
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
}control_un;
struct cmsghdr *cmptr;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
cmptr = CMSG_FIRSTHDR(&msg);
cmptr->cmsg_len = CMSG_LEN(sizeof(int));
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
*((int*)CMSG_DATA(cmptr)) = sendfd;
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = &c;
iov[0].iov_len = 1;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
return sendmsg(fd, &msg, 0);
}
UpperServer.c
int main(int argc, char *argv[])
{
int sock; /* Socket descriptor */
int userClientSock;
struct sockaddr_un masterListenAddr; /* Echo server address */
char buffer[256]; /* Buffer for echo string */
unsigned int n; /* Length of string to echo */
sock = socket(AF_UNIX, SOCK_STREAM, 0));
bzero((char *) &masterListenAddr, sizeof(masterListenAddr));
masterListenAddr.sun_family = AF_UNIX;
strcpy(masterListenAddr.sun_path, "./socketfile");
/* Establish the connection to the echo server */
printf("connect to master ...\n");
connect(sock, (struct sockaddr *) &masterListenAddr, sizeof(masterListenAddr));
/* Send the string to the master server */
strcpy(buffer, READY);
n = 5; /* Determine input length */
printf("send 'READY' to listener ...\n");
write(sock, buffer, n);
printf("wait to receive FD ...\n");
userClientSock = recv_fd(sock);
close(sock);
strcpy(buffer, READY);
n = 5; /* Determine input length */
printf("send READY to client ...\n");
write(userClientSock, buffer, n);
while (1)
{
int i=0;
n = read(userClientSock, buffer, RCVBUFSIZE - 1));
printf("receive message from client: %s", buffer); /* Print the echo buffer */
for (i=0; i < n; i++)
buffer[i] = toupper(buffer[i]);
if (strncmp(buffer, "QUIT", 4) == 0 )
break;
write(userClientSock, buffer, n));
}
close(userClientSock);
exit(0);
}
int recv_fd(int fd)
{
int c = 0;
struct msghdr msg;
struct iovec iov[1];
int n;
int newfd;
union{
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
}control_un;
struct cmsghdr *cmptr;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov[0].iov_base = &c;
iov[0].iov_len = 1;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
if((n = recvmsg(fd, &msg, 0)) <= 0)
{
return n;
}
cmptr = CMSG_FIRSTHDR(&msg);
if((cmptr != NULL) && (cmptr->cmsg_len == CMSG_LEN(sizeof(int))))
{
if(cmptr->cmsg_level != SOL_SOCKET)
{
printf("control level != SOL_SOCKET/n");
exit(-1);
}
if(cmptr->cmsg_type != SCM_RIGHTS)
{
printf("control type != SCM_RIGHTS/n");
exit(-1);
}
int recvfd = *((int*)CMSG_DATA(cmptr));
printf("recv FD %d\n", recvfd);
return recvfd;
}
else
{
if(cmptr == NULL) printf("null cmptr, fd not passed./n");
else printf("message len[%d] if incorrect./n", cmptr->cmsg_len);
return -1; // descriptor was not passed
}
return -1;
}
Client.c
/**
* Usage: client $PORT $WORKER
* For example: ./client 12345 UpperServer
*/
int main(int argc, char *argv[])
{
int sock;
struct sockaddr_in sock_addr;
struct hostent *server;
char buffer[256];
int n;
assert (argc == 3);
sock = socket(AF_INET, SOCK_STREAM, 0);
server = gethostbyname("localhost");
bzero((char *) &sock_addr, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *)&sock_addr.sin_addr.s_addr, server->h_length);
sock_addr.sin_port = htons(atoi(argv[1]));
printf("connect to server %s...\n",argv[1]);
connect(sock, (struct sockaddr *) &sock_addr,sizeof(sock_addr));
strcpy(buffer, argv[2]);
n = strlen(argv[2]); /* Determine input length */
write(sock, buffer, n);
printf("waiting worker '%s' to startup ...\n", argv[2]);
n = read(sock, buffer, 255));
assert(strncmp(buffer, READY, strlen(READY)) != 0);
/* Begin to receive the same string back from the server */
while (1)
{
printf("Please enter the message(quit for quit): ");
bzero(buffer,256);
fgets(buffer,255,stdin);
if (strncmp(buffer, "quit", strlen("quit")) == 0)
break;
n = write(sock, buffer, strlen(buffer));
bzero(buffer,256);
n = read(sock, buffer, 255);
printf("Receive message from server: %s",buffer);
}
close(sock);
return 0;
}