Simple IPC by Socket on Linux

时间:2021-04-02 16:14:22

Simple IPC by Socket on Linux

Communicate between process on the same host or different hosts.

  • On the same host:  Use INET socket or UNIX socket
  • On different hosts: Use UNIX socket

   

Head File [sock.h]

#ifndef __SOCK_H
#define __SOCK_H

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <stddef.h>
#include <string.h>

/* ===== API for simple INET/UNIX socket ===== */


/*
   Create a server
   If port is valid, create an INET server.
   Otherwise, create an UNIX server.
   Return serverfd for OK, negetive value for ERR
   Example:
       [INET Server]
       int serverfd;
       if ((serverfd = sock_server(2333, NULL, 5)) < 0)
       {
           prinf("Error: fail to create server\n");
       }
       [UNIX Server]
       int serverfd;
       if ((serverfd = sock_server(-1, "test.sock", 5)) < 0)
       {
           prinf("Error: fail to create server\n");
       }
*/
int sock_server(int port, const char *sockfile, int max_connection);

/*
   Accept a client and create a session
   Return sessionfd for OK, negetive value for ERR
   Example:
       int sessionfd;
       if ((sessionfd = sock_accept(serverfd)) < 0)
       {
           prinf("Error: fail to accept client\n");
       }
*/
int sock_accept(int serverfd);

/*
   Create a client connected to a server
   If host and port are valid, create an INET client.
   Otherwise, create an UNIX client.
   Return clientfd for OK, negetive value for ERR
   Example:
       [INET Client]
       int clientfd;
       if ((clientfd = sock_client("127.0.0.1", 2333, NULL)) < 0)
       {
           printf("Error: fail to create client\n");
       }
       [UNIX Client]
       int clientfd;
       if ((clientfd = sock_client(NULL, -1, "test.sock")) < 0)
       {
           prinf("Error: fail to create client\n");
       }
*/
int sock_client(const char *host, int port, const char *sockfile);

/*
   Close a server/client/session
   Example:
       close(clientfd);
       close(sessionfd);
       close(serverfd);
*/
void sock_close(int fd);


/*
   Send message
   Return message length sent for OK, negetive value for ERR
   Example:
       if ((sock_send(clientfd, msg, msg_len) < 0)
       {
           printf("Error: fail to send message\n");
       }
*/
ssize_t sock_send(int sockfd, const void *buf, size_t len);

/*
   Receive message
   Return message length received for OK, negetive value for ERR
   Example:
       if ((msg_len = sock_recv(sessionfd, buf, MAX_LEN) < 0)
       {
           printf("Error: fail to receive message\n");
       }
*/
ssize_t sock_recv(int sockfd, void *buf, size_t len);

/*
   Sock Example:

   [Sever]
	int serverfd;
	int sessionfd;
	char buf[512];
	int len;
	if ((serverfd = sock_server(-1, "test.sock", 5)) < 0)
	{
		exit(-1);
	}
	if ((sessionfd = sock_accept(serverfd)) < 0)
	{
		exit(-2);
	}
	if ((len = sock_recv(sessionfd, buf, 512)) > 0)
	{
		buf[len] = 0;
		printf("Recv: %s\n", buf);
	}
	sock_close(sessionfd);
	sock_close(serverfd);	

   [Client]
	int clientfd;
	char buf[512];
	int len;
	if ((clientfd = sock_client(NULL, -1, "test.sock")) < 0)
	{
		exit(-1);
	}
	sprintf(buf, "Hello world");
	len = strlen(buf);
	if ((len = sock_send(clientfd, buf, len)) > 0)
	{
		printf("Send: %s\n", buf);
	}
	sock_close(clientfd);
*/

#endif

Source File [sock.c]

#include "sock.h"

int sock_server(int port, const char *sockfile, int max_connection)
{
	int domain;
	struct sockaddr *addr;
	int serverfd;
	struct sockaddr_in in;
	struct sockaddr_un un;
	int len;
	
	/* Parse socket type  */
	if (port >= 0)
	{
		domain = AF_INET;
		addr = (struct sockaddr *) ∈
		memset(&in, 0, sizeof(in));
		in.sin_family = AF_INET;
		in.sin_addr.s_addr = htonl(INADDR_ANY);
		in.sin_port = htons(port);
		len = sizeof(in);	
	}
	else if (sockfile != NULL)
	{
		domain = AF_UNIX;
		addr = (struct sockaddr *) &un;
		/* Remove old file if it exists */
		unlink(sockfile);
		memset(&un, 0, sizeof(un));
		un.sun_family = AF_UNIX;
		strcpy(un.sun_path, sockfile);
		len = offsetof(struct sockaddr_un, sun_path) + strlen(sockfile);
	}
	else
	{
		return -1;
	}
	/* Create socket */
	if ((serverfd = socket(domain, SOCK_STREAM, 0)) < 0)
	{
		return -2;
	}
	/* Bind socket */
	if (bind(serverfd, addr, len) < 0)
	{
		close(serverfd);
		return -3;
	}
	/* Start listening */
	if (listen(serverfd, max_connection) < 0)
	{
		close(serverfd);
		return -4;
	}

	return serverfd;
}

