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/socket.h> // For socket(), connect(), send(), and recv()
31 #include <netdb.h> // For gethostbyname()
32 #include <arpa/inet.h> // For inet_addr()
33 #include <unistd.h> // For close()
34 #include <netinet/in.h> // For sockaddr_in
35 typedef void raw_type; // Type used for raw data on this platform
38 #include <errno.h> // For errno
39 #include <string.h> // For memset
44 static bool initialized = false;
47 // SocketException Code
49 SocketException::SocketException(const string &message, bool inclSysMsg)
50 throw() : userMessage(message) {
52 userMessage.append(": ");
53 userMessage.append(strerror(errno));
57 SocketException::~SocketException() throw() {
60 const char *SocketException::what() const throw() {
61 return userMessage.c_str();
64 // Function to fill in address structure given an address and port
65 static void fillAddr(const string &address, unsigned short port,
67 memset(&addr, 0, sizeof(addr)); // Zero out address structure
68 addr.sin_family = AF_INET; // Internet address
70 hostent *host; // Resolve name
71 if ((host = gethostbyname(address.c_str())) == NULL) {
72 // strerror() will not work for gethostbyname() and hstrerror()
73 // is supposedly obsolete
74 throw SocketException("Failed to resolve name (gethostbyname())");
76 addr.sin_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
78 addr.sin_port = htons(port); // Assign port in network byte order
83 Socket::Socket(int type, int protocol) throw(SocketException) {
86 WORD wVersionRequested;
89 wVersionRequested = MAKEWORD(2, 0); // Request WinSock v2.0
90 if (WSAStartup(wVersionRequested, &wsaData) != 0) { // Load WinSock DLL
91 throw SocketException("Unable to load WinSock DLL");
98 if ((sockDesc = socket(PF_INET, type, protocol)) < 0) {
99 throw SocketException("Socket creation failed (socket())", true);
103 Socket::Socket(int sockDesc) {
104 this->sockDesc = sockDesc;
109 ::closesocket(sockDesc);
116 string Socket::getLocalAddress() throw(SocketException) {
118 unsigned int addr_len = sizeof(addr);
120 if (getsockname(sockDesc, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) {
121 throw SocketException("Fetch of local address failed (getsockname())", true);
123 return inet_ntoa(addr.sin_addr);
126 unsigned short Socket::getLocalPort() throw(SocketException) {
128 unsigned int addr_len = sizeof(addr);
130 if (getsockname(sockDesc, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) {
131 throw SocketException("Fetch of local port failed (getsockname())", true);
133 return ntohs(addr.sin_port);
136 void Socket::setLocalPort(unsigned short localPort) throw(SocketException) {
137 // Bind the socket to its port
138 sockaddr_in localAddr;
139 memset(&localAddr, 0, sizeof(localAddr));
140 localAddr.sin_family = AF_INET;
141 localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
142 localAddr.sin_port = htons(localPort);
144 if (bind(sockDesc, (sockaddr *) &localAddr, sizeof(sockaddr_in)) < 0) {
145 throw SocketException("Set of local port failed (bind())", true);
149 void Socket::setLocalAddressAndPort(const string &localAddress,
150 unsigned short localPort) throw(SocketException) {
151 // Get the address of the requested host
152 sockaddr_in localAddr;
153 fillAddr(localAddress, localPort, localAddr);
155 if (bind(sockDesc, (sockaddr *) &localAddr, sizeof(sockaddr_in)) < 0) {
156 throw SocketException("Set of local address and port failed (bind())", true);
160 void Socket::cleanUp() throw(SocketException) {
162 if (WSACleanup() != 0) {
163 throw SocketException("WSACleanup() failed");
168 unsigned short Socket::resolveService(const string &service,
169 const string &protocol) {
170 struct servent *serv; /* Structure containing service information */
172 if ((serv = getservbyname(service.c_str(), protocol.c_str())) == NULL)
173 return atoi(service.c_str()); /* Service is port number */
175 return ntohs(serv->s_port); /* Found port (network byte order) by name */
178 // CommunicatingSocket Code
180 CommunicatingSocket::CommunicatingSocket(int type, int protocol)
181 throw(SocketException) : Socket(type, protocol) {
184 CommunicatingSocket::CommunicatingSocket(int newConnSD) : Socket(newConnSD) {
187 void CommunicatingSocket::connect(const string &foreignAddress,
188 unsigned short foreignPort) throw(SocketException) {
189 // Get the address of the requested host
190 sockaddr_in destAddr;
191 fillAddr(foreignAddress, foreignPort, destAddr);
193 // Try to connect to the given port
194 if (::connect(sockDesc, (sockaddr *) &destAddr, sizeof(destAddr)) < 0) {
195 throw SocketException("Connect failed (connect())", true);
199 void CommunicatingSocket::send(const void *buffer, int bufferLen)
200 throw(SocketException) {
201 if (::send(sockDesc, (raw_type *) buffer, bufferLen, 0) < 0) {
202 throw SocketException("Send failed (send())", true);
206 int CommunicatingSocket::recv(void *buffer, int bufferLen)
207 throw(SocketException) {
209 if ((rtn = ::recv(sockDesc, (raw_type *) buffer, bufferLen, 0)) < 0) {
210 throw SocketException("Received failed (recv())", true);
216 string CommunicatingSocket::getForeignAddress()
217 throw(SocketException) {
219 unsigned int addr_len = sizeof(addr);
221 if (getpeername(sockDesc, (sockaddr *) &addr,(socklen_t *) &addr_len) < 0) {
222 throw SocketException("Fetch of foreign address failed (getpeername())", true);
224 return inet_ntoa(addr.sin_addr);
227 unsigned short CommunicatingSocket::getForeignPort() throw(SocketException) {
229 unsigned int addr_len = sizeof(addr);
231 if (getpeername(sockDesc, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) {
232 throw SocketException("Fetch of foreign port failed (getpeername())", true);
234 return ntohs(addr.sin_port);
239 TCPSocket::TCPSocket()
240 throw(SocketException) : CommunicatingSocket(SOCK_STREAM,
244 TCPSocket::TCPSocket(const string &foreignAddress, unsigned short foreignPort)
245 throw(SocketException) : CommunicatingSocket(SOCK_STREAM, IPPROTO_TCP) {
246 connect(foreignAddress, foreignPort);
249 TCPSocket::TCPSocket(int newConnSD) : CommunicatingSocket(newConnSD) {
252 // TCPServerSocket Code
254 TCPServerSocket::TCPServerSocket(unsigned short localPort, int queueLen)
255 throw(SocketException) : Socket(SOCK_STREAM, IPPROTO_TCP) {
256 setLocalPort(localPort);
260 TCPServerSocket::TCPServerSocket(const string &localAddress,
261 unsigned short localPort, int queueLen)
262 throw(SocketException) : Socket(SOCK_STREAM, IPPROTO_TCP) {
263 setLocalAddressAndPort(localAddress, localPort);
267 TCPSocket *TCPServerSocket::accept() throw(SocketException) {
269 if ((newConnSD = ::accept(sockDesc, NULL, 0)) < 0) {
270 throw SocketException("Accept failed (accept())", true);
273 return new TCPSocket(newConnSD);
276 void TCPServerSocket::setListen(int queueLen) throw(SocketException) {
277 if (listen(sockDesc, queueLen) < 0) {
278 throw SocketException("Set listening socket failed (listen())", true);
284 UDPSocket::UDPSocket() throw(SocketException) : CommunicatingSocket(SOCK_DGRAM,
289 UDPSocket::UDPSocket(unsigned short localPort) throw(SocketException) :
290 CommunicatingSocket(SOCK_DGRAM, IPPROTO_UDP) {
291 setLocalPort(localPort);
295 UDPSocket::UDPSocket(const string &localAddress, unsigned short localPort)
296 throw(SocketException) : CommunicatingSocket(SOCK_DGRAM, IPPROTO_UDP) {
297 setLocalAddressAndPort(localAddress, localPort);
301 void UDPSocket::setBroadcast() {
302 // If this fails, we'll hear about it when we try to send. This will allow
303 // system that cannot broadcast to continue if they don't plan to broadcast
304 int broadcastPermission = 1;
305 setsockopt(sockDesc, SOL_SOCKET, SO_BROADCAST,
306 (raw_type *) &broadcastPermission, sizeof(broadcastPermission));
309 void UDPSocket::disconnect() throw(SocketException) {
310 sockaddr_in nullAddr;
311 memset(&nullAddr, 0, sizeof(nullAddr));
312 nullAddr.sin_family = AF_UNSPEC;
315 if (::connect(sockDesc, (sockaddr *) &nullAddr, sizeof(nullAddr)) < 0) {
317 if (errno != WSAEAFNOSUPPORT) {
319 if (errno != EAFNOSUPPORT) {
321 throw SocketException("Disconnect failed (connect())", true);
326 void UDPSocket::sendTo(const void *buffer, int bufferLen,
327 const string &foreignAddress, unsigned short foreignPort)
328 throw(SocketException) {
329 sockaddr_in destAddr;
330 fillAddr(foreignAddress, foreignPort, destAddr);
332 // Write out the whole buffer as a single message.
333 if (sendto(sockDesc, (raw_type *) buffer, bufferLen, 0,
334 (sockaddr *) &destAddr, sizeof(destAddr)) != bufferLen) {
335 throw SocketException("Send failed (sendto())", true);
339 int UDPSocket::recvFrom(void *buffer, int bufferLen, string &sourceAddress,
340 unsigned short &sourcePort) throw(SocketException) {
341 sockaddr_in clntAddr;
342 socklen_t addrLen = sizeof(clntAddr);
344 if ((rtn = recvfrom(sockDesc, (raw_type *) buffer, bufferLen, 0,
345 (sockaddr *) &clntAddr, (socklen_t *) &addrLen)) < 0) {
346 throw SocketException("Receive failed (recvfrom())", true);
348 sourceAddress = inet_ntoa(clntAddr.sin_addr);
349 sourcePort = ntohs(clntAddr.sin_port);
354 void UDPSocket::setMulticastTTL(unsigned char multicastTTL) throw(SocketException) {
355 if (setsockopt(sockDesc, IPPROTO_IP, IP_MULTICAST_TTL,
356 (raw_type *) &multicastTTL, sizeof(multicastTTL)) < 0) {
357 throw SocketException("Multicast TTL set failed (setsockopt())", true);
361 void UDPSocket::joinGroup(const string &multicastGroup) throw(SocketException) {
362 struct ip_mreq multicastRequest;
364 multicastRequest.imr_multiaddr.s_addr = inet_addr(multicastGroup.c_str());
365 multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY);
366 if (setsockopt(sockDesc, IPPROTO_IP, IP_ADD_MEMBERSHIP,
367 (raw_type *) &multicastRequest,
368 sizeof(multicastRequest)) < 0) {
369 throw SocketException("Multicast group join failed (setsockopt())", true);
373 void UDPSocket::leaveGroup(const string &multicastGroup) throw(SocketException) {
374 struct ip_mreq multicastRequest;
376 multicastRequest.imr_multiaddr.s_addr = inet_addr(multicastGroup.c_str());
377 multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY);
378 if (setsockopt(sockDesc, IPPROTO_IP, IP_DROP_MEMBERSHIP,
379 (raw_type *) &multicastRequest,
380 sizeof(multicastRequest)) < 0) {
381 throw SocketException("Multicast group leave failed (setsockopt())", true);