blob: 45671611d4645e16b4aff2de0f6f060a63a23939 [file] [log] [blame]
Brian Silverman8fce7482020-01-05 13:18:21 -08001/*
2 TCPStream.h
3
Austin Schuh1e69f942020-11-14 15:06:14 -08004 TCPStream class definition. TCPStream provides methods to transfer
Brian Silverman8fce7482020-01-05 13:18:21 -08005 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
Austin Schuh812d0d12021-11-04 20:16:48 -070060TCPStream::~TCPStream() {
61 close();
62}
Brian Silverman8fce7482020-01-05 13:18:21 -080063
64size_t TCPStream::send(const char* buffer, size_t len, Error* err) {
65 if (m_sd < 0) {
66 *err = kConnectionClosed;
67 return 0;
68 }
69#ifdef _WIN32
70 WSABUF wsaBuf;
71 wsaBuf.buf = const_cast<char*>(buffer);
72 wsaBuf.len = (ULONG)len;
73 DWORD rv;
74 bool result = true;
75 while (WSASend(m_sd, &wsaBuf, 1, &rv, 0, nullptr, nullptr) == SOCKET_ERROR) {
76 if (WSAGetLastError() != WSAEWOULDBLOCK) {
77 result = false;
78 break;
79 }
80 if (!m_blocking) {
81 *err = kWouldBlock;
82 return 0;
83 }
84 Sleep(1);
85 }
86 if (!result) {
87 char Buffer[128];
88#ifdef _MSC_VER
89 sprintf_s(Buffer, "Send() failed: WSA error=%d\n", WSAGetLastError());
90#else
91 std::snprintf(Buffer, sizeof(Buffer), "Send() failed: WSA error=%d\n",
92 WSAGetLastError());
93#endif
94 OutputDebugStringA(Buffer);
95 *err = kConnectionReset;
96 return 0;
97 }
98#else
99#ifdef MSG_NOSIGNAL
100 // disable SIGPIPE on Linux
101 ssize_t rv = ::send(m_sd, buffer, len, MSG_NOSIGNAL);
102#else
103 ssize_t rv = ::send(m_sd, buffer, len, 0);
104#endif
105 if (rv < 0) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700106 if (!m_blocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800107 *err = kWouldBlock;
Austin Schuh812d0d12021-11-04 20:16:48 -0700108 } else {
Brian Silverman8fce7482020-01-05 13:18:21 -0800109 *err = kConnectionReset;
Austin Schuh812d0d12021-11-04 20:16:48 -0700110 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800111 return 0;
112 }
113#endif
114 return static_cast<size_t>(rv);
115}
116
117size_t TCPStream::receive(char* buffer, size_t len, Error* err, int timeout) {
118 if (m_sd < 0) {
119 *err = kConnectionClosed;
120 return 0;
121 }
122#ifdef _WIN32
123 int rv;
124#else
125 ssize_t rv;
126#endif
127 if (timeout <= 0) {
128#ifdef _WIN32
129 rv = recv(m_sd, buffer, len, 0);
130#else
131 rv = read(m_sd, buffer, len);
132#endif
133 } else if (WaitForReadEvent(timeout)) {
134#ifdef _WIN32
135 rv = recv(m_sd, buffer, len, 0);
136#else
137 rv = read(m_sd, buffer, len);
138#endif
139 } else {
140 *err = kConnectionTimedOut;
141 return 0;
142 }
143 if (rv < 0) {
144#ifdef _WIN32
Austin Schuh812d0d12021-11-04 20:16:48 -0700145 if (!m_blocking && WSAGetLastError() == WSAEWOULDBLOCK) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800146#else
Austin Schuh812d0d12021-11-04 20:16:48 -0700147 if (!m_blocking && (errno == EAGAIN || errno == EWOULDBLOCK)) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800148#endif
149 *err = kWouldBlock;
Austin Schuh812d0d12021-11-04 20:16:48 -0700150 } else {
Brian Silverman8fce7482020-01-05 13:18:21 -0800151 *err = kConnectionReset;
Austin Schuh812d0d12021-11-04 20:16:48 -0700152 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800153 return 0;
154 }
155 return static_cast<size_t>(rv);
156}
157
158void TCPStream::close() {
159 if (m_sd >= 0) {
160#ifdef _WIN32
161 ::shutdown(m_sd, SD_BOTH);
162 closesocket(m_sd);
163#else
164 ::shutdown(m_sd, SHUT_RDWR);
165 ::close(m_sd);
166#endif
167 }
168 m_sd = -1;
169}
170
Austin Schuh812d0d12021-11-04 20:16:48 -0700171std::string_view TCPStream::getPeerIP() const {
172 return m_peerIP;
173}
Brian Silverman8fce7482020-01-05 13:18:21 -0800174
Austin Schuh812d0d12021-11-04 20:16:48 -0700175int TCPStream::getPeerPort() const {
176 return m_peerPort;
177}
Brian Silverman8fce7482020-01-05 13:18:21 -0800178
179void TCPStream::setNoDelay() {
Austin Schuh812d0d12021-11-04 20:16:48 -0700180 if (m_sd < 0) {
181 return;
182 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800183 int optval = 1;
184 setsockopt(m_sd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&optval),
185 sizeof optval);
186}
187
188bool TCPStream::setBlocking(bool enabled) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700189 if (m_sd < 0) {
190 return true; // silently accept
191 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800192#ifdef _WIN32
193 u_long mode = enabled ? 0 : 1;
Austin Schuh812d0d12021-11-04 20:16:48 -0700194 if (ioctlsocket(m_sd, FIONBIO, &mode) == SOCKET_ERROR) {
195 return false;
196 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800197#else
198 int flags = fcntl(m_sd, F_GETFL, nullptr);
Austin Schuh812d0d12021-11-04 20:16:48 -0700199 if (flags < 0) {
200 return false;
201 }
202 if (enabled) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800203 flags &= ~O_NONBLOCK;
Austin Schuh812d0d12021-11-04 20:16:48 -0700204 } else {
Brian Silverman8fce7482020-01-05 13:18:21 -0800205 flags |= O_NONBLOCK;
Austin Schuh812d0d12021-11-04 20:16:48 -0700206 }
207 if (fcntl(m_sd, F_SETFL, flags) < 0) {
208 return false;
209 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800210#endif
211 return true;
212}
213
Austin Schuh812d0d12021-11-04 20:16:48 -0700214int TCPStream::getNativeHandle() const {
215 return m_sd;
216}
Brian Silverman8fce7482020-01-05 13:18:21 -0800217
218bool TCPStream::WaitForReadEvent(int timeout) {
219 fd_set sdset;
220 struct timeval tv;
221
222 tv.tv_sec = timeout;
223 tv.tv_usec = 0;
224 FD_ZERO(&sdset);
225 FD_SET(m_sd, &sdset);
Austin Schuh812d0d12021-11-04 20:16:48 -0700226 if (select(m_sd + 1, &sdset, nullptr, nullptr, &tv) > 0) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800227 return true;
228 }
229 return false;
230}