Xedi.Xermawan's Blog

personal-technical blog

Posts Tagged ‘C/C++

Rendering animated GIF file

with 3 comments

gif file ( http://en.wikipedia.org/wiki/Graphics_Interchange_Format ) is very popular format , you can share funny animation, emotion, a splash for ads, etc. All web browser support this format. do you interested in how game or application (non-web) render animated gif ? . here my experiment result :

( set video quality to 480p  )

info :
rendering : DirectX 11
language : c/c++
GIF decoder : libnsgif

Written by XediXermawan

May 30, 2014 at 12:31 am

Posted in C/C++ Diary

Tagged with , , ,

tcp client server dengan winsock2

leave a comment »

Untuk keperluan selanjutnya di pengembangan game yang sedang saya buat ( 😮 ), saya memerlukan program yang bisa di tweak lewat program lain. Dengan kata lain 2 program yang saling bisa berkomunikasi. Cara yang bisa dicapai , pakai file sharing atau dengan cara yang pas : pakai networking tcp / udp . Kalau mau buat program dengan kemampuan networking, cara yang bijaksana mungkin pakai 3rd party library yang sudah cukup populer dan reliable. Di antaranya, yang saya ketahui dan sepertinya sudah terbukti bagus : boost-asio ( free ), raknet ( free untuk hobyist ), libevent ( free ), etc. ada yang lain ?. Tapi saya pinginnya ndak usah pakai 3rd party lib, lagipula program yang akan saya buat cukup sederhana. winsock2 & c/c++ . itu pilihannya. dan rasanya akan fun juga buat program nya hehe. Sebagai pemanasan awal dengan winsock2 ( saya pemula ) , saya perlu membuat program seperti ini : ada client dan server. client bisa kirim data ke server. server bisa kirim data ke client. jika client nya lebih dari satu, client bisa kirim data ke client yang lain. representasi dari program ini adalah program “chatting” :d .
kemampuan server dalam keadaan running :
– stand by & listening kalau ada client mau membuat koneksi.
– setiap ada client tersambung, server akan membuat handler yang menangani koneksi ke client tersebut berdasarkan socket id. ( dalam test program dipost ini, setiap ada client, server membuat thread baru, untuk menanganinya ).
– server bisa kirim data ke client lewat console
– ada > 3 thread . 1 main thread: untuk menangkap command line dari user, 1 thread untuk “stand by” / connection acceptor, dan n thread , untuk n client yang tersambung. ya sebenarnya ndak benar ( bisa di optimize pakai thread pool mungkin nantinya ).

kemampuan client dalam keadaan running :
– connect ke server
– kirim data ke server
– ada 2 thread, 1 main thread, 1 client handler thread ( data receiver & sender )
– port hardcoded : 22536

catatan :
[1] saya membuat socket mode ke non-blocking , artinya pemanggilan fungsi2 winsock seperti connect, recv, send tidak akan mem-blok jalannya program loop. jadi non-blocking & di thread lain selain main thread.
u_long iMode = 1;  // iMode=0, blocking mode, default value
ioctlsocket(ConnectSocket, FIONBIO, &iMode);
[2] untuk sharing data antar thread, saya menggunakan concurrent queue dari blog Anthony William http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html yang saya modifikasi menggunakan std library ( awalnya pakai boost )
[3] data yang dikirim berupa string.

Beberapa pertanyaan :
– apa cara di catatan no [1] ( non-blocking & di thread lain ) efektif ?
– saat memakai non-blocking mode, kalau mengirim data byte yang sangat besaarr , apakah semuanya terkirim langsung ATAU setelah beberapa call sent?

Reference :
MSDN winsock2 ( belum semuanya saya baca 😐 )

berikut code program chat client & server yang saya buat :

(server code) : ~288 baris

// xedi xermawan < edi.ermawan@gmail.com >
// May 27, 2014
// tcp client server networking test using winsock2
// server code

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <queue>

#include "concurrent_queue.hpp"

#pragma comment (lib, "ws2_32.lib")

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "22536"

struct XDclient_info
{
	int socket_id;
	struct sockaddr_in address;
};

class XDclient
{
public:
	XDclient(XDclient_info& clientinfo)
	{
		m_clientinfo = clientinfo;
		m_recvbuflen = DEFAULT_BUFLEN;
		m_islive = true;
		m_thread = new std::thread(&XDclient::run, this);
	}
	bool isLive()
	{
		return m_islive;
	}
	XDclient_info getinfo() const
	{
		return m_clientinfo;
	}
	~XDclient()
	{
		m_thread->join();
		delete m_thread;
	}
	std::string get_data()
	{
		std::string ret_str = "";
		if (!m_queue.empty())
		{
			m_queue.try_pop(ret_str);
		}
		return ret_str;
	}
private:
	XDclient_info m_clientinfo;
	std::thread* m_thread;
	char m_recvbuf[DEFAULT_BUFLEN];
	int m_recvbuflen;
	bool m_islive;
	concurrent_queue <std::string> m_queue;

	void run()
	{
		while (m_islive)
		{
			unsigned int result_recv = recv( m_clientinfo.socket_id, m_recvbuf, m_recvbuflen, 0);
			int nError = WSAGetLastError();
			if (nError != WSAEWOULDBLOCK&&nError != 0)
			{
				printf("Client Error: %d\n", nError);
				printf("Client disconnected: %d\n", nError);
				m_islive = false;
			}
			else if (result_recv != INVALID_SOCKET && result_recv!= 0)
			{
				m_recvbuf[result_recv] = '\0';
				std::string str_send(m_recvbuf);
				m_queue.push(str_send);
			}
			else if (result_recv == 0)
			{
				printf("Client disconnected: %d\n", nError);
				m_islive = false;
			}
			// give a sleep, avoid high percentage cpu usage
			Sleep(50);
		}
	}
};

class XDserver
{
public:
	XDserver()
	{
		m_recvbuflen = DEFAULT_BUFLEN;
		m_isrun = true;
		m_thread = new std::thread(&XDserver::run, this);
	}
	~XDserver()
	{
		m_isrun = false;
		m_thread->join();
		delete m_thread;
	}
	void run()
	{
		WSADATA wsaData;
		int result_ ;

		SOCKET listen_socket = INVALID_SOCKET;
		SOCKET client_socket = INVALID_SOCKET;

		struct addrinfo *result = NULL;
		struct addrinfo hints;

		char recvbuf[DEFAULT_BUFLEN];
		int recvbuflen = DEFAULT_BUFLEN;

		// init
		result_ = WSAStartup(MAKEWORD(2, 2), &wsaData);
		if (result_ != 0) {
			printf("WSAStartup failed with error: %d\n", result_);
		}

		ZeroMemory(&hints, sizeof(hints));
		hints.ai_family = AF_INET;
		hints.ai_socktype = SOCK_STREAM;
		hints.ai_protocol = IPPROTO_TCP;
		hints.ai_flags = AI_PASSIVE;

		// get info address for host
		result_ = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
		if (result_ != 0) {
			printf("getaddrinfo failed with error: %d\n", result_);
			WSACleanup();
		}
		// create socket
		listen_socket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
		if (listen_socket == INVALID_SOCKET) {
			printf("socket failed. error code : %ld\n", WSAGetLastError());
			freeaddrinfo(result);
			WSACleanup();
		}
		// set-up socket listening
		result_ = bind(listen_socket, result->ai_addr, (int)result->ai_addrlen);
		if (result_ == SOCKET_ERROR) {
			printf("bind failed. error code: %d\n", WSAGetLastError());
			freeaddrinfo(result);
			closesocket(listen_socket);
			WSACleanup();
			m_isrun = false;
		}
		if (!m_isrun)
			return;
		freeaddrinfo(result);

		result_ = listen(listen_socket, SOMAXCONN);
		if (result_ == SOCKET_ERROR) {
			printf("listen failed. error code: %d\n", WSAGetLastError());
			closesocket(listen_socket);
			WSACleanup();
		}
		// set to non-blocking mode
		u_long iMode = 1;
		ioctlsocket(listen_socket, FIONBIO, &iMode);
		int t_clientAddressLen = sizeof(struct sockaddr_in);
		struct sockaddr_in t_clientAddress;
		// server loop
		while (m_isrun)
		{
			// check new client connected
			client_socket = accept(listen_socket, (struct sockaddr *)&t_clientAddress, &t_clientAddressLen);
			int nError = WSAGetLastError();
			if (nError != WSAEWOULDBLOCK&&nError != 0)
			{
				// todo :
			}
			else if (client_socket != INVALID_SOCKET)
			{
				XDclient_info clientinfo;
				clientinfo.address = t_clientAddress;
				clientinfo.socket_id = client_socket;

				XDclient* clientnew = new XDclient(clientinfo);
				m_clients.push_back(clientnew);
				if (client_socket == INVALID_SOCKET) {
					printf("accept failed. error code: %d\n", WSAGetLastError());
				}
			}
			// check client live
			for (std::vector< XDclient* >::iterator it = m_clients.begin(); it != m_clients.end(); it++)
			{
				if (!(*it)->isLive())
				{
					delete *it;
					m_clients.erase(it);
					break;
				}
			}
			// check if there are data need to be sent
			if (!m_queue.empty())
			{
				std::string str = "";
				m_queue.try_pop(str );
				std::cout << " send data: " << str.c_str() <<std::endl;
				for (std::vector< XDclient* >::iterator it = m_clients.begin(); it != m_clients.end(); it++)
				{
					int m_recvbuflen;
					int sendresult = send((*it)->getinfo().socket_id, str.c_str(), str.length(), 0);
					if (sendresult == SOCKET_ERROR)
					{
						std::cout << " send failed. error code " << sendresult << std::endl;
					}
				}
			}
			for (std::vector< XDclient* >::iterator it = m_clients.begin(); it != m_clients.end(); it++)
			{
				std::string str_data = (*it)->get_data();
				if (str_data != "")
				{
					for (std::vector< XDclient* >::iterator it2 = m_clients.begin(); it2 != m_clients.end(); it2++)
					{
						if (*it != *it2)
						{
							int sendresult = send((*it2)->getinfo().socket_id, str_data.c_str(), str_data.length(), 0);
							if (sendresult == SOCKET_ERROR)
							{
								std::cout << " send failed. error code " << sendresult << std::endl;
							}
						}
					}
				}
			}
			// give a sleep, avoid high percentage cpu usage
			Sleep(50);
		}
	}
	void senddata(std::string& str)
	{
		m_queue.push(str);
	}
	bool isLive()
	{
		return m_isrun;
	}
private:
	char m_recvbuf[DEFAULT_BUFLEN];
	int m_recvbuflen;
	bool m_isrun;
	std::thread* m_thread;
	std::vector< XDclient* > m_clients;
	concurrent_queue <std::string> m_queue;
};

class Games
{
};

int __cdecl main(int argc, char** argv)
{
	XDserver* server = new XDserver();
	std::cout << "welcome to the server"<<std::endl;
	std::cout << "type an input to broadcast to all client. ""exit"" to close program " << std::endl;
	while (server->isLive())
	{
		char str_in[512]="";
		std::cin >> str_in;
		std::string str_input(str_in);
		server->senddata("server|"+str_input);
		if (!str_input.compare("exit"))
		{
			break;
		}
	}
	delete server;
	std::cout << "server shutdown" << std::endl;
	system("pause");
	return 0;
}

(client  code) :  ~181 baris

// xedi xermawan < edi.ermawan@gmail.com >
// May 27, 2014
// tcp client server networking test using winsock2
// client code

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <queue>
#include <sstream>

#include "concurrent_queue.hpp"

// link to winsock2 static lib
#pragma comment (lib, "ws2_32.lib")

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "22536"

class XDclientapp
{
public:
	XDclientapp(std::string& clientname, std::string& serveraddresss)
	{
		m_server_address = serveraddresss;
		m_client_name    = clientname;
		m_recvbuflen = DEFAULT_BUFLEN;
		m_isrun = true;
		m_thread = new std::thread(&XDclientapp::run, this);
	}
	~XDclientapp()
	{
		m_isrun = false;
		m_thread->join();
		delete m_thread;
	}
	void run()
	{
		WSADATA wsaData;
		SOCKET ConnectSocket = INVALID_SOCKET;
		struct addrinfo *result = NULL,
			*ptr = NULL,
			hints;
		char *sendbuf = "this is a test";
		char recvbuf[DEFAULT_BUFLEN];
		int iResult;
		int recvbuflen = DEFAULT_BUFLEN;

		// Initialize Winsock
		iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
		if (iResult != 0) {
			printf("WSAStartup failed with error: %d\n", iResult);
		}

		ZeroMemory(&hints, sizeof(hints));
		hints.ai_family = AF_UNSPEC;
		hints.ai_socktype = SOCK_STREAM;
		hints.ai_protocol = IPPROTO_TCP;

		// Resolve the server address and port
		iResult = getaddrinfo( m_server_address.c_str(), DEFAULT_PORT, &hints, &result);
		if (iResult != 0) {
			printf("getaddrinfo failed with error: %d\n", iResult);
			WSACleanup();
		}

		// Attempt to connect to an address until one succeeds
		for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

			// Create a SOCKET for connecting to server
			ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
				ptr->ai_protocol);
			if (ConnectSocket == INVALID_SOCKET) {
				printf("socket failed with error: %ld\n", WSAGetLastError());
				WSACleanup();
			}

			// Connect to server.
			iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
			if (iResult == SOCKET_ERROR) {
				closesocket(ConnectSocket);
				ConnectSocket = INVALID_SOCKET;
				continue;
			}
			break;
		}
		// set to non-blocking mode
		u_long iMode = 1;
		ioctlsocket(ConnectSocket, FIONBIO, &iMode);
		freeaddrinfo(result);
		int errorid;
		while (1)
		{
			unsigned int result_recv = recv(ConnectSocket, recvbuf, recvbuflen, 0);

			errorid = WSAGetLastError();
			if (errorid != WSAEWOULDBLOCK && errorid != 0)
			{
				printf("Client disconnected: %d\n", errorid);
				break;
			}
			else if (result_recv != INVALID_SOCKET && result_recv != 0)
			{
				// printf("Bytes received: %d\n", result_recv);
				recvbuf[result_recv] = '\0';
				printf("\n%s\n", recvbuf);
				std::cout << m_client_name.c_str() << "(You) : ";
			}
			else if (result_recv == 0)
			{
				printf("Client disconnected: %d\n", errorid);
				break;
			}

			// check if there are data need to be sent
			if (!m_queue.empty())
			{
				std::string str = "";
				m_queue.try_pop(str);
				int m_recvbuflen;
				int sendresult = send( ConnectSocket , str.c_str(), str.length(), 0);
				if (sendresult == SOCKET_ERROR)
				{
					std::cout << " send failed. error code " << sendresult << std::endl;
				}
			}
			// give a sleep, avoid high percentage cpu usage
			Sleep(50);
		}
	}
	void senddata(std::string& str)
	{
		std::string pstr = m_client_name + " : " + str;
		m_queue.push(pstr);
	}
private:
	char m_recvbuf[DEFAULT_BUFLEN];
	int m_recvbuflen;
	bool m_isrun;
	std::thread* m_thread;
	concurrent_queue <std::string> m_queue;
	std::string m_client_name;
	std::string m_server_address;
};
//--
int __cdecl main(int argc,char** argv)
{
	std::string clientname = "";
	std::string serveraddress = "localhost";
	std::cout << "Please enter your name: ";
	std::getline(std::cin, clientname);

	if (argc > 1)
	{
		serveraddress = argv[1];
	}
	XDclientapp* client = new XDclientapp(clientname, serveraddress);
	std::cout << "welcome to the chat client" << std::endl;
	std::cout << "type an input to broadcast to all client. ""exit"" to close program " << std::endl;
	std::cout << "---" << std::endl;
	while (1)
	{
		std::string str_input = "";
		std::cout << clientname.c_str() << "(You) : ";
		std::getline(std::cin, str_input);

		client->senddata(str_input);
		if (!str_input.compare("exit"))
		{
			break;
		}
	}
	delete client;
	return 0;
}

Screenshoot :
chatprogram
Download : source + vs project + binary ( hanya 39 kb ! ) .

Written by XediXermawan

May 27, 2014 at 6:43 pm

Posted in C/C++ Diary

Tagged with , , ,

#define problem

leave a comment »

Masalah kecil tapi fatal di projects C/C++ yang  saya temui ( dan parahnya terulang-ulang ), adalah penggunaan define. #define secara salah digunakan karena dicampur aduk antara definisi & pengecekannya. sebagai contoh ada 2 define :

#define USE_FEATURE_A
#define USE_FEATURE_B (1)

Dalam penggunaanya ada beberapa kemungkinan :

#ifdef USE_FEATURE_A
// code_inside_a.1
#endif
–>penggunaan yang benar. code “code_inside_a.1” akan ter-compile jika USE_FEATURE_A ter-define. kalau ingin menghilangkan code “code_inside_a.1”, USE_FEATURE_A harus un-define dengan komen : // #define USE_FEATURE_A

#if USE_FEATURE_A
// code_inside_a.2
#endif
–> salah. dan langsung kelihatan ( compile error ) jadi tidak perlu kawatir.

#if defined(USE_FEATURE_A)
// code_inside_a.3
#endif
–> benar. code “code_inside_a.3” akan ter-compile jika USE_FEATURE_A ter-define. kalau ingin menghilangkan code “code_inside_a.3”, USE_FEATURE_A harus un-define dengan komen : // #define USE_FEATURE_A

#if USE_FEATURE_B
// code_inside_b.1
#endif
–> benar. code “code_inside_b.1” ter-compile jika #define USE_FEATURE_B (1)

#if USE_FEATURE_B == 0
// code_inside_b.2
#endif
–>benar. code “code_inside_b.2” ter-compile jika #define USE_FEATURE_B (0)

#if defined(USE_FEATURE_B)
// code_inside_b.3
#endif
->salah. walaupun “code_inside_b.3” akan ter-compile ketika #define USE_FEATURE_B (1), namun “code_inside_b.3” juga akan ter-compile jika #define USE_FEATURE_B (0)

#ifdef USE_FEATURE_B
// code_inside_b.4
#endif
–>salah. walaupun “code_inside_b.4” akan ter-compile ketika #define USE_FEATURE_B (1), namun “code_inside_b.4” juga akan ter-compile jika #define USE_FEATURE_B (0)

best practice paling baik menurut saya memakai #define USE_FEATURE_B (1) dan secara konsisten menggunakan #if untuk mengecek.

 

Written by XediXermawan

May 21, 2014 at 5:22 pm

Posted in C/C++ Diary

Tagged with ,

my recent spare time projects

leave a comment »

I have been working on these projects in my spare time quite while ( like I work on it when I want work on it  :p )., but recently I’m disappointed with my progress ( apparently I’m busy and have difficulty when creating graphics assets by myself ). so I decided to upload source code to google code in case I’m interested again next time .There are 2 projects :

[1] unknown project 1. very simple game, almost done. keyword : C/C++,DirectX, Desktop App,Windows Phone 8

home project : https://code.google.com/p/my-fifteen-puzzle-dx/

svn link : https://my-fifteen-puzzle-dx.googlecode.com/svn/trunk

[2] unknown project 2 . also very simple game ( I like simple thing ). puzzle. gameplay complete. keyword : C/C++,DirectX, Desktop App, Windows Phone 8.

home project : https://code.google.com/p/proximity-game-clone2-dx/

svn link : https://proximity-game-clone2-dx.googlecode.com/svn/trunk

both of them actually same app I had made before . I rewrite again these projects using entirely new c/c++ code and using DirectX as graphics API. I also implemented some design pattern there. In case you are interested , check it out 🙂

game screenhoot running on windows phone 8 device
fiveteen_wp8

Written by XediXermawan

May 13, 2014 at 11:55 am