2 * C++ sockets on Unix and Windows
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // http://cs.ecs.baylor.edu/~donahoo/practical/CSockets/practical/
25 #include <winsock.h> // For socket(), connect(), send(), and recv()
26 typedef int socklen_t;
27 typedef char raw_type; // Type used for raw data on this platform
29 #include <sys/types.h> // For data types
30 #include <sys/select.h> // For select() - non-blocking socket
31 #include <sys/socket.h> // For socket(), connect(), send(), and recv()
32 #include <netdb.h> // For gethostbyname()
33 #include <arpa/inet.h> // For inet_addr()
34 #include <unistd.h> // For close()
35 #include <netinet/in.h> // For sockaddr_in
36 typedef void raw_type; // Type used for raw data on this platform
39 #include <errno.h> // For errno
40 #include <string.h> // For memset
41 #include <fcntl.h> // For fcntl
46 static bool initialized = false;
49 // SocketException Code
51 SocketException::SocketException(const string &message, bool inclSysMsg)
52 throw() : userMessage(message) {
54 userMessage.append(": ");
55 userMessage.append(strerror(errno));
59 SocketException::~SocketException() throw() {
62 const char *SocketException::what() const throw() {
63 return userMessage.c_str();
66 // Function to fill in address structure given an address and port
67 static void fillAddr(const string &address, unsigned short port,
69 memset(&addr, 0, sizeof(addr)); // Zero out address structure
70 addr.sin_family = AF_INET; // Internet address
72 hostent *host; // Resolve name
73 if ((host = gethostbyname(address.c_str())) == NULL) {
74 // strerror() will not work for gethostbyname() and hstrerror()
75 // is supposedly obsolete
76 throw SocketException("Failed to resolve name (gethostbyname())");
78 addr.sin_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
80 addr.sin_port = htons(port); // Assign port in network byte order
85 Socket::Socket(int type, int protocol) throw(SocketException) {
88 WORD wVersionRequested;
91 wVersionRequested = MAKEWORD(2, 0); // Request WinSock v2.0
92 if (WSAStartup(wVersionRequested, &wsaData) != 0) { // Load WinSock DLL
93 throw SocketException("Unable to load WinSock DLL");
100 if ((sockDesc = socket(PF_INET, type, protocol)) < 0) {
101 throw SocketException("Socket creation failed (socket())", true);
105 Socket::Socket(int sockDesc) {
106 this->sockDesc = sockDesc;
111 ::closesocket(sockDesc);
118 string Socket::getLocalAddress() throw(SocketException) {
120 unsigned int addr_len = sizeof(addr);
122 if (getsockname(sockDesc, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) {
123 throw SocketException("Fetch of local address failed (getsockname())", true);
125 return inet_ntoa(addr.sin_addr);
128 unsigned short Socket::getLocalPort() throw(SocketException) {
130 unsigned int addr_len = sizeof(addr);
132 if (getsockname(sockDesc, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) {
133 throw SocketException("Fetch of local port failed (getsockname())", true);
135 return ntohs(addr.sin_port);
138 void Socket::setLocalPort(unsigned short localPort) throw(SocketException) {
139 // Bind the socket to its port
140 sockaddr_in localAddr;
141 memset(&localAddr, 0, sizeof(localAddr));
142 localAddr.sin_family = AF_INET;
143 localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
144 localAddr.sin_port = htons(localPort);
146 if (bind(sockDesc, (sockaddr *) &localAddr, sizeof(sockaddr_in)) < 0) {
147 throw SocketException("Set of local port failed (bind())", true);
151 void Socket::setLocalAddressAndPort(const string &localAddress,
152 unsigned short localPort) throw(SocketException) {
153 // Get the address of the requested host
154 sockaddr_in localAddr;
155 fillAddr(localAddress, localPort, localAddr);
157 if (bind(sockDesc, (sockaddr *) &localAddr, sizeof(sockaddr_in)) < 0) {
158 throw SocketException("Set of local address and port failed (bind())", true);
162 void Socket::cleanUp() throw(SocketException) {
164 if (WSACleanup() != 0) {
165 throw SocketException("WSACleanup() failed");
170 unsigned short Socket::resolveService(const string &service,
171 const string &protocol) {
172 struct servent *serv; /* Structure containing service information */
174 if ((serv = getservbyname(service.c_str(), protocol.c_str())) == NULL)
175 return atoi(service.c_str()); /* Service is port number */
177 return ntohs(serv->s_port); /* Found port (network byte order) by name */
180 // CommunicatingSocket Code
182 CommunicatingSocket::CommunicatingSocket(int type, int protocol)
183 throw(SocketException) : Socket(type, protocol) {
186 CommunicatingSocket::CommunicatingSocket(int newConnSD) : Socket(newConnSD) {
189 void CommunicatingSocket::connect(const string &foreignAddress,
190 unsigned short foreignPort) throw(SocketException) {
191 // Get the address of the requested host
192 sockaddr_in destAddr;
193 fillAddr(foreignAddress, foreignPort, destAddr);
195 // Try to connect to the given port
196 if (::connect(sockDesc, (sockaddr *) &destAddr, sizeof(destAddr)) < 0) {
197 throw SocketException("Connect failed (connect())", true);
201 void CommunicatingSocket::send(const void *buffer, int bufferLen)
202 throw(SocketException) {
203 if (::send(sockDesc, (raw_type *) buffer, bufferLen, 0) < 0) {
204 throw SocketException("Send failed (send())", true);
208 int CommunicatingSocket::recv(void *buffer, int bufferLen)
209 throw(SocketException) {
211 if ((rtn = ::recv(sockDesc, (raw_type *) buffer, bufferLen, 0)) < 0) {
212 throw SocketException("Received failed (recv())", true);
218 string CommunicatingSocket::getForeignAddress()
219 throw(SocketException) {
221 unsigned int addr_len = sizeof(addr);
223 if (getpeername(sockDesc, (sockaddr *) &addr,(socklen_t *) &addr_len) < 0) {
224 throw SocketException("Fetch of foreign address failed (getpeername())", true);
226 return inet_ntoa(addr.sin_addr);
229 unsigned short CommunicatingSocket::getForeignPort() throw(SocketException) {
231 unsigned int addr_len = sizeof(addr);
233 if (getpeername(sockDesc, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) {
234 throw SocketException("Fetch of foreign port failed (getpeername())", true);
236 return ntohs(addr.sin_port);
241 TCPSocket::TCPSocket()
242 throw(SocketException) : CommunicatingSocket(SOCK_STREAM,
246 TCPSocket::TCPSocket(const string &foreignAddress, unsigned short foreignPort)
247 throw(SocketException) : CommunicatingSocket(SOCK_STREAM, IPPROTO_TCP) {
248 connect(foreignAddress, foreignPort);
251 TCPSocket::TCPSocket(int newConnSD) : CommunicatingSocket(newConnSD) {
254 // TCPServerSocket Code
256 TCPServerSocket::TCPServerSocket(unsigned short localPort, int queueLen)
257 throw(SocketException) : Socket(SOCK_STREAM, IPPROTO_TCP) {
258 setLocalPort(localPort);
262 TCPServerSocket::TCPServerSocket(const string &localAddress,
263 unsigned short localPort, int queueLen)
264 throw(SocketException) : Socket(SOCK_STREAM, IPPROTO_TCP) {
265 setLocalAddressAndPort(localAddress, localPort);
269 TCPSocket *TCPServerSocket::accept() throw(SocketException) {
271 if ((newConnSD = ::accept(sockDesc, NULL, 0)) < 0) {
272 throw SocketException("Accept failed (accept())", true);
275 return new TCPSocket(newConnSD);
278 void TCPServerSocket::setListen(int queueLen) throw(SocketException) {
279 if (listen(sockDesc, queueLen) < 0) {
280 throw SocketException("Set listening socket failed (listen())", true);
286 UDPSocket::UDPSocket() throw(SocketException) : CommunicatingSocket(SOCK_DGRAM,
291 UDPSocket::UDPSocket(unsigned short localPort) throw(SocketException) :
292 CommunicatingSocket(SOCK_DGRAM, IPPROTO_UDP) {
293 setLocalPort(localPort);
297 UDPSocket::UDPSocket(const string &localAddress, unsigned short localPort)
298 throw(SocketException) : CommunicatingSocket(SOCK_DGRAM, IPPROTO_UDP) {
299 setLocalAddressAndPort(localAddress, localPort);
303 void UDPSocket::setBroadcast() {
304 // If this fails, we'll hear about it when we try to send. This will allow
305 // system that cannot broadcast to continue if they don't plan to broadcast
306 int broadcastPermission = 1;
307 setsockopt(sockDesc, SOL_SOCKET, SO_BROADCAST,
308 (raw_type *) &broadcastPermission, sizeof(broadcastPermission));
311 void UDPSocket::disconnect() throw(SocketException) {
312 sockaddr_in nullAddr;
313 memset(&nullAddr, 0, sizeof(nullAddr));
314 nullAddr.sin_family = AF_UNSPEC;
317 if (::connect(sockDesc, (sockaddr *) &nullAddr, sizeof(nullAddr)) < 0) {
319 if (errno != WSAEAFNOSUPPORT) {
321 if (errno != EAFNOSUPPORT) {
323 throw SocketException("Disconnect failed (connect())", true);
328 void UDPSocket::sendTo(const void *buffer, int bufferLen,
329 const string &foreignAddress, unsigned short foreignPort)
330 throw(SocketException) {
331 sockaddr_in destAddr;
332 fillAddr(foreignAddress, foreignPort, destAddr);
334 // Write out the whole buffer as a single message.
335 if (sendto(sockDesc, (raw_type *) buffer, bufferLen, 0,
336 (sockaddr *) &destAddr, sizeof(destAddr)) != bufferLen) {
337 throw SocketException("Send failed (sendto())", true);
341 int UDPSocket::recvFrom(void *buffer, int bufferLen, string &sourceAddress,
342 unsigned short &sourcePort) throw(SocketException) {
343 sockaddr_in clntAddr;
344 socklen_t addrLen = sizeof(clntAddr);
346 if ((rtn = recvfrom(sockDesc, (raw_type *) buffer, bufferLen, 0,
347 (sockaddr *) &clntAddr, (socklen_t *) &addrLen)) < 0) {
348 throw SocketException("Receive failed (recvfrom())", true);
350 sourceAddress = inet_ntoa(clntAddr.sin_addr);
351 sourcePort = ntohs(clntAddr.sin_port);
356 int UDPSocket::recvFrom(void *buffer, int bufferLen, string &sourceAddress,
357 unsigned short &sourcePort, int timeOut) throw(SocketException) {
359 if(!(fcntl(sockDesc, F_GETFL) & O_NONBLOCK)) {
360 // If not non-blocking then put it into non-blocking
361 if(fcntl(sockDesc, F_SETFL, fcntl(sockDesc, F_GETFL) | O_NONBLOCK) < 0) {
362 throw SocketException("Set non-blocking failed (recvfrom())", true);
365 fd_set readFds; // FD
367 FD_SET(sockDesc, &readFds);
368 struct timeval to; // timeout
370 to.tv_usec = timeOut;
371 sockaddr_in clntAddr;
372 socklen_t addrLen = sizeof(clntAddr);
374 int rv = select(sockDesc+1 ,&readFds, NULL, NULL, &to);
375 int rtn = -1; // Assuming receive failure
377 if ((rtn = recvfrom(sockDesc, (raw_type *) buffer, bufferLen, 0,
378 (sockaddr *) &clntAddr, (socklen_t *) &addrLen)) < 0) {
379 throw SocketException("Receive failed (recvfrom())", true);
381 sourceAddress = inet_ntoa(clntAddr.sin_addr);
382 sourcePort = ntohs(clntAddr.sin_port);
388 void UDPSocket::setMulticastTTL(unsigned char multicastTTL) throw(SocketException) {
389 if (setsockopt(sockDesc, IPPROTO_IP, IP_MULTICAST_TTL,
390 (raw_type *) &multicastTTL, sizeof(multicastTTL)) < 0) {
391 throw SocketException("Multicast TTL set failed (setsockopt())", true);
395 void UDPSocket::joinGroup(const string &multicastGroup) throw(SocketException) {
396 struct ip_mreq multicastRequest;
398 multicastRequest.imr_multiaddr.s_addr = inet_addr(multicastGroup.c_str());
399 multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY);
400 if (setsockopt(sockDesc, IPPROTO_IP, IP_ADD_MEMBERSHIP,
401 (raw_type *) &multicastRequest,
402 sizeof(multicastRequest)) < 0) {
403 throw SocketException("Multicast group join failed (setsockopt())", true);
407 void UDPSocket::leaveGroup(const string &multicastGroup) throw(SocketException) {
408 struct ip_mreq multicastRequest;
410 multicastRequest.imr_multiaddr.s_addr = inet_addr(multicastGroup.c_str());
411 multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY);
412 if (setsockopt(sockDesc, IPPROTO_IP, IP_DROP_MEMBERSHIP,
413 (raw_type *) &multicastRequest,
414 sizeof(multicastRequest)) < 0) {
415 throw SocketException("Multicast group leave failed (setsockopt())", true);
419 void UDPSocket::setTimeOut(int interval) throw(SocketException) {
421 struct timeval timeout;
423 timeout.tv_usec = interval;
424 if (setsockopt(sockDesc, SOL_SOCKET, SO_RCVTIMEO, &timeout,
425 sizeof(timeout)) < 0) {
426 throw SocketException("Multicast group join failed (setsockopt())", true);