Skip to content

Commit 63b3ab2

Browse files
xunnanxufacebook-github-bot
authored andcommitted
Add RAII wrapper for socket
Summary: Credit to original author Pieter Noordhuis (pietern) This diff resolves conflicts. Differential Revision: D45437710 fbshipit-source-id: 6a812f78465537877a62806ccf830edc7419cb16
1 parent 847924c commit 63b3ab2

File tree

3 files changed

+282
-0
lines changed

3 files changed

+282
-0
lines changed

gloo/transport/tcp/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ else()
88
"${CMAKE_CURRENT_SOURCE_DIR}/device.cc"
99
"${CMAKE_CURRENT_SOURCE_DIR}/loop.cc"
1010
"${CMAKE_CURRENT_SOURCE_DIR}/pair.cc"
11+
"${CMAKE_CURRENT_SOURCE_DIR}/socket.cc"
1112
"${CMAKE_CURRENT_SOURCE_DIR}/unbound_buffer.cc"
1213
)
1314
list(APPEND GLOO_TRANSPORT_HDRS
@@ -18,6 +19,7 @@ else()
1819
"${CMAKE_CURRENT_SOURCE_DIR}/device.h"
1920
"${CMAKE_CURRENT_SOURCE_DIR}/loop.h"
2021
"${CMAKE_CURRENT_SOURCE_DIR}/pair.h"
22+
"${CMAKE_CURRENT_SOURCE_DIR}/socket.h"
2123
"${CMAKE_CURRENT_SOURCE_DIR}/unbound_buffer.h"
2224
)
2325
endif()

