TCP Socket Class 2

Expanding my webserver is getting more complicated. I’ve decided to port it to C++, where the use of classes will help me organize my code. The first step I have accomplished in this process is the creation of a tcp socket class.

This class exports functions similar to that of file I/O functions found from the Standard C Library, except the I/O operations are performed on the TCP socket instead of files.

Despite the cross-platform code, this library doesn’t seem to work on Windows. It works perfectly on Linux though.

The class:

#if defined(_WIN32)
#include <WinSock2.h>
#include <WS2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
#elif defined(linux)
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif

#include <cstdio>
#include <cstdarg>
#include <cstdlib>
#include <cstring>
#include <cctype>

class fsock {
public:
	fsock();
	~fsock();
#if defined(_WIN32)
	SOCKET sock;
#elif defined(linux)
	int sock;
#endif
	char* eol;
	size_t eol_len;

	int connect(const char* host, const char* port);
	int listen(const char* port, int backlog);
	int accept(fsock& f);
	int close();

	int send(const char* buf, size_t len);
	int recv(char* buf, size_t len);
	
	const char* getpeerip(char* s);
	char* getpeername(char* s);

	int getc();
	int gets(char* s);
	int printf(const char* format, ...);
	int putc(int c);
	int puts(const char* s);
	int read(char* ptr, size_t size, size_t nmemb);
	void scanf(const char* format, ...);
	int write(const char* ptr, size_t size, size_t nmemb);
	int peek(char* ptr, size_t size, size_t nmemb);
	
};

fsock::fsock() {
#if defined(_WIN32)
	struct WSAData* wd = (struct WSAData*)malloc(sizeof(struct WSAData));
	WSAStartup(MAKEWORD(2, 0), wd);
	free(wd);
#endif
	eol = (char*)malloc(512);
	strcpy(eol, "\r\n");

	eol_len = 2;
}

fsock::~fsock() {
#if defined(_WIN32)
	WSACleanup();
#endif
	free(eol);
}

int fsock::connect(const char* host, const char* port) {
	struct addrinfo *ai, hints;
	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	if (getaddrinfo(host, port, &hints, &ai)) return 0;
	sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
	if (::connect(sock, ai->ai_addr, ai->ai_addrlen))
		{ freeaddrinfo(ai); return 0; }
	freeaddrinfo(ai);
	return 1;
}

int fsock::listen(const char* port, int backlog) {
	struct addrinfo *ai, hints;
	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	hints.ai_flags = AI_PASSIVE;
	if (getaddrinfo(0, port, &hints, &ai)) return 0;
	sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
	if (bind(sock, ai->ai_addr, ai->ai_addrlen))
		{ freeaddrinfo(ai); return 0; }
	freeaddrinfo(ai);
	if (::listen(sock, backlog)) return 0;
	return 1;
}

int fsock::accept(fsock& f) {
	f.sock = ::accept(sock, 0, 0);
	if (f.sock == -1) return 0;
	else return 1;
}

int fsock::close() {
#if defined(_WIN32)
	shutdown(sock, SD_BOTH);
	if (!closesocket(sock)) return 1;
	else return 0;
#else
	shutdown(sock, SHUT_RDWR);
	if (!::close(sock)) return 1;
	else return 0;
#endif
}

int fsock::send(const char* buf, size_t len) { return ::send(sock, buf, len, 0); }
int fsock::recv(char* buf, size_t len) { return ::recv(sock, buf, len, 0); }

const char* fsock::getpeerip(char* s) {
	struct sockaddr_storage ss;
	socklen_t sslen = sizeof(struct sockaddr_storage);
	if (::getpeername(this->sock, (struct sockaddr*)&ss, &sslen)) return 0;
	if (ss.ss_family == AF_INET)
		return inet_ntop(ss.ss_family, &((struct sockaddr_in*)&ss)->sin_addr, s, 40);
	else if (ss.ss_family == AF_INET6)
		return inet_ntop(ss.ss_family, &((struct sockaddr_in6*)&ss)->sin6_addr, s, 40);
	return s;
}

char* fsock::getpeername(char* s) {
	struct sockaddr sa;
	socklen_t slen = sizeof(struct sockaddr);
	if (::getpeername(sock, &sa, &slen)) return 0;
	if (getnameinfo(&sa, slen, s, 256, 0, 0, 0)) return 0;
	return s;
}

int fsock::getc() {
	char c;
	if (::recv(sock, &c, 1, 0) > 0) return (int)c;
	else return -1;
}

int fsock::gets(char* s) {
	int i;
	for (
		i = 0, s[0] = 0;
		(i >= eol_len) ? strncmp(s + i - eol_len, eol, eol_len) : 1;
		::recv(sock, s + (i++), 1, 0), s[i] = 0
	);
	return --i;
}

int fsock::printf(const char* format, ...) {
	int i;
	char* buf = (char*)malloc(512);
	va_list vl;
	va_start(vl, format);
	vsprintf(buf, format, vl);
	i = ::send(sock, buf, strlen(buf), 0);
	va_end(vl);
	free(buf);
	return i;
}

