1 //#**************************************************************
2 //#
3 //# filename:             TcpIpRoutines.cpp
4 //#
5 //# author:               Sch�rgenhumer Markus
6 //#
7 //# generated:						Feb and July 2013
8 //#
9 //# description:          - base class TCPIPSocket for TCP/IP server/client
10 //#													socket set-up, communication, and error handling
11 //#												- derived class TCPIPHotInt for use in HOTINT
12 //#
13 //# remarks:              - additional linking dependencies: ws2_32.lib
14 //# 											  (in HOTINT in the project "MBSElementsAndModels")
15 //#
16 //# Copyright (c) 2003-2013 Johannes Gerstmayr, Linz Center of Mechatronics GmbH, Austrian
17 //# Center of Competence in Mechatronics GmbH, Institute of Technical Mechanics at the
18 //# Johannes Kepler Universitaet Linz, Austria. All rights reserved.
19 //#
20 //# This file is part of HotInt.
21 //# HotInt is free software: you can redistribute it and/or modify it under the terms of
22 //# the HOTINT license. See folder 'licenses' for more details.
23 //#
24 //# bug reports are welcome!!!
25 //# WWW:		www.hotint.org
26 //# email:	bug_reports@hotint.org or support@hotint.org
27 //#**************************************************************
28 
29 
30 #include <fstream>
31 #include <iostream>
32 #include <sstream>
33 #include "TcpIpRoutines.h"
34 #include "winsock2.h"        //note: also includes windows.h
35 
36 using namespace std;
37 
38 
39 //========================================================================================
40 //================================== TCPIPSocket =========================================
41 //========================================================================================
42 
TCPIPSocket(SocketType typei,char * ipdata,int errormsgflag,int infomsgflag)43 TCPIPSocket::TCPIPSocket(SocketType typei, char* ipdata, int errormsgflag, int infomsgflag)
44 {
45 	type=typei;
46 	port=0;
47 	//ipstring=NULL;
48 	status=0;
49 	autoreconnect=0;
50 	looptime=1000;
51 	//recvbuffer=0;
52 	SetErrorMessageMode(errormsgflag);
53 	SetInfoMessageMode(infomsgflag);
54 	GetTCPIPConfig(ipdata);
55 	accepttimeout = -1;
56 }
57 
TCPIPSocket(SocketType typei,char * ipIn,unsigned short portIn,int errormsgflag,int infomsgflag)58 TCPIPSocket::TCPIPSocket(SocketType typei, char* ipIn, unsigned short portIn, int errormsgflag, int infomsgflag)
59 {
60 	Set(typei,ipIn,portIn,errormsgflag,infomsgflag);
61 }
62 
Set(SocketType typei,char * ipIn,unsigned short portIn,int errormsgflag,int infomsgflag)63 void TCPIPSocket::Set(SocketType typei, char* ipIn, unsigned short portIn, int errormsgflag, int infomsgflag)
64 {
65 	type=typei;
66 	port=portIn;
67 	//ipstring=ipIn;
68 	strcpy(ipstring,ipIn);
69 	status=0;
70 	autoreconnect=0;
71 	looptime=1000;
72 	SetErrorMessageMode(errormsgflag);
73 	SetInfoMessageMode(infomsgflag);
74 	accepttimeout = -1;
75 }
76 
77 
SetUpConnection()78 int TCPIPSocket::SetUpConnection()
79 {
80 	if(status)
81 		CloseConnection();
82 
83 	if(type==ST_client)
84 	{
85 		StartWinsock();
86 		CreateSocket();
87 		//ConnectOnce();
88 		ConnectLoop();
89 		//FlushRecvBuffer();
90 		Initialize();
91 		return 1;
92 	}
93 	else if(type==ST_server)
94 	{
95 		StartWinsock();
96 		CreateSocket();
97 		BindSocket();
98 		ListenMode();
99 		AcceptConnection();
100 		Initialize();
101 		return 1;
102 	}
103 	else
104 	{
105 		Error(0,"Invalid socket type!");
106 		return -1;
107 	}
108 }
109 
CloseConnection()110 void TCPIPSocket::CloseConnection()
111 {
112 	if(status)
113 	{
114 		closesocket(c);
115 		if(type==ST_server)
116 			closesocket(s);
117 		WSACleanup();
118 		status = 0;
119 	}
120 }
121 
~TCPIPSocket()122 TCPIPSocket::~TCPIPSocket()
123 {
124 	CloseConnection();
125 	//if(ipstring)
126 	//	delete [] ipstring;
127 }
128 
129 
GetTCPIPConfig(char * file)130 void TCPIPSocket::GetTCPIPConfig(char* file)
131 {
132 	ifstream ipconfig;
133 	string a1,a2,a3,a4,temp1;
134 
135 	ipconfig.open(file,ios::binary|ios::in);
136 	if(!ipconfig.is_open())
137 	{
138 		Error(0,string("Access to TCP/IP configuration file '").append(string(file)).append(string("' failed!")));
139 		//cout << "Error accessing  'IP.txt'" << endl;
140 		//cout << "Enter configuration manually: " << endl << endl;
141 		//cout << "Server-IP a1.a2.a3.a4 (IPv4): " << endl << endl;
142 		//cout << "a1: ";
143 		//cin >> a1;
144 		//cout << "a2: ";
145 		//cin >> a2;
146 		//cout << "a3: ";
147 		//cin >> a3;
148 		//cout << "a4: ";
149 		//cin >> a4;
150 		//cout << "Port: ";
151 		//cin >> port;
152 		//cout << endl;
153 	}
154 	else
155 	{
156 		ipconfig >> a1;
157 		ipconfig >> a2;
158 		ipconfig >> a3;
159 		ipconfig >> a4;
160 		ipconfig >> port;
161 		ipconfig.clear();
162 		ipconfig.close();
163 	}
164 
165 	temp1.append(a1).append(".").append(a2).append(".").append(a3).append(".").append(a4);
166 	//ipstring = new char[temp1.length()+1];   //generation of a c-strings for the IP
167 	strcpy(ipstring,temp1.c_str());
168 
169 	//stringstream out;
170 	//out << "server-IP / port: " << ipstring << " / " << port << endl << endl;
171 	//InfoMessage(out.str());
172 }
173 
StartWinsock()174 long TCPIPSocket::StartWinsock(){
175   WSADATA wsa;
176   long rc = WSAStartup(MAKEWORD(2,0),&wsa);  //MAKEWORD ... macro which transforms the version number 2.0 into WORD (unsigned short)
177 	if(rc)
178 		Error(1,"Failed to start winsock!",rc);
179 	else
180 		InfoMessage("Winsock started");
181 	return rc;
182 }
183 
CreateSocket()184 void TCPIPSocket::CreateSocket()
185 {
186 	//SOCKET & loc = s;    //somehow, this reference IS NOT WORKING, no clue why; workaround below the commented lines
187 	//if(type==ST_client)
188 	//	loc=c;
189 	//loc=socket(AF_INET,SOCK_STREAM,0);
190 	//if(loc==INVALID_SOCKET)
191 	//	Error(1,"Failed to create socket!\n",WSAGetLastError());
192 	//else
193 	//	InfoMessage("Socket created\n");
194 
195 	if(type==ST_client)
196 	{
197 		c=socket(AF_INET,SOCK_STREAM,0);
198 		if(c==INVALID_SOCKET)
199 			Error(1,"Failed to create socket!",WSAGetLastError());
200 		else
201 			InfoMessage("Socket created");
202 	}
203 	else
204 	{
205 		s=socket(AF_INET,SOCK_STREAM,0);
206 		if(s==INVALID_SOCKET)
207 			Error(1,"Failed to create socket!",WSAGetLastError());
208 		else
209 			InfoMessage("Socket created");
210 	}
211 }
212 
BindSocket()213 long TCPIPSocket::BindSocket()
214 {
215 	SOCKADDR_IN addr;
216 	long rc;
217 
218 	memset(&addr,0,sizeof(SOCKADDR_IN));   //cf. p.19, Beej's guide
219 	addr.sin_family=AF_INET; //IPv4
220 	addr.sin_port=htons(port);
221 	addr.sin_addr.s_addr=inet_addr(ipstring);
222 	rc=bind(s,(SOCKADDR*)&addr,sizeof(SOCKADDR_IN));
223 	if(rc==SOCKET_ERROR)
224 		Error(1,"Failed to bind server socket to given IP and port!",WSAGetLastError());
225 	else
226 	{
227 		stringstream out;
228 		out << "Server socket bound to IP " << ipstring << ", port " << port;
229 		InfoMessage(out.str());
230 	}
231 	return rc;
232 }
233 
ListenMode()234 long TCPIPSocket::ListenMode()
235 {
236 	long rc;
237 	rc=listen(s,10);
238 	if(rc==SOCKET_ERROR)
239 		Error(1,"Failed to activate listen mode!",WSAGetLastError());
240 	else
241 		InfoMessage("Server socket is in listen mode...");
242 	return rc;
243 }
244 
AcceptConnection()245 int TCPIPSocket::AcceptConnection()
246 {
247 	if(accepttimeout>0)
248 	{
249 		int i=0;
250 		while(1)
251 		{
252 			//workaround to implement timeout for accept
253 
254 			//from MSDN on the "select"-function:
255 			//The parameter readfds identifies the sockets that are to be checked for readability. If the socket is currently in the listen state, it will be marked as readable
256 			//if an incoming connection request has been received such that an accept is guaranteed to complete without blocking.
257 
258 			timeval to;
259 			to.tv_sec = (unsigned int) ((double)accepttimeout)/1000;
260 			to.tv_usec = accepttimeout%1000;
261 			fd_set socketset;
262 			socketset.fd_count = 1;
263 			socketset.fd_array[0] = s;
264 			//FD_ZERO(&socketset);
265 			//FD_SET(c,&socketset);
266 			int temp = select(1,&socketset,NULL,NULL,&to);
267 			if(temp==0)
268 			{
269 				++i;
270 				if(i<3)
271 					Message("Failed to accept connection from client! (timeout) Retry...");
272 				else
273 					Error(1,"Failed to accept connection from client! (timeout)");
274 			}
275 			else if(temp==SOCKET_ERROR)
276 				Error(1,"Failed to accept connection from client!",WSAGetLastError());
277 			else
278 				break;
279 		}
280 	}
281 
282 	c=accept(s,NULL,NULL);
283 	if(c==INVALID_SOCKET)
284 		Error(1,"Failed to accept connection from client!",WSAGetLastError());
285 	else
286 	{
287 		InfoMessage("New connection to client accepted");
288 		status=1;
289 	}
290 	return 1;
291 }
292 
ConnectLoop()293 long TCPIPSocket::ConnectLoop()
294 {
295 	long rc;
296 	SOCKADDR_IN addr;	//addr contains all neccessary information about the server socket; the port of the client socket is chosen by Windows
297 	stringstream out;
298 
299 	memset(&addr,0,sizeof(SOCKADDR_IN)); //reset everything to 0
300 	addr.sin_family=AF_INET;
301 	addr.sin_port=htons(port); //port
302 	addr.sin_addr.s_addr=inet_addr(ipstring); //IP v4 adress of server
303 
304 	int waitingcounter = -1;
305 	while(1){
306 		rc=connect(c,(SOCKADDR*)&addr,sizeof(SOCKADDR));
307 		if(rc==SOCKET_ERROR)
308 		{
309 			switch(waitingcounter){
310 					case -1: out.str(""); out << "Waiting to connect to " << ipstring << ", port " << port << endl; break;
311 					case 0:  out.str(""); out << "\r." << "   "; break;
312 					case 1:  out.str(""); out << "\r.." << "  "; break;
313 					case 2:  out.str(""); out << "\r..." << " "; waitingcounter = -1; break;
314 			}
315 			InfoMessage(out.str());
316 			++waitingcounter;
317 			Sleep(looptime); //wait for "time" milliseconds
318 		}
319 		else
320 		{
321 			InfoMessage("Connected!");
322 			status=1;
323 			break;
324 		}
325 	}
326 	return rc;
327 }
328 
ConnectOnce()329 long TCPIPSocket::ConnectOnce()
330 {
331 	long rc;
332 	SOCKADDR_IN addr;	//addr contains all neccessary information about the server socket; the port of the client socket is chosen by Windows
333 
334 	memset(&addr,0,sizeof(SOCKADDR_IN)); //reset everything to 0
335 	addr.sin_family=AF_INET;
336 	addr.sin_port=htons(port); // port
337 	addr.sin_addr.s_addr=inet_addr(ipstring); //IP v4 adress of server
338 	rc=connect(c,(SOCKADDR*)&addr,sizeof(SOCKADDR));
339 
340 	if(rc==SOCKET_ERROR)
341 		Error(1,"Connect failed!",WSAGetLastError());
342 	else
343 	{
344 		InfoMessage("Connected!");
345 		status=1;
346 	}
347 	return rc;
348 }
349 
350 //void TCPIPSocket::FlushRecvBuffer()
351 //{
352 //	int bufsize;
353 //	int optlen=sizeof(int);
354 //	getsockopt(c,sol_socket,so_rcvbuf,reinterpret_cast<char*> (&bufsize),&optlen);
355 //	//cout << "recv buffer in bytes: " << temp << "\n";
356 //	char* temp = new char[bufsize];
357 //
358 //	//unsigned long recvto;
359 //	//optlen=sizeof(unsigned long);
360 //	//getsockopt(c,sol_socket,so_rcvtimeo,reinterpret_cast<char*> (&recvto),&optlen);
361 //	//cout << "timeout " << recvto << "\n";
362 //
363 //	setrecvtimeout(10000);
364 //
365 //	int remain=bufsize;
366 //	int tmp=0;
367 //	int count=0;
368 //	while(remain!=0 && tmp>=0)
369 //	{
370 //		int tmp = recv(c,temp+(bufsize-remain),remain,0);
371 //		remain-=tmp;
372 //		count+=tmp;
373 //	}
374 //
375 //	cout << "from buffer: " << count << "\n";
376 //	cout << "last error" << wsagetlasterror() << "\n";
377 //
378 //	//setrecvtimeout(recvto);
379 //
380 //	delete [] temp;
381 //}
382 
Initialize()383 void TCPIPSocket::Initialize()
384 {
385 }
386 
SendByte(byte data)387 int TCPIPSocket::SendByte(byte data)
388 {
389 	int stat = SendData(reinterpret_cast<char*> (&data),1);
390 	return stat;
391 }
392 
RecvByte(byte & data)393 int TCPIPSocket::RecvByte(byte & data)
394 {
395 	int stat = RecvData(reinterpret_cast<char*> (&data),1);
396 	return stat;
397 }
398 
SendInteger(int data)399 int TCPIPSocket::SendInteger(int data)
400 {
401 	int temp=htonl(data);
402 	int stat = SendData(reinterpret_cast<char*> (&temp),4);
403 	return stat;
404 }
405 
RecvInteger(int & datain)406 int TCPIPSocket::RecvInteger(int & datain)
407 {
408 	datain=0;
409 	int stat = RecvData(reinterpret_cast<char*> (&datain),4);
410 	datain=ntohl(datain);
411 	return stat;
412 }
413 
SendData(char * data,int size)414 int TCPIPSocket::SendData(char* data, int size)
415 {
416 	if(!status)
417 	{
418 		Error(1,"Failed to send data. Socket is not properly initialized!");
419 		return -1;
420 	}
421 
422 	int remain=size;
423 	int tmp=0;
424 
425 	while(remain!=0)
426 	{
427 		int tmp = send(c,data+(size-remain),remain,0);
428 		if(tmp<=0)
429 		{
430 			Error(1,"An error occurred while sending data!",WSAGetLastError());
431 			return tmp;
432 		}
433 		remain-=tmp;
434 	}
435 	return size;
436 }
437 
RecvData(char * data,int size)438 int TCPIPSocket::RecvData(char* data, int size)
439 {
440 	if(!status)
441 	{
442 		Error(1,"Failed to receive data. Socket is not properly initialized!");
443 		return -1;
444 	}
445 
446 	int remain=size;
447 	int tmp=0;
448 
449 	while(remain!=0)
450 	{
451 		int tmp = recv(c,data+(size-remain),remain,0);
452 		//if(tmp==0)
453 		//{
454 		//	Error(1,"Connection was closed by the server!");
455 		//	return tmp;
456 		//}
457 		if(tmp<=0)
458 		{
459 			Error(1,"An error occurred while receiving data!",WSAGetLastError());
460 			return tmp;
461 		}
462 		remain-=tmp;
463 	}
464 	return size;
465 }
466 
SetRecvTimeOut(unsigned long time)467 void TCPIPSocket::SetRecvTimeOut(unsigned long time)
468 {
469 	setsockopt(c,SOL_SOCKET,SO_RCVTIMEO,reinterpret_cast<const char*> (&time),sizeof(unsigned long));
470 }
471 
SetSendTimeOut(unsigned long time)472 void TCPIPSocket::SetSendTimeOut(unsigned long time)
473 {
474 	setsockopt(c,SOL_SOCKET,SO_SNDTIMEO,reinterpret_cast<const char*> (&time),sizeof(unsigned long));
475 }
476 
SetAcceptTimeOut(unsigned long time)477 void TCPIPSocket::SetAcceptTimeOut(unsigned long time)
478 {
479 	accepttimeout = time;
480 }
481 
482 //void TCPIPSocket::SetRecvBufferSize(int size)
483 //{
484 //	recvbuffer=size;
485 //	setsockopt(c,SOL_SOCKET,SO_RCVBUF,reinterpret_cast<const char*> (&recvbuffer),sizeof(int));
486 //}
487 
SetLoopTime(int time)488 void TCPIPSocket::SetLoopTime(int time)
489 {
490 	looptime=time;
491 }
492 
Error(int type,string err,int errornumber)493 int TCPIPSocket::Error(int type, string err, int errornumber)
494 {
495 	stringstream ts;
496 	if(type==0)
497 	{
498 		if(errormsg)
499 		{
500 			string temp = "Fatal error: ";
501 			if(errornumber)
502 			{
503 				ts<<errornumber;
504 				temp.append(ts.str());
505 			}
506 			temp.append("\n");
507 			std::cout << temp;
508 			std::cout << err << "\n";
509 			system("PAUSE");
510 		}
511 		exit(-1);
512 	}
513 	else if(type==1)
514 	{
515 		if(errormsg)
516 		{
517 			string temp="Error: ";
518 			if(errornumber)
519 			{
520 				ts<<errornumber;
521 				temp.append(ts.str());
522 			}
523 			temp.append("\n");
524 			std::cout << temp;
525 			std::cout << err << "\n";
526 			system("PAUSE");
527 		}
528 		CloseConnection();
529 		if(autoreconnect)
530 		{
531 			InfoMessage("Re-initializing TCP/IP connection...");
532 			SetUpConnection();
533 		}
534 	}
535 	return errornumber;
536 }
537 
Message(string message)538 void TCPIPSocket::Message(string message)
539 {
540 	cout << message << endl;
541 }
542 
InfoMessage(string info)543 void TCPIPSocket::InfoMessage(string info)
544 {
545 	if(infomsg)
546 		cout << info << endl;
547 }
548 
SetErrorMessageMode(int flag)549 void TCPIPSocket::SetErrorMessageMode(int flag)
550 {
551 	if(flag)
552 		errormsg=1;
553 	else
554 		errormsg=0;
555 }
556 
SetInfoMessageMode(int flag)557 void TCPIPSocket::SetInfoMessageMode(int flag)
558 {
559 	if(flag)
560 		infomsg=1;
561 	else
562 		infomsg=0;
563 }
564 
SetAutoReconnect(int flag)565 void TCPIPSocket::SetAutoReconnect(int flag)
566 {
567 	if(flag)
568 		autoreconnect=1;
569 	else
570 		autoreconnect=0;
571 }
572 
573 
574 //========================================================================================
575 //=================================== TCPIPHotInt ========================================
576 //========================================================================================
577 
Set(SocketType typei,char * ipIn,unsigned short portIn,int errormsgflag,int infomsgflag)578 void TCPIPHotInt::Set(SocketType typei, char* ipIn, unsigned short portIn, int errormsgflag, int infomsgflag)
579 {
580 	TCPIPSocket::Set(typei,ipIn,portIn,errormsgflag,infomsgflag);
581 }
582 
Set(MBS * mbsI,SocketType typei,char * ipIn,unsigned short portIn,int errormsgflag,int infomsgflag)583 void TCPIPHotInt::Set(MBS* mbsI, SocketType typei, char* ipIn, unsigned short portIn, int errormsgflag, int infomsgflag)
584 {
585 	mbs=mbsI;
586 	TCPIPSocket::Set(typei,ipIn,portIn,errormsgflag,infomsgflag);
587 }
588 
Error(int type,std::string err,int errornumber)589 int TCPIPHotInt::Error(int type, std::string err, int errornumber)
590 {
591 	stringstream ts;
592 	if(errormsg)
593 	{
594 		string temp = "TCP/IP error: ";
595 		temp.append(err);
596 		if(errornumber)
597 		{
598 		  temp.append(" (");
599 			ts<<errornumber;
600 			temp.append(ts.str());
601 			temp.append(")\n");
602 		}
603 
604 		mbs->UO().InstantMessageText(temp.c_str());
605 		assert(0);
606 		exit(0);
607 	}
608 	return errornumber;
609 }
610 
Message(std::string message)611 void TCPIPHotInt::Message(std::string message)
612 {
613 	mbs->UO() << message.c_str() << "\n";
614 }
615 
InfoMessage(std::string info)616 void TCPIPHotInt::InfoMessage(std::string info)
617 {
618 	mbs->UO() << info.c_str() << "\n";
619 }
620