Česky
Kamil Dudka

Tiny programs (C, C++, C#, ...)

File detail

Name:Downloadnetio.c [Download]
Location: tiny > IPK > trunk
Size:5.0 KB
Last modification:2022-09-09 13:06

Source code

/*
 * file: netio.c - TCP layer
 * project: BSD Sockets (IPK)
 * By Kamil Dudka, xdudka00
 * mailto: xdudka00@gmail.com
 */
 
#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <ctype.h>
#include <sys/socket.h>
 
#include "netio.h"
 
/*
 * TCP I/O private data (initialized by netLineInit ())
 */
struct NetLine {
	unsigned defLineLength;
	char *pBuff;
	TSocket sockfd;
};
 
static TSocket listenfd;
static volatile sig_atomic_t bTerm = 0;
 
static inline int readChar (int sockfd, char *);
static inline int writeBlock (int sockfd, const char *, size_t);
 
/*
 * SIGTERM signal handler
 */
void sigTermHandle (int i) {
	i = 0;
	bTerm = 1;
	close (listenfd);
	printf ("server: exit (SIGTERM caught)\n");
}
 
/*
 * SIGCHLD signal handler
 */
void sigChldHandle (int i) {
	wait (&i);
}
 
/*
 * Start concurent server
 * procAddr - CallBack proc for incomming connection
 * return only if an error occur
 */
int netListen (unsigned portNumber, unsigned listenQueue, PAcceptProc procAddr) {
	// Set signal handling
	signal (SIGTERM, sigTermHandle);
	signal (SIGCHLD, sigChldHandle);
	sigset_t sigMask;
	sigemptyset (&sigMask);
	sigaddset (&sigMask, SIGTERM);
	sigaddset (&sigMask, SIGCHLD);
	sigprocmask (SIG_UNBLOCK, &sigMask, NULL);
 
	// Create socket
	listenfd = socket (PF_INET, SOCK_STREAM, 0);
	if (0> listenfd)
		return 1;
 
	// Bind port
	struct sockaddr_in sa;
	bzero (&sa, sizeof (sa));
	sa.sin_family = PF_INET;
	sa.sin_addr.s_addr = htonl (INADDR_ANY);
	sa.sin_port = htons (portNumber);
	if (0> bind (listenfd, (struct sockaddr*) &sa, sizeof (sa))) {
		fprintf (stderr, "server: error: Could not bind\n");
		close (listenfd);
		return 1;
	}
 
	// Listen
	if (listen (listenfd, listenQueue))
		return 1;
 
	// Accept and reply
	socklen_t cbClAddr = sizeof (sa);
	while (!bTerm) {
		int connfd = accept (listenfd, (struct sockaddr*) &sa, &cbClAddr);
		if (0> connfd)
			break;
		if (0== fork ()) {
			// Child process
			close (listenfd);
 
			// Call call-back procedure and exit
			exit (procAddr (connfd));
		}
		close (connfd);
	}
 
	return 0;
}
 
/*
 * Connect to remote server
 * Set sockfd in *pSock
 * Return 0 if no error occur
 */
int netConnect (const char *serverName, unsigned portNumber, TSocket *pSock) {
	// Server address
	struct sockaddr_in sa;
	bzero (&sa, sizeof (sa));
	sa.sin_family = PF_INET;
	sa.sin_port = htons (portNumber);
 
	// Deteremine type of addr (IP/URL)
	if (isdigit(*serverName)) {
		// Direct IP adress
		if (0>= inet_pton (PF_INET, serverName, &sa.sin_addr)) {
			fprintf (stderr, "client: error: Invalid address\n");
			return 1;
		}
	} else {
		// NS-lookup
		struct hostent *pHost;
		if (NULL== (pHost= gethostbyname (serverName))) {
			fprintf (stderr, "client: error: NS-lookup failed\n");
			return 1;
		}
		memcpy (&sa.sin_addr, pHost->h_addr_list[0], pHost->h_length);	
	}
 
	// Create socket
	if (0> (*pSock = socket (PF_INET, SOCK_STREAM, 0))) {
		return 1;
	}
 
	// Connect to server
	if (0!= connect (*pSock, (const struct sockaddr*) &sa, sizeof (sa))) {
		fprintf (stderr, "client: error: connect failed\n");
		close (*pSock);
		return 1;
	}
 
	return 0;
}
 
/*
 * Close opened socket
 */
void netClose (TSocket socket) {
	close (socket);
}
 
/*
 * Initialize TCP I/O
 */
PNetLine netLineInit (TSocket socket, unsigned defLineLength) {
	PNetLine pNL = malloc (sizeof (TNetLine));
	if (NULL== pNL)
		return NULL;
 
	pNL->pBuff = NULL;
	pNL->sockfd = socket;
	pNL->defLineLength = defLineLength;
 
	return pNL;
}
 
/*
 * Destroy TCP I/O
 */
void netLineDestroy (PNetLine pNL) {
	free (pNL->pBuff);
	free (pNL);
}
 
/*
 * Read one line from other side
 */
int netLineGet (PNetLine pNL, char **szText) {
	// Initial allocation
	int size = pNL->defLineLength;
	free (pNL->pBuff);
	pNL->pBuff = malloc (size);
 
	// Read one line
	int cbWritten=0;
	while ( 0<readChar (pNL->sockfd, pNL->pBuff + cbWritten) && pNL->pBuff[cbWritten] != '\n')
		if (++cbWritten >= size) {
			size <<= 1;
			if (NULL== (pNL->pBuff= realloc (pNL->pBuff, size) ))
				return -1;
		}
	if (pNL->pBuff [cbWritten] != '\n')
		// Error during reading
		return -1;
 
	// End with zero end return string
	pNL->pBuff [cbWritten] = '\0';
	*szText = pNL->pBuff;
 
	// All ok
	return 0;
}
 
/*
 * Send plain text to other side
 */
int netPut (PNetLine pNL, const char *szText) {
	if (0> writeBlock (pNL->sockfd, szText, strlen (szText)))
		return -1;
 
	return 0;
}
 
/*
 * Read one char from socket
 */
static inline int readChar (int sockfd, char *pTo) {
	while (0> read (sockfd, pTo, 1))
		if (errno != EINTR)
			return -1;
	return 1;
}
 
/*
 * Write block to socket
 */
static inline int writeBlock (int sockfd, const char *pBuff, size_t size) {
	int cbWritten = 0;
	while (size > 0) {
		if (0>= (cbWritten= write (sockfd, pBuff, size) )) {
			if (cbWritten<0 && errno==EINTR)
				cbWritten = 0;
			else
				return -1;
		}
		size -= cbWritten;
		pBuff += cbWritten;
	}
 
	return cbWritten;
}