int fsock::putc(int c) {
	if (::send(sock, (char*)&c, 1, 0) > 0) return c;
	else return -1;
}

int fsock::puts(const char* s) { return ::send(this->sock, s, strlen(s), 0); }

int fsock::read(char* ptr, size_t size, size_t nmemb) {
	int i;
	for (i = 0; i < nmemb; ptr = ptr + size, i++)
		if (::recv(sock, ptr, size, 0) == -1) break;
	return i;
}

void fsock::scanf(const char* format, ...) {
	char* buf = (char*)malloc(512);
	char* s;
	va_list v;
	int i, width;
	char ignore, mod;
	va_start(v, format);
	while (format = strchr(format, '%')) {
		while (1) {
			::recv(sock, &ignore, 1, MSG_PEEK);
			if (!isspace(ignore)) break;
			else ::recv(sock, &ignore, 1, 0);
		}
		format = format + 1;
		ignore = 0;
		mod = 0;
		width = -1;
		if (*format == '*') { ignore = 1; format = format + 1; }
		if (isdigit(*format)) {
			sscanf(format, "%d", &width);
			for (; isdigit(*format); format = format + 1);
		}
		if (*format == 'h' || *format == 'l' || *format == 'L') {
			mod = *format;
			format = format + 1;
		}
		switch (*format) {
		case 'c': 
			if (ignore) s = &mod;
			else s = va_arg(v, char*);
			::recv(sock, va_arg(v, char*), 1, 0);
			break;
		case 's': 
			if (ignore) s = &mod;
			else s = va_arg(v, char*);
			for (i = 0; width == -1 ? 1 : (i < width);) {
				::recv(sock, s + i, 1, 0);
				if (isspace(s[i])) break;
				if (!ignore) i++;
			}
			s[i] = 0;
			break;
		case 'd':
			buf[0] = '0';
			for (i = 0; width == -1 ? 1 : (i < width); i++) {
				::recv(sock, buf + i, 1, MSG_PEEK);
				if (isdigit(buf[i]) || (buf[i] == '+' && !i) || (buf[i] == '-' && !i))
					::recv(sock, buf + i, 1, 0);
				else break;
			}
			buf[i] = 0;
			if (ignore) break;
			switch (mod) {
			case 0: sscanf(buf, "%d", va_arg(v, int*)); break;
			case 'h': sscanf(buf, "%hd", va_arg(v, short int*)); break;
			case 'l': sscanf(buf, "%ld", va_arg(v, long int*)); break;
			}
			break;
		case 'e':case 'E':case 'f':case 'g':case 'G':
			buf[0] = '0';
			for (i = 0; width == -1 ? 1 : (i < width); i++) {
				::recv(sock, buf + i, 1, MSG_PEEK);
				if (
					isdigit(buf[i]) ||
					(buf[i] == '+' && !i) ||
					(buf[i] == '-' && !i) ||
					(buf[i] == '.' && (strchr(buf, '.') == buf + i))
				)
					::recv(sock, buf + i, 1, 0);
				else break;
			}
			buf[i] = 0;
			if (ignore) break;
			switch (mod) {
			case 0: sscanf(buf, "%f", va_arg(v, float*)); break;
			case 'l': sscanf(buf, "%lf", va_arg(v, double*)); break;
			case 'L': sscanf(buf, "%Lf", va_arg(v, long double*)); break;
			}
			break;
		case 'o':
			buf[0] = '0';
			for (i = 0; width == -1 ? 1 : (i < width); i++) {
				::recv(sock, buf + i, 1, MSG_PEEK);
				if (isdigit(buf[i]))
					::recv(sock, buf + i, 1, 0);
				else break;
			}
			buf[i] = 0;
			if (ignore) break;
			switch (mod) {
			case 0: sscanf(buf, "%o", va_arg(v, int*)); break;
			case 'h': sscanf(buf, "%ho", va_arg(v, unsigned short int*)); break;
			case 'l': sscanf(buf, "%lo", va_arg(v, unsigned long int*)); break;
			}
			break;
		case 'u':
			buf[0] = '0';
			for (i = 0; width == -1 ? 1 : (i < width); i++) {
				::recv(sock, buf + i, 1, MSG_PEEK);
				if (isdigit(buf[i]))
					::recv(sock, buf + i, 1, 0);
				else break;
			}
			buf[i] = 0;
			if (ignore) break;
			switch (mod) {
			case 0: sscanf(buf, "%d", va_arg(v, int*)); break;
			case 'h': sscanf(buf, "%hd", va_arg(v, unsigned short int*)); break;
			case 'l': sscanf(buf, "%ld", va_arg(v, unsigned long int*)); break;
			}
			break;
		case 'x':
			buf[0] = '0';
			for (i = 0; width == -1 ? 1 : (i < width); i++) {
				::recv(sock, buf + i, 1, MSG_PEEK);
				if (
					isdigit(buf[i]) ||
					(buf[i] >= 'A' && buf[i] <= 'F') ||
					(buf[i] >= 'a' && buf[i] <= 'f') ||
					(tolower(buf[i]) == 'x' && strchr(buf, buf[i]) == buf + i)
				)
					::recv(sock, buf + i, 1, 0);
				else break;
			}
			buf[i] = 0;
			if (ignore) break;
			switch (mod) {
			case 0: sscanf(buf, "%x", va_arg(v, int*)); break;
			case 'h': sscanf(buf, "%hx", va_arg(v, unsigned short int*)); break;
			case 'l': sscanf(buf, "%lx", va_arg(v, unsigned long int*)); break;
			}
			break;
		}
	}
	va_end(v);
	free(buf);
}

