blob: d4e36714eff6830de701a330900023650b54d05f [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*
2 TCPStream.h
3
4 TCPStream class definition. TCPStream provides methods to trasnfer
5 data between peers over a TCP/IP connection.
6
7 ------------------------------------------
8
9 Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
10
11 Licensed under the Apache License, Version 2.0 (the "License");
12 you may not use this file except in compliance with the License.
13 You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22*/
23
24#include "wpi/TCPStream.h"
25
26#include <fcntl.h>
27
28#ifdef _WIN32
29#include <winsock2.h>
30#include <ws2tcpip.h>
31#else
32#include <arpa/inet.h>
33#include <netinet/tcp.h>
34#include <unistd.h>
35#endif
36
37#include <cerrno>
38
39using namespace wpi;
40
41TCPStream::TCPStream(int sd, sockaddr_in* address)
42 : m_sd(sd), m_blocking(true) {
43 char ip[50];
44#ifdef _WIN32
45 InetNtop(PF_INET, &(address->sin_addr.s_addr), ip, sizeof(ip) - 1);
46#else
47 inet_ntop(PF_INET, reinterpret_cast<in_addr*>(&(address->sin_addr.s_addr)),
48 ip, sizeof(ip) - 1);
49#ifdef SO_NOSIGPIPE
50 // disable SIGPIPE on Mac OS X
51 int set = 1;
52 setsockopt(m_sd, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char*>(&set),
53 sizeof set);
54#endif
55#endif
56 m_peerIP = ip;
57 m_peerPort = ntohs(address->sin_port);
58}
59
60TCPStream::~TCPStream() { close(); }
61
62size_t TCPStream::send(const char* buffer, size_t len, Error* err) {
63 if (m_sd < 0) {
64 *err = kConnectionClosed;
65 return 0;
66 }
67#ifdef _WIN32
68 WSABUF wsaBuf;
69 wsaBuf.buf = const_cast<char*>(buffer);
70 wsaBuf.len = (ULONG)len;
71 DWORD rv;
72 bool result = true;
73 while (WSASend(m_sd, &wsaBuf, 1, &rv, 0, nullptr, nullptr) == SOCKET_ERROR) {
74 if (WSAGetLastError() != WSAEWOULDBLOCK) {
75 result = false;
76 break;
77 }
78 if (!m_blocking) {
79 *err = kWouldBlock;
80 return 0;
81 }
82 Sleep(1);
83 }
84 if (!result) {
85 char Buffer[128];
86#ifdef _MSC_VER
87 sprintf_s(Buffer, "Send() failed: WSA error=%d\n", WSAGetLastError());
88#else
89 std::snprintf(Buffer, sizeof(Buffer), "Send() failed: WSA error=%d\n",
90 WSAGetLastError());
91#endif
92 OutputDebugStringA(Buffer);
93 *err = kConnectionReset;
94 return 0;
95 }
96#else
97#ifdef MSG_NOSIGNAL
98 // disable SIGPIPE on Linux
99 ssize_t rv = ::send(m_sd, buffer, len, MSG_NOSIGNAL);
100#else
101 ssize_t rv = ::send(m_sd, buffer, len, 0);
102#endif
103 if (rv < 0) {
104 if (!m_blocking && (errno == EAGAIN || errno == EWOULDBLOCK))
105 *err = kWouldBlock;
106 else
107 *err = kConnectionReset;
108 return 0;
109 }
110#endif
111 return static_cast<size_t>(rv);
112}
113
114size_t TCPStream::receive(char* buffer, size_t len, Error* err, int timeout) {
115 if (m_sd < 0) {
116 *err = kConnectionClosed;
117 return 0;
118 }
119#ifdef _WIN32
120 int rv;
121#else
122 ssize_t rv;
123#endif
124 if (timeout <= 0) {
125#ifdef _WIN32
126 rv = recv(m_sd, buffer, len, 0);
127#else
128 rv = read(m_sd, buffer, len);
129#endif
130 } else if (WaitForReadEvent(timeout)) {
131#ifdef _WIN32
132 rv = recv(m_sd, buffer, len, 0);
133#else
134 rv = read(m_sd, buffer, len);
135#endif
136 } else {
137 *err = kConnectionTimedOut;
138 return 0;
139 }
140 if (rv < 0) {
141#ifdef _WIN32
142 if (!m_blocking && WSAGetLastError() == WSAEWOULDBLOCK)
143#else
144 if (!m_blocking && (errno == EAGAIN || errno == EWOULDBLOCK))
145#endif
146 *err = kWouldBlock;
147 else
148 *err = kConnectionReset;
149 return 0;
150 }
151 return static_cast<size_t>(rv);
152}
153
154void TCPStream::close() {
155 if (m_sd >= 0) {
156#ifdef _WIN32
157 ::shutdown(m_sd, SD_BOTH);
158 closesocket(m_sd);
159#else
160 ::shutdown(m_sd, SHUT_RDWR);
161 ::close(m_sd);
162#endif
163 }
164 m_sd = -1;
165}
166
167StringRef TCPStream::getPeerIP() const { return m_peerIP; }
168
169int TCPStream::getPeerPort() const { return m_peerPort; }
170
171void TCPStream::setNoDelay() {
172 if (m_sd < 0) return;
173 int optval = 1;
174 setsockopt(m_sd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&optval),
175 sizeof optval);
176}
177
178bool TCPStream::setBlocking(bool enabled) {
179 if (m_sd < 0) return true; // silently accept
180#ifdef _WIN32
181 u_long mode = enabled ? 0 : 1;
182 if (ioctlsocket(m_sd, FIONBIO, &mode) == SOCKET_ERROR) return false;
183#else
184 int flags = fcntl(m_sd, F_GETFL, nullptr);
185 if (flags < 0) return false;
186 if (enabled)
187 flags &= ~O_NONBLOCK;
188 else
189 flags |= O_NONBLOCK;
190 if (fcntl(m_sd, F_SETFL, flags) < 0) return false;
191#endif
192 return true;
193}
194
195int TCPStream::getNativeHandle() const { return m_sd; }
196
197bool TCPStream::WaitForReadEvent(int timeout) {
198 fd_set sdset;
199 struct timeval tv;
200
201 tv.tv_sec = timeout;
202 tv.tv_usec = 0;
203 FD_ZERO(&sdset);
204 FD_SET(m_sd, &sdset);
205 if (select(m_sd + 1, &sdset, NULL, NULL, &tv) > 0) {
206 return true;
207 }
208 return false;
209}