-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTCPInterface.cpp
190 lines (155 loc) · 7.1 KB
/
TCPInterface.cpp
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#include "TCPInterface.h"
/// @brief Constructor.
/// @param mode The mode for this interface to act in. Can be either TCPCLIENT or TCPSERVER.
/// @param port The server port which will, in the case of the client, be connected to or, in the
/// case of the server, be used to accept incoming connections.
TCPInterface::TCPInterface (TCPInterfaceMode mode, int port) : mMode(mode),
mServerPort(port)
{
if (mode == TCPCLIENT)
setupClient(SERVER_ADDRESS);
else
setupServer();
}
/// @brief Deconstructor.
TCPInterface::~TCPInterface (void)
{
if (mMode == TCPCLIENT)
shutdownClient();
else
shutdownServer();
}
/// @brief Configures and starts a TCP server at the given port on the local machine.
void TCPInterface::setupServer (void)
{
CHECK_TCPMODE(TCPSERVER);
int retVal;
// Create an IP streaming socket (which uses TCP).
mServerSocket = socket(PF_INET, SOCK_STREAM, 0);
CHECK_RETURN(mServerSocket, "socket");
// Set SO_REUSEADDR so that we can rebind to the same addr/port (as the server) after an unclean shutdown.
int socket_optval = 1;
setsockopt(mServerSocket, SOL_SOCKET, SO_REUSEADDR, &socket_optval, sizeof(socket_optval));
// Bind to the port, setting up the address of the server given by the defines.
// Note that both address and port number must be in network byte order.
memset(&mServerAddress, 0, sizeof(mServerAddress));
mServerAddress.sin_family = AF_INET;
mServerAddress.sin_port = htons(mServerPort); // host to network short: converts a u_short from host to TCP network byte order (big-endian).
retVal = bind(mServerSocket, (struct sockaddr*) &mServerAddress, sizeof(mServerAddress));
CHECK_RETURN(retVal, "bind");
// Set to listen on the port with a connection queue length of 1.
retVal = listen(mServerSocket, 1);
CHECK_RETURN(retVal, "listen");
// turn off read blocking
setBlocking(false);
}
/// @brief Configures and starts a TCP client and loads the data for the server to connect to
/// (though it does not connect at this stage, see the connectToServer() method).
void TCPInterface::setupClient (const char* serverAddress)
{
CHECK_TCPMODE(TCPCLIENT);
int retVal;
// Create an IP streaming socket (which uses TCP).
mClientSocket = socket(PF_INET, SOCK_STREAM, 0);
CHECK_RETURN(mClientSocket, "socket");
// Set up the address of the server as given by the defines.
// Note that both address and port number must be in network byte order.
memset(&mServerAddress, 0, sizeof(mServerAddress));
mServerAddress.sin_family = AF_INET;
mServerAddress.sin_port = htons(mServerPort); // host to network short: converts to TCP network byte order (big-endian).
retVal = inet_aton(serverAddress, &(mServerAddress.sin_addr));
CHECK_RETURN(retVal, "inet_aton"); // NB: There was originally a close(client_socket) in this if.
}
/// @brief Shuts down the server, closing all sockets (thus terminating all connections).
void TCPInterface::shutdownServer (void)
{
CHECK_TCPMODE(TCPSERVER);
close(mServerSocket);
close(mClientSocket);
}
/// @brief Shuts down the client, closing all sockets (thus terminating all connections).
void TCPInterface::shutdownClient (void)
{
CHECK_TCPMODE(TCPCLIENT);
close(mClientSocket);
}
/// @brief Sets whether the server port is blocking or not, i.e. if accepting connections from the
/// port will block if no connections are currently available. This is only available in
/// server mode for the time being.
/// @param blocking Whether or not accepting connections will block. This defaults to true when
/// you open a connection.
void TCPInterface::setBlocking (bool_t blocking)
{
CHECK_TCPMODE(TCPSERVER); // TODO should this work in client mode?
if (blocking)
fcntl(mClientSocket, F_SETFL, !O_NONBLOCK); // TODO is this the right socket for server mode?
else
fcntl(mClientSocket, F_SETFL, O_NONBLOCK);
}
/// @brief Connects to the server (whose details are either defined or given as to the constructor).
void TCPInterface::connectToServer (void)
{
CHECK_TCPMODE(TCPCLIENT);
int retVal;
printf("Waiting to connect to server on %s:%d...\n", SERVER_ADDRESS, mServerPort);
retVal = connect(mClientSocket, (struct sockaddr*) &mServerAddress, sizeof(mServerAddress));
CHECK_RETURN(retVal, "connect");
printf("Connected.\n");
}
/// @brief Connects to a client, if one has requested to join, or waits until one requests to do
/// so. Blocking mode must be set to true.
/// FOR NON-BLOCKING CONNECTIONS USE checkForClients()!
void TCPInterface::connectToClient (void)
{
CHECK_TCPMODE(TCPSERVER);
socklen_t sizeofmClientAddress = sizeof(mClientAddress);
mClientSocket = accept(mServerSocket, (struct sockaddr*) &mClientAddress, &sizeofmClientAddress);
CHECK_RETURN(mClientSocket, "accept");
printf("Connection from %s:%d.\n", inet_ntoa(mClientAddress.sin_addr), htons(mClientAddress.sin_port));
}
/// @brief Waits for the server to become ready and then connects to it.
void TCPInterface::waitForServerConnection (void)
{
connectToServer();
}
/// @brief Checks whether any clients have requested to join and, if they have, accepts them. If
/// not the function will return. Blocking mode must be set to false.
/// FOR BLOCKING CONNECTIONS USE connectToClient()!
/// @return Whether or not a client was found (true = yes, false = no).
bool TCPInterface::checkForClients (void)
{
CHECK_TCPMODE(TCPSERVER);
socklen_t sizeofmClientAddress = sizeof(mClientAddress);
mClientSocket = accept(mServerSocket, (struct sockaddr*) &mClientAddress, &sizeofmClientAddress);
if ((mClientSocket < 0) && (errno == EWOULDBLOCK))
return false;
CHECK_RETURN(mClientSocket, "accept");
printf("Connection from %s:%d.\n", inet_ntoa(mClientAddress.sin_addr), htons(mClientAddress.sin_port));
return true;
}
/// @brief Writes the supplied bytes to the socket (sending them to the other party).
/// @param bs The bytes to write.
/// @param n The number of bytes from bs to write.
int TCPInterface::writeBytes (const void* bs, size_t n)
{
//int retVal = write(mClientSocket, bs, n);
//CHECK_RETURN(retVal, "writeBytes");
return write(mClientSocket, bs, n);
}
/// @brief Reads the given number of bytes from the socket. This will block until the requested
/// number of bytes have been read (unlike the read command by default which will read only
/// as many bytes as are available).
/// @param bs The byte array to read into.
/// @param n The number of bytes to read.
void TCPInterface::readBytes (void* bs, size_t n)
{
int bytesRead;
size_t totalBytesRead = 0;
while ((bytesRead = read(mClientSocket, (((uint8_t*) bs) + totalBytesRead), (n - totalBytesRead))) > 0)
{
totalBytesRead += bytesRead;
if (totalBytesRead >= n)
return;
}
CHECK_RETURN(bytesRead, "readBytes");
}