//
// Written by D. D. Khoa on 2013 Apr
//

Up:[[Tutorial]]

----
#contents

*Connect to SIGVerse controller via socket using Winsock (blocking mode)[#ief309b1]

※ This section explains how to connect to SIGVerse controller (run on Linux) via socket from a client computer (Windows) using Winsock. The socket code is integrated to  the controller, not the server.

※ The demo application in this tutorial is ''Receive images captured inside SIGVerse to local computer''. 
- Environment: C++.Net Windows Form Application program, Visual Studio 2008, Windows 7 (32bit).
- The script of this application is as follow:
-- Build a form having 3 buttons (for the sake of clarity, but you can combine the functionality of these 3 buttons in a way you want): 
--- ''Connect'': connect to controller
--- ''Get Image'': get image from controller to client computer
--- ''Close'': close a socket after receiving an image from controller
-- When the button ''Connect'' is pushed:
--- [Client] The ''sndmsg'' function is called to notify the controller that a client is about to connect.
--- [Controller] After receiving the notification from client, the controller will call the ''accept'' function and then it wait for connection from client. 
--- [Client] Call the ''connect'' function to make a connection.
-- When the button ''Get Image'' is pushed:
--- [Client] Call the ''sndmsg'' to notify the controller to send image data.
--- [Controller] After receiving the notification from client, the controller will open the image as a binary file, read this file and send to client as a block of 512 bytes (you can change this size).
--- [Client] Receive blocks of data until no more data is received, and then save as a file.
-- When the button ''Close'' is pushed:
--- [Client] The ''sndmsg'' is called to notify the controller to close the socket.
--- [Controller] After receiving the notification, the controller will close the socket. 

※ Some problems:
- This tutorial used ''blocking'' mode socket. It means the controller will wait for a client connection and can not do anything during this period. I also show you a temporary solution but it's not so convenient. A better solution is using ''non-blocking'' mode, which will be presented in another tutorial [future work].

- This tutorial requires using Inamura Lab network or you have to use the ''port forwarding'' in order to use the '''''sndmsg''''' function. For more information about this function, please refer to this tutorial [[メッセージ送信ツールの作成]].

- The problem of this tutorial is by using socket, you have to close it after the first time you finish receiving data from controller, or else the next time you would get a mess. This is considered an open problem for readers to try.

** Set up the environment [#j07cee5a]
*** Client side [#d82bf052]
- Create a C++.Net Windows Form Application like this:
&ref(Form.JPG);
- Add a reference to the ''sigverse.dll''. For more information, please refer to this tutorial [[メッセージ送信ツールの作成]].
- Because my client program runs on Windows, I use Winsock for the socket.
- Declare some variables:
-- ''wsData'' for the Winsock as global variables.
-- ''skServer'' for containing server ID 
-- ''msgSIG'' as a MessageSender variable for sending notification message to the controller
-- ''bConnectedFlag'' is '''true''' if it's ready to send/receive image data
#highlight(c++){{
...
#include <winsock2.h>
#pragma comment (lib,"ws2_32.lib")

SOCKET skServer; //Contain server socket ID
WSADATA wsData; //For using Winsock 
public ref class Form1 : public System::Windows::Forms::Form
{
//For sending message to SIGVerse using the sndmsg function
private: sigverse::MessageSender^ msgSIG; //declare a MessageSender variable
private: bool bConnectedFlag;
public:
	Form1(void)
	{
		...
		WSAStartup(0x0202,&wsData); //Start the Winsock
		msgSIG = gcnew sigverse::MessageSender("ImageClient");
		bConnectedFlag = false; //not connected yet
	}
...
	~Form1()
	{
		...
		WSACleanup();
	}
}}
*** Controller side [#o0020298]
- Basically, I reused this tutorial [[エージェント視点の画像取得]] and modify the ''captureView.cpp''. So you use that tutorial to build the environment at the controller side first. Then we will add the code like below.
- Add some more code to the ''RobotController'' class:
#highlight(c++){{
//Header files for using socket in the controller
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
...

class RobotController : public Controller
{
private:
   ...
   //Variables for handling sockets
   int iServerSock, iClientSock; //Socket IDs
   struct sockaddr_in skaddrServer, skaddrClient; //Socket addresses
   bool bConnectedFlag; // is 'true' if there is no connection problem so far

public:
   ...
   //This function is called after the controller receive "get image" message from client
   void handleImageSending(int iClientSock);
}
}}

**What happens at the client side? [#mf52e6f2]
***When the button "'''Connect'''" is pushed... [#fd56c77c]
- This button sends a message notify the controller that this client is going to make a connection, so that the controller will call the ''accept'' function to wait, and then we make a connection.
- Add an push button event handler:
#highlight(c++){{
private: System::Void bt_connect_Click(System::Object^  sender, System::EventArgs^  e) 
{
//Notify the controller to be ready to accept client connection
	char* sMsg[] = {"open socket"};
	msgSIG->connect("socio2.iir.nii.ac.jp",9011);
	msgSIG->sendto("robot_0",1,sMsg);

//Set up the connection
	skServer = socket(AF_INET,SOCK_STREAM,IPPROTO_IP); //create a socket ID
	sockaddr_in skaddrServer;	//for sever address

	skaddrServer.sin_family = AF_INET; //this connection use TCP model
	skaddrServer.sin_port = htons( 9020 ); //Choose a port for this socket
	skaddrServer.sin_addr.S_un.S_addr = (inet_addr("136.187.35.112")); // SIGVers server's IP (socio2)
	//Then, we make a connection
	if (connect(skServer, (sockaddr*)&skaddrServer, sizeof(skaddrServer))!= SOCKET_ERROR)    
		bConnectedFlag = true; //Success
}
}}
***When the button "'''Get Image'''" is pushed ... [#d79b58d8]
- First, we notify the controller to start to send image data
- Then, we receive the image size first, to stop waiting for receiving data from controller when all the image data has been sent to client. Or you can find some way to let client know when all data has been sent so that client will not keep waiting anymore.
- After that, we start receiving image data
#highlight(c++){{
private: System::Void bt_getimage_Click(System::Object^  sender, System::EventArgs^  e) {
   if (bConnectedFlag)
   {
	int nByteRecv, nSize; //Number of byte received each time and the size of image data
	char sResponse[512]={0}; //data received each time

	//Notify the controller to send image using socket
	char* sMsg[] = {"get image"};
	msgSIG->connect("socio2.iir.nii.ac.jp",9011);
	msgSIG->sendto("robot_0",1,sMsg);

	//Start receiving image data:
	//Get the image size first
	nSize = recv(skServer,sResponse,sizeof(sResponse),0);
	nSize = atoi(sResponse);

	//Open a file to write image data
	FILE * fo = fopen("image.bmp","wb");
	while(1)
	{
		nByteRecv = recv(skServer,sResponse,sizeof(sResponse),0);// read 512 Bytes			
		fwrite(sResponse,sizeof(char),nByteRecv,fo);

		//Check to know if all the image data is received enough
		nSize -= nByteRecv;
		if(nSize <= 0)    //Break when no more data left
			break;
	}
	fclose(fo);
   }
}
}}
***When the button "'''Close'''" is pushed ... [#ub91d46d]
- Send a message to the controller to close the socket. This is because when I tried to use this opened socket for receiving data the second time, it didn't work as I expected.
#highlight(c++){{
private: System::Void bt_close_Click(System::Object^  sender, System::EventArgs^  e) {
	char* sMsg[] = {"close socket"};
	msgSIG->connect("socio2.iir.nii.ac.jp",9011);
	msgSIG->sendto(sAgentname,1,sMsg);
}
}}
**What happens at the controller side? [#l53b0996]
I attached in this tutorial the modified &ref(Capture_View.cpp);. I just note some important code here.
- In the ''onInit'', bind a socket to a port and start listening on this socket.
- In the ''onRecvMessage'', we will handle situations when a client sends the ''open'', ''get image'' and ''close'' message.
***When the ''open'' message is received... [#ecd5a6c8]
- The controller will call the ''accept'' function and wait until client call the '''''connect''''' function.
#highlight(c++){{
else if (strcmp(msg.c_str(), "open socket") == 0)
{
	unsigned int iAddrLen = sizeof(sockaddr);
	if ((iClientSock = accept(iServerSock, (struct sockaddr *) &skaddrServer, &iAddrLen)) < 0) 
	{	LOG_MSG(("Failed to accept client connection")); }
	else
		LOG_MSG(("Accepted connection"));
}
}} 
***When the '''''get image''''' message is received... [#k04a5f97]
- Implement the ''handleImageSending'' function (just for the ''onRecvMessage'' function to be clear).
- Open the image file want to send (for example, ''view000.jpg'')
#highlight(c++){{
FILE * fi = fopen("view000.jpg","rb");
if (fi==NULL)
{
	LOG_MSG(("Failed to open image file"));
	return;
}
}}
- Send the image size first
#highlight(c++){{
/*Send the file size to client*/
char sSize[10];
long iSize;
		
fseek(fi,0,SEEK_END);
iSize = ftell(fi);
fseek(fi,0,SEEK_SET);
sprintf(sSize,"%d",iSize);
write(iClientSock,sSize,sizeof(sSize));	
}}
- The final part of the code is to send image data to client.
***When the '''''close''''' message is received... [#n5600d2f]
- Call the ''close'' function to close the socket.
#highlight(end)

#counter

Front page   Edit Diff Backup Upload Copy Rename Reload   New List of pages Search Recent changes   Help   RSS of recent changes