Blob Blame Raw
#ifndef TCP_H
#define TCP_H


#include <atomic>
#include <thread>
#include <vector>
#include <list>
#include <mutex>

#include "error.h"


enum : ErrorCode {
	ERR_TCP_COMMON = ERR_TCP,
	ERR_TCP_RESOLVE_FAILED,
	ERR_TCP_BAD_ADDRESS,
	ERR_TCP_BAD_HANDLER,
	ERR_TCP_CONNECTION_FAILED,
	ERR_TCP_CONNECTION_LOST,
	ERR_TCP_LISTEN_FAILED,
	ERR_TCP_LISTEN_LOST,
};


namespace Tcp {

enum Status {
	STATUS_NONE,
	STATUS_OPEN,
	STATUS_CLOSED,
	STATUS_LOST,
};


typedef int SockId;


class Address {
public:
	unsigned char ip[4];
	unsigned short port;
	
	inline Address():
		ip(), port() { }
	inline Address(unsigned char ip0, unsigned char ip1, unsigned char ip2, unsigned char ip3, unsigned short port):
		port(port) { ip[0] = ip0, ip[1] = ip1, ip[2] = ip2, ip[3] = ip3; }
	inline Address(unsigned char *ip, unsigned short port):
		Address(ip[0], ip[1], ip[2], ip[3], port) { }
	
	inline bool isValidIp() const
		{ return ip[0] || ip[1] || ip[2] || ip[3]; }
	inline bool isValidPort() const
		{ return port; }
	inline bool isValid() const
		{ return isValidIp() && isValidPort(); }
		
	ErrorCode resolve(const char *addrString);
};


class Connection {
private:
	Status status;
	ErrorCode lastError;
	SockId sockId;
	Address remoteAddr;
	std::atomic<bool> stopping;
	
	Connection(const Connection&) = delete;
	Connection& operator=(const Connection&) = delete;

public:
	Connection();
	Connection(SockId sockId, const Address &remoteAddr);
	~Connection();
	
	inline Status getStatus() const
		{ return status; }
	ErrorCode getLastError() const
		{ return lastError; }
	inline SockId getSockId() const
		{ return sockId; }
	inline const Address& getRemoteAddr() const
		{ return remoteAddr; }
	
	inline void stop()
		{ stopping = true; }
	inline bool check() {
		if (stopping) close(ERR_TCP_CONNECTION_LOST);
		return status == STATUS_OPEN;
	}
	
	bool open(const Address &remoteAddr);
	void close(ErrorCode errorCode = ERR_NONE);
	
	bool read(void *data, size_t size);
	bool write(const void *data, size_t size);
};


class Handler {
public:
	virtual ~Handler();
	virtual void handleTcpConnection(Connection &connection) = 0;
};


class Server {
public:
	struct ConnDesc {
		Connection *connection;
		std::thread *thread;
	};
	typedef std::list<ConnDesc> ConnList;
	typedef std::vector<ConnDesc> ConnVector;
	
private:
	Status status;
	ErrorCode lastError;
	SockId sockId;
	Address localAddr;
	Handler *handler;
	std::thread *thread;
	std::atomic<bool> stopping;
	
	std::mutex connectionsMutex;
	ConnList connections;
	ConnVector connectionsFinished;
	
	Server(const Server&) = delete;
	Server& operator=(const Server&) = delete;
	
	void cleanConnections();
	void handleConnection(ConnList::iterator iter);
	void listen();
	
public:
	Server();
	~Server();
	
	inline Status getStatus() const
		{ return status; }
	ErrorCode getLastError() const
		{ return lastError; }
	inline SockId getSockId() const
		{ return sockId; }
	inline const Address& getLocalAddr() const
		{ return localAddr; }
	inline Handler* getHandler() const
		{ return handler; }
	
	inline bool check()
		{ return !stopping && status == STATUS_OPEN; }

	bool start(const Address &localAddr, Handler *handler);
	void join();
	void stop();
};

} // Tcp

#endif