gloo/transport/tcp/socket.cc

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/**
2+
* Copyright (c) 2017-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#include <gloo/transport/tcp/socket.h>
10+
11+
#include <fcntl.h>
12+
#include <netinet/tcp.h>
13+
#include <string.h>
14+
#include <unistd.h>
15+
16+
#include <gloo/common/logging.h>
17+
18+
namespace gloo {
19+
namespace transport {
20+
namespace tcp {
21+
22+
std::shared_ptr<Socket> Socket::createForFamily(sa_family_t ai_family) {
23+
auto rv = socket(ai_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
24+
GLOO_ENFORCE_NE(rv, -1, "socket: ", strerror(errno));
25+
return std::make_shared<Socket>(rv);
26+
}
27+
28+
Socket::Socket(int fd) : fd_(fd) {}
29+
30+
Socket::~Socket() {
31+
if (fd_ >= 0) {
32+
::close(fd_);
33+
}
34+
}
35+
36+
void Socket::reuseAddr(bool on) {
37+
int value = on ? 1 : 0;
38+
auto rv = ::setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
39+
GLOO_ENFORCE_NE(rv, -1, "setsockopt: ", strerror(errno));
40+
}
41+
42+
void Socket::noDelay(bool on) {
43+
int value = on ? 1 : 0;
44+
auto rv = ::setsockopt(fd_, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value));
45+
GLOO_ENFORCE_NE(rv, -1, "setsockopt: ", strerror(errno));
46+
}
47+
48+
void Socket::block(bool on) {
49+
auto rv = fcntl(fd_, F_GETFL);
50+
GLOO_ENFORCE_NE(rv, -1, "fcntl: ", strerror(errno));
51+
if (!on) {
52+
// Set O_NONBLOCK
53+
rv |= O_NONBLOCK;
54+
} else {
55+
// Clear O_NONBLOCK
56+
rv &= ~O_NONBLOCK;
57+
}
58+
rv = fcntl(fd_, F_SETFL, rv);
59+
GLOO_ENFORCE_NE(rv, -1, "fcntl: ", strerror(errno));
60+
}
61+
62+
void Socket::configureTimeout(int opt, std::chrono::milliseconds timeout) {
63+
struct timeval tv = {
64+
.tv_sec = timeout.count() / 1000,
65+
.tv_usec = (timeout.count() % 1000) * 1000,
66+
};
67+
auto rv = setsockopt(fd_, SOL_SOCKET, opt, &tv, sizeof(tv));
68+
GLOO_ENFORCE_NE(rv, -1, "setsockopt: ", strerror(errno));
69+
}
70+
71+
void Socket::recvTimeout(std::chrono::milliseconds timeout) {
72+
configureTimeout(SO_RCVTIMEO, std::move(timeout));
73+
}
74+
75+
void Socket::sendTimeout(std::chrono::milliseconds timeout) {
76+
configureTimeout(SO_SNDTIMEO, std::move(timeout));
77+
}
78+
79+
void Socket::bind(const sockaddr_storage& ss) {
80+
if (ss.ss_family == AF_INET) {
81+
const struct sockaddr_in* sa = (const struct sockaddr_in*)&ss;
82+
bind((const struct sockaddr*)sa, sizeof(*sa));
83+
return;
84+
}
85+
if (ss.ss_family == AF_INET6) {
86+
const struct sockaddr_in6* sa = (const struct sockaddr_in6*)&ss;
87+
bind((const struct sockaddr*)sa, sizeof(*sa));
88+
return;
89+
}
90+
GLOO_ENFORCE(false, "Unknown address family: ", ss.ss_family);
91+
}
92+
93+
void Socket::bind(const struct sockaddr* addr, socklen_t addrlen) {
94+
auto rv = ::bind(fd_, addr, addrlen);
95+
GLOO_ENFORCE_NE(rv, -1, "bind: ", strerror(errno));
96+
}
97+
98+
void Socket::listen(int backlog) {
99+
auto rv = ::listen(fd_, backlog);
100+
GLOO_ENFORCE_NE(rv, -1, "listen: ", strerror(errno));
101+
}
102+
103+
std::shared_ptr<Socket> Socket::accept() {
104+
struct sockaddr_storage addr;
105+
socklen_t addrlen = sizeof(addr);
106+
int rv = -1;
107+
for (;;) {
108+
rv = ::accept(fd_, (struct sockaddr*)&addr, &addrlen);
109+
if (rv == -1) {
110+
if (errno == EINTR) {
111+
continue;
112+
}
113+
// Return empty shared_ptr to indicate failure.
114+
// The caller can assume errno has been set.
115+
return std::shared_ptr<Socket>();
116+
}
117+
break;
118+
}
119+
return std::make_shared<Socket>(rv);
120+
}
121+
122+
void Socket::connect(const sockaddr_storage& ss) {
123+
if (ss.ss_family == AF_INET) {
124+
const struct sockaddr_in* sa = (const struct sockaddr_in*)&ss;
125+
return connect((const struct sockaddr*)sa, sizeof(*sa));
126+
}
127+
if (ss.ss_family == AF_INET6) {
128+
const struct sockaddr_in6* sa = (const struct sockaddr_in6*)&ss;
129+
return connect((const struct sockaddr*)sa, sizeof(*sa));
130+
}
131+
GLOO_ENFORCE(false, "Unknown address family: ", ss.ss_family);
132+
}
133+
134+
void Socket::connect(const struct sockaddr* addr, socklen_t addrlen) {
135+
for (;;) {
136+
auto rv = ::connect(fd_, addr, addrlen);
137+
if (rv == -1) {
138+
if (errno == EINTR) {
139+
continue;
140+
}
141+
if (errno != EINPROGRESS) {
142+
GLOO_ENFORCE_NE(rv, -1, "connect: ", strerror(errno));
143+
}
144+
}
145+
break;
146+
}
147+
}
148+
149+
ssize_t Socket::read(void* buf, size_t count) {
150+
ssize_t rv = -1;
151+
for (;;) {
152+
rv = ::read(fd_, buf, count);
153+
if (rv == -1 && errno == EINTR) {
154+
continue;
155+
}
156+
break;
157+
}
158+
return rv;
159+
}
160+
161+
ssize_t Socket::write(const void* buf, size_t count) {
162+
ssize_t rv = -1;
163+
for (;;) {
164+
rv = ::write(fd_, buf, count);
165+
if (rv == -1 && errno == EINTR) {
166+
continue;
167+
}
168+
break;
169+
}
170+
return rv;
171+
}
172+
173+
Address Socket::sockName() const {
174+
return Address::fromSockName(fd_);
175+
}
176+
177+
Address Socket::peerName() const {
178+
return Address::fromPeerName(fd_);
179+
}
180+
181+
} // namespace tcp
182+
} // namespace transport
183+
} // namespace gloo

gloo/transport/tcp/socket.h

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* Copyright (c) 2017-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#pragma once
10+
11+
#include <netinet/in.h>
12+
#include <sys/socket.h>
13+
14+
#include <chrono>
15+
#include <memory>
16+
17+
#include <gloo/transport/tcp/address.h>
18+
19+
namespace gloo {
20+
namespace transport {
21+
namespace tcp {
22+
23+
class Socket final : public std::enable_shared_from_this<Socket> {
24+
public:
25+
static std::shared_ptr<Socket> createForFamily(sa_family_t ai_family);
26+
27+
explicit Socket(int fd);
28+
29+
~Socket();
30+
31+
// Return underlying file descriptor.
32+
int fd() const {
33+
return fd_;
34+
}
35+
36+
// Release underlying file descriptor.
37+
int release() {
38+
auto fd = fd_;
39+
fd_ = -1;
40+
return fd;
41+
}
42+
43+
// Enable or disable SO_REUSEADDR socket option.
44+
void reuseAddr(bool on);
45+
46+
// Enable or disable TCP_NODELAY socket option.
47+
void noDelay(bool on);
48+
49+
// Configure if the socket is blocking or not.
50+
void block(bool on);
51+
52+
// Configure recv timeout.
53+
void recvTimeout(std::chrono::milliseconds timeout);
54+
55+
// Configure send timeout.
56+
void sendTimeout(std::chrono::milliseconds timeout);
57+
58+
// Bind socket to address.
59+
void bind(const sockaddr_storage& ss);
60+
61+
// Bind socket to address.
62+
void bind(const struct sockaddr* addr, socklen_t addrlen);
63+
64+
// Listen on socket.
65+
void listen(int backlog);
66+
67+
// Accept new socket connecting to listening socket.
68+
std::shared_ptr<Socket> accept();
69+
70+
// Connect to address.
71+
void connect(const sockaddr_storage& ss);
72+
73+
// Connect to address.
74+
void connect(const struct sockaddr* addr, socklen_t addrlen);
75+
76+
// Proxy to read(2) with EINTR retry.
77+
ssize_t read(void* buf, size_t count);
78+
79+
// Proxy to write(2) with EINTR retry.
80+
ssize_t write(const void* buf, size_t count);
81+
82+
// Return address for getsockname(2).
83+
Address sockName() const;
84+
85+
// Return address for getpeername(2).
86+
Address peerName() const;
87+
88+
private:
89+
int fd_;
90+
91+
// Configure send or recv timeout.
92+
void configureTimeout(int opt, std::chrono::milliseconds timeout);
93+
};
94+
95+
} // namespace tcp
96+
} // namespace transport
97+
} // namespace gloo

0 commit comments

Comments
 (0)