int fsock::write(const char* ptr, size_t size, size_t nmemb) {
	int i;
	for (i = 0; i < nmemb; ptr = ptr + size, i++)
		if (::send(sock, ptr, size, 0) == -1) break;
	return i;
}

int fsock::peek(char* ptr, size_t size, size_t nmemb) {
	int i;	
	if ((i = ::recv(sock, ptr, size * nmemb, MSG_PEEK)) == -1)
		return 0;
	else return i;
}

The documentation:

Jakash3's fsock class.
Documentation for a TCP socket wrapper for C++.

=================
Public variables
=================

int sock;
   The socket descriptor associated with the class.
char* eol;
   Pointer to buffer containing end of line sequenc   e.
size_t eol_len;
   Size in bytes of the end of line sequence.

=================
Functions
=================
int connect(const char* host, const char* port);

   Description:
   Connects the socket to specified host on specified port or service.
   host can be a hostname, or ipv4/ipv6 address.
   port can be a port number in string format or a service name such
   as "http".

   Return value: 1 on success, 0 on error.

int listen(const char* port, int backlog);

   Description:
   Binds and listens on specified port/service.
   port can be a port number in string format or a service name.
   backlog defines the maximum length of pending connections allowed
   on this socket.

   Return value: 1 on success, 0 on error.

int accept(fsock&; f);

   Description:
   Accepts a pending connection from connection queue of associated
   socket and assigns the connected socket to specified fsock class.

   Return value:
   1 on success, 0 for error and errno set according to what the POSIX
   accept function assigned to it.

int close();

   Description:
   Shutsdown read and write access and closes the associated socket
   descriptor.

   Return value: 1 on success, 0 on error.

int send(const char* buf, size_t len);

   Description:
   Calls POSIX send function with no flags. Sends data specified by
   buf with a length of len over the associated socket.

   Return value: Returns number of characters sent or -1 on error.
   errno set appropriately on error. (See man send 2)

int recv(char* buf, size_t len)

   Description:
   Calls POSIX recv functions with no flags. Receives a maximum of
   len bytes into buf from the associated socket.

   Return value: Returns number of characters read or -1 on error.
   errno set appropriately on error. (See man recv 2)

const char* getpeerip(char* s);

   Description:
   Stores the string format of the IP address of the peer connected to
   associated socket in buffer s.

   Return value: Returns s on success or 0 on error.

const char* getpeername(char* s);

   Description:
   Gets the hostname associated with the peer connected to associated
   socket. hostname stored in s.

   Return value: Returns s on success or 0 on error.

int getc();

   Description:
   Reads and returns 1 character from the associated socket.

   Return value:
   Returns the character read casted to an int or -1 on error.

int gets(char* s);

   Description:
   Reads data from associated socket into s until the eol sequence is
   detected (as specified from global variables eol and eol_len).

   Return value: This blocking function returns the number of bytes read.

int printf(const char* format, ...);

   Description:
   Sends formatted data over associated socket. See man 3 printf for details.

   Return value: Returns number of formatted elements written.

int putc(int c);

   Description:
   Sends a character over the associated socket.

   Return value: Returns the character written or -1 on error.

int read(char* ptr, size_t size, size_t nmemb);

   Description:
   Reads nmemb elements of data, each size bytes long, from the associated
   socket.

   Return value:
   This blocking function returns the number of elements read.

void scanf(const char* format, ...);

   Description:
   Reads formatted data from associated socket. See man 3 scanf for details.

int write(const char* ptr, size_t size, size_t nmemb);

   Description:
   Writes nmemb elements of data, each size byte long, from the associated
   socket.

   Return value: Returns the number of elements written.

int peek(char* ptr, size_t size, size_t nmemb);

   Description:
   Peeks at nmemb elements of data, each size bytes long, from the associated
   socket without extracting them from the input socket data queue.

   Return value: This blocking function returns the number of elements read.

A sample program:

#include "fsock.cpp"

int main(int argc, char** argv) {
   int a, b;
   fsock f, g;
   f.listen("7654", 5);
   f.accept(g);
   g.puts("Enter an integer: ");
   g.scanf("%d", &a);
   g.puts("Enter another integer: ");
   g.scanf("%d", &b);
   g.printf("The sum is %d\n", a + b);
   g.close();
   f.close();
   return 0;
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: