Quick and dirty client application for windows
Posted: 09 Mar 2014, 01:45
DON'T read this post if:
- you are happy with installing multi-Magabyte set of "Boost" libraries and injecting something from there into your application
- you are happy with handling exceptions thrown from some "Boost" element
- you like new language extentions, like BOOST_FOREACH
- you are happy with CMake and its fantastic syntax
- you like to link MSVCRTxx.DLL with the Boost libraries and 100% sure they use the same heap and threads handling
- the application you've made using SDK is extremely stable
Otherwise please take a look at the small console application (190 lines of code) made in MS Visual Studio 10 SP1.
It connects to the local eyetracker and prints the screen coordinates until you press the 'Enter' button.
It does not need any of the SDK headers/libraries. And this is plain C, not really C++.
Of course, it has some disadvantages. Not much error checking and a lot of assumptions.
You may tell me:
"What if the recv() function return only a part of the frame? Or two frames together?"
It can. Theoretically. But in practice, if you work with loopback interface, this will never happen. Besides, the frame is never larger than minimal TCP MTU.
So you only spent extra CPU cycles and programming work trying to serialize what is already serialized.
"You don't parse every single bracket"
Sure. I don't need it. The scanf() function is enough to parse these JSON data.
"What if they slightly change the frame format / protocol in the future?"
Ok, then this app will not work. But you'll now how it works and will easily fix it.
Here is the code. It works.
Your comments are welcome.
- you are happy with installing multi-Magabyte set of "Boost" libraries and injecting something from there into your application
- you are happy with handling exceptions thrown from some "Boost" element
- you like new language extentions, like BOOST_FOREACH
- you are happy with CMake and its fantastic syntax
- you like to link MSVCRTxx.DLL with the Boost libraries and 100% sure they use the same heap and threads handling
- the application you've made using SDK is extremely stable
Otherwise please take a look at the small console application (190 lines of code) made in MS Visual Studio 10 SP1.
It connects to the local eyetracker and prints the screen coordinates until you press the 'Enter' button.
It does not need any of the SDK headers/libraries. And this is plain C, not really C++.
Of course, it has some disadvantages. Not much error checking and a lot of assumptions.
You may tell me:
"What if the recv() function return only a part of the frame? Or two frames together?"
It can. Theoretically. But in practice, if you work with loopback interface, this will never happen. Besides, the frame is never larger than minimal TCP MTU.
So you only spent extra CPU cycles and programming work trying to serialize what is already serialized.
"You don't parse every single bracket"
Sure. I don't need it. The scanf() function is enough to parse these JSON data.
"What if they slightly change the frame format / protocol in the future?"
Ok, then this app will not work. But you'll now how it works and will easily fix it.
Here is the code. It works.
Your comments are welcome.
- Code: Select all
#include <Windows.h>
#include <stdio.h>
#include <process.h>
#include <winsock.h>
// The only two messages that we send to the server
char *JSON_heart_beat="{\"category\":\"heartbeat\"}";
char *JSON_set_push="{\"category\":\"tracker\",\"request\":\"set\",\"values\":{\"push\":true,\"version\":1}}";
// Scanf template for the frame (containing "fix:false")
char *JSON_frame_false="{\"category\":\"tracker\",\"request\":\"get\",\"statuscode\":200,\"values\":{\"frame\":{"\
"\"avg\":{\"x\":%f,\"y\":%f},\"fix\":false,\"lefteye\":{\"avg\":{\"x\":%f,\"y\""\
":%f},\"pcenter\":{\"x\":%f,\"y\":%f},\"psize\":%f,\"raw\":{\"x\":%f"\
",\"y\":%f}},\"raw\":{\"x\":%f,\"y\":%f},\"righteye\":{\"avg\":{\"x\":%f"\
",\"y\":%f},\"pcenter\":{\"x\":%f,\"y\":%f},\"psize\":%f,\"raw\":{\"x\":%f"\
",\"y\":%f}},\"state\":%d,\"time\":%d}}}";
// Scanf template for the frame (containing "fix:true")
char *JSON_frame_true="{\"category\":\"tracker\",\"request\":\"get\",\"statuscode\":200,\"values\":{\"frame\":{"\
"\"avg\":{\"x\":%f,\"y\":%f},\"fix\":true,\"lefteye\":{\"avg\":{\"x\":%f,\"y\""\
":%f},\"pcenter\":{\"x\":%f,\"y\":%f},\"psize\":%f,\"raw\":{\"x\":%f"\
",\"y\":%f}},\"raw\":{\"x\":%f,\"y\":%f},\"righteye\":{\"avg\":{\"x\":%f"\
",\"y\":%f},\"pcenter\":{\"x\":%f,\"y\":%f},\"psize\":%f,\"raw\":{\"x\":%f"\
",\"y\":%f}},\"state\":%d,\"time\":%d}}}";
SOCKET TETSocket;
volatile bool flag_ShutDownThreads=false; // flag to inform the threads to terminate
uintptr_t handler_HeartBeat=0, handler_Reader=0; // thread handlers
//==================================================
// HeartBeat thread - sends a message once a second
//==================================================
unsigned __stdcall HeartBeatThread(void *p)
{
while(!flag_ShutDownThreads)
{
if(SOCKET_ERROR==send(TETSocket,JSON_heart_beat,strlen(JSON_heart_beat),0)) return 0; // Connection closed
Sleep(1000);
}
return 0;
}
//=========================================================
// Reader thread - receiving messages from the server here
//=========================================================
unsigned __stdcall ReaderThread(void *p)
{
// big enough buffers...
char buffer[4096];
char true_false_buffer[128];
int bytesReceived;
int num_scanned=0;
float x_avg,y_avg,
left_x_avg, left_y_avg, left_pcenter_x, left_pcenter_y, left_psize, left_x_raw, left_y_raw,
x_raw, y_raw,
right_x_avg, right_y_avg, right_pcenter_x, right_pcenter_y, right_psize, right_x_raw, right_y_raw;
int state, tet_time, fix;
while(!flag_ShutDownThreads)
{
bytesReceived = recv(TETSocket, buffer, 4095, 0);
if(0>=bytesReceived) return 0; // Connection closed
buffer[bytesReceived]=0; // Make a string zero-terminated
// First try. Template string contains "fix:false"
fix=0;
num_scanned=sscanf(buffer,JSON_frame_false,
&x_avg,&y_avg,
&left_x_avg, &left_y_avg, &left_pcenter_x, &left_pcenter_y, &left_psize, &left_x_raw, &left_y_raw,
&x_raw, &y_raw,
&right_x_avg, &right_y_avg, &right_pcenter_x, &right_pcenter_y, &right_psize, &right_x_raw, &right_y_raw,
&state, &tet_time
);
// Second try. Template string contains "fix:true"
if(20!=num_scanned)
{
fix=1;
num_scanned=sscanf(buffer,JSON_frame_true,
&x_avg,&y_avg,
&left_x_avg, &left_y_avg, &left_pcenter_x, &left_pcenter_y, &left_psize, &left_x_raw, &left_y_raw,
&x_raw, &y_raw,
&right_x_avg, &right_y_avg, &right_pcenter_x, &right_pcenter_y, &right_psize, &right_x_raw, &right_y_raw,
&state, &tet_time
);
}
if(20==num_scanned)
{
// Do domething real here instead of just printing
//if(7==state)
printf("X:%f Y:%f fix:%d state:%d\n",x_avg,y_avg,fix,state);
}
}
return 0;
}
//=========================================================
// Init TCP connection (socket)
//=========================================================
int TETconnect()
{
WORD sockVersion;
WSADATA wsaData;
int nret;
char *err_string;
SOCKADDR_IN TETserver;
// Initialize Winsock
sockVersion=MAKEWORD(2, 2);
nret=WSAStartup(sockVersion, &wsaData);
if(nret)
{
err_string="Failed to initialize Winsock\n";
goto ws_error;
}
// Create a socket
TETSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == TETSocket)
{
err_string="Cannot create socket\n";
goto ws_error;
}
// Local server
TETserver.sin_family = AF_INET;
TETserver.sin_addr.S_un.S_un_b.s_b1=127; // 127.0.0.1
TETserver.sin_addr.S_un.S_un_b.s_b2=0;
TETserver.sin_addr.S_un.S_un_b.s_b3=0;
TETserver.sin_addr.S_un.S_un_b.s_b4=1;
TETserver.sin_port = htons(6555);
// Connect the local server
nret = connect(TETSocket, (LPSOCKADDR)&TETserver, sizeof(struct sockaddr));
if (SOCKET_ERROR == nret)
{
err_string="Cannot connect to server\n";
goto ws_error;
}
// Connected!!!
fprintf(stderr,"Connected to server!\n");
return 0;
ws_error:
// Print error and shut down winsock
fprintf(stderr,"%s. Press 'Enter' to close the program.\n", err_string);
WSACleanup();
return 1;
}
int main()
{
char buffer[4096];
// 1. Initialize TCP connection
if(TETconnect()) goto cleanup;
// 2. Set Push mode
send(TETSocket,JSON_set_push,strlen(JSON_set_push),0);
recv(TETSocket, buffer, 4095, 0); // ignore the reply
// 2. Start heartbeat thread
handler_HeartBeat=_beginthreadex(NULL,0,HeartBeatThread,0,0,NULL);
if(1>handler_HeartBeat) goto cleanup; // this will never happen... but...
// 3. Start Reader thread
handler_Reader=_beginthreadex(NULL,0,ReaderThread,0,0,NULL);
if(1>handler_Reader) goto cleanup; // this will never happen... but...
cleanup:
//press 'Enter' to end the program
getc(stdin);
// Shut down winsock
WSACleanup();
// Wait for threads to terminate
flag_ShutDownThreads=true; // inform the threads to terminate
if(handler_HeartBeat) WaitForSingleObject((HANDLE)handler_HeartBeat,0);
if(handler_Reader) WaitForSingleObject((HANDLE)handler_Reader,0);
return 0;
}