int sock_accept(int serverfd)
{
	int sessionfd;
	/* Accept client connection */
	if ((sessionfd = accept(serverfd, NULL, NULL)) < 0)
	{
		return -1;
	}

	return sessionfd;
}

int sock_client(const char *host, int port, const char *sockfile)
{
	int domain;
	int clientfd;
	struct sockaddr *addr;
	struct sockaddr_in in;
	struct hostent *h;
	struct sockaddr_un un;
	int len;

	/* Parse socket type */
	if (host != NULL && port > 0)
	{
		domain = AF_INET;
		addr = (struct sockaddr *) ∈
		memset(&in, 0, sizeof(in));
		in.sin_family = AF_INET;
		/* Read ip */
		if (inet_pton(AF_INET, host, &in.sin_addr) <= 0)
		{
			/* Failed to read ip from host, try gethostbyname */
			if ((h = gethostbyname(host)) == NULL)
			{
				return -1;
			}
			memcpy(&in.sin_addr.s_addr, h->h_addr, 4);
		}
		in.sin_port = htons(port);
		len = sizeof(in);
	}
	else if (sockfile != NULL)
	{
		domain = AF_UNIX;
		addr = (struct sockaddr *) &un;
		memset(&un, 0, sizeof(un));
		un.sun_family = AF_UNIX;
		strcpy(un.sun_path, sockfile);
		len = offsetof(struct sockaddr_un, sun_path) + strlen(sockfile);
	}
	/* Create socket */
	if ((clientfd = socket(domain, SOCK_STREAM, 0)) < 0 )
	{
		return -2;
	}
	/* Connect server */
	if (connect(clientfd, addr, len) < 0)
	{
		close(clientfd);
		return -3;
	}

	return clientfd;
}

void sock_close(int fd)
{
	close(fd);
}

ssize_t sock_send(int sockfd, const void *buf, size_t len)
{
	return send(sockfd, buf, len, 0);
}

ssize_t sock_recv(int sockfd, void *buf, size_t len)
{
	return recv(sockfd, buf, len, 0);
}

Demo File [ever.c]

#include "sock.h"
#include <unistd.h>

static char *SOCK_NAME = "test.sock";

void start_daemon(int port)
{
	int serverfd;
	int sessionfd;
	char buf[512];
	int len;

	printf("Start\n");
	if ((serverfd = sock_server(port, SOCK_NAME, 5)) < 0)
	{
		printf("Error[%5d]: fail to create server\n", serverfd);
		return;
	}
new_session:
	if ((sessionfd = sock_accept(serverfd)) < 0 )
	{
		printf("Error[%5d]: fail to accept client\n", sessionfd);
		return;
	}
	dup2(sessionfd, STDOUT_FILENO);
	if ((len = sock_recv(sessionfd, buf, 512)) > 0)
	{
		buf[len] = 0;
		printf("Recv: %s\n", buf);
	}
	sock_close(sessionfd);
	if (strncmp(buf, "stop", 4))
		goto new_session;
	sock_close(serverfd);
	printf("Stop\n");
}

void send_cmd(char *cmd, const char *host, int port)
{
	int clientfd;
	int len;
	char buf[512];
	
	if ((clientfd = sock_client(host, port, SOCK_NAME)) < 0)
	{
		printf("Error[%5d]: fail to create client\n", clientfd);
		return;
	}
	len = strlen(cmd);
	len = sock_send(clientfd, cmd, len);
	printf("Send %s [len: %d]\n", cmd, len);
	if ((len = sock_recv(clientfd, buf, 512)) > 0)
	{
		buf[len] = 0;
		printf("Recv: %s\n", buf);
	}
	sock_close(clientfd);
}

void stop_daemon(const char *host, int port)
{
	send_cmd("stop", host, port);
}

int main(int argc, char *argv[])
{
	char *host = "127.0.0.1";
	int port = 6666;
	if (argc < 2)
	{
		printf("usage: %s <cmd> [ip port]", argv[0]);
		return -1;
	}

	if (!strcmp(argv[1], "start"))
	{
		if (argc > 2)
			port = atoi(argv[2]);
		start_daemon(port);
	}
	else if (!strcmp(argv[1], "stop"))
	{
		if (argc > 3)
		{
			host = argv[2];
			port = atoi(argv[3]);
		}
		stop_daemon(host, port);
	}
	else
	{
		if (argc > 3)
		{
			host = argv[2];
			port = atoi(argv[3]);
		}
		send_cmd(argv[1], host, port);
	}
	return 0;
}

Build & Run

Build [Makefile]

CC := gcc

TARGETS := ever

all: $(TARGETS)

ever: ever.o sock.o
	$(CC) -o $@ $^

%.o: %.c
	$(CC) -c -o $@ $<

clean:
	-rm $(TARGETS)
	-rm *.o

Run

Run server on one terminal

$ ./ever start

Run client on another terminal

On the same host:

$ ./ever message

On a different host:

$ ./ever message xxx.xxx.xxx.xxx 6666