summaryrefslogtreecommitdiffstats
path: root/tests/Network/EchoServer.cpp
blob: 728db0b7cc0e8d03cc39d5c585fd5025e07d7134 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

// EchoServer.cpp

// Implements an Echo server using the LibEvent-based cNetwork API, as a test of that API

#include "Globals.h"
#include <iostream>
#include <string>
#include "OSSupport/Network.h"





/** cTCPLink callbacks that echo everything they receive back to the remote peer. */
class cEchoLinkCallbacks:
	public cTCPLink::cCallbacks
{
	// cTCPLink::cCallbacks overrides:
	virtual void OnLinkCreated(cTCPLinkPtr a_Link) override
	{
		ASSERT(m_Link == nullptr);
		m_Link = a_Link;
	}


	virtual void OnReceivedData(const char * a_Data, size_t a_Size) override
	{
		ASSERT(m_Link != nullptr);

		// Echo the incoming data back to outgoing data:
		LOGD("%p (%s:%d): Data received (%u bytes), echoing back.", m_Link.get(), m_Link->GetRemoteIP().c_str(), m_Link->GetRemotePort(), static_cast<unsigned>(a_Size));
		m_Link->Send(a_Data, a_Size);
		LOGD("Echo queued");

		// Search for a Ctrl+Z, if found, drop the connection:
		for (size_t i = 0; i < a_Size; i++)
		{
			if (a_Data[i] == '\x1a')
			{
				m_Link->Close();
				m_Link.reset();
				return;
			}
		}
	}


	virtual void OnRemoteClosed(void) override
	{
		ASSERT(m_Link != nullptr);

		LOGD("%p (%s:%d): Remote has closed the connection.", m_Link.get(), m_Link->GetRemoteIP().c_str(), m_Link->GetRemotePort());
		m_Link.reset();
	}


	virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
	{
		ASSERT(m_Link != nullptr);

		LOGD("%p (%s:%d): Error %d in the cEchoLinkCallbacks: %s", m_Link.get(), m_Link->GetRemoteIP().c_str(), m_Link->GetRemotePort(), a_ErrorCode, a_ErrorMsg.c_str());
		m_Link.reset();
	}

	/** The link attached to this callbacks instance. */
	cTCPLinkPtr m_Link;
};





class cEchoServerCallbacks:
	public cNetwork::cListenCallbacks
{
	virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort)
	{
		LOGD("New incoming connection(%s:%d).", a_RemoteIPAddress.c_str(), a_RemotePort);
		return std::make_shared<cEchoLinkCallbacks>();
	}

	virtual void OnAccepted(cTCPLink & a_Link) override
	{
		LOGD("New client accepted (%s:%d), sending welcome message.", a_Link.GetRemoteIP().c_str(), a_Link.GetRemotePort());
		// Send a welcome message to each connecting client:
		a_Link.Send("Welcome to the simple echo server.\r\n");
		LOGD("Welcome message queued.");
	}

	virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override
	{
		LOGWARNING("An error occured while listening for connections: %d (%s).", a_ErrorCode, a_ErrorMsg.c_str());
	}
};





int main()
{
	LOGD("EchoServer: starting up");
	cServerHandlePtr Server = cNetwork::Listen(9876, std::make_shared<cEchoServerCallbacks>());
	if (!Server->IsListening())
	{
		LOGWARNING("Cannot listen on port 9876");
		abort();
	}
	ASSERT(Server->IsListening());

	// Wait for the user to terminate the server:
	printf("Press enter to close the server.\n");
	AString line;
	std::getline(std::cin, line);

	// Close the server and all its active connections:
	LOG("Server terminating.");
	Server->Close();
	ASSERT(!Server->IsListening());
	LOGD("Server has been closed.");

	printf("Press enter to exit test.\n");
	std::getline(std::cin, line);

	LOG("Network test finished.");
	return 0;
}