URING++
Loading...
Searching...
No Matches
tcp_listener.h
1#pragma once
2
3#include <memory>
4#include <stdexcept>
5#include <sys/socket.h>
6#include <utility>
7
8#include "uringpp/event_loop.h"
9#include "uringpp/ip_address.h"
10#include "uringpp/socket.h"
11
12namespace uringpp {
13
14static inline std::string get_in_addr_string(struct addrinfo *ai) {
15 char s[INET6_ADDRSTRLEN];
16 inet_ntop(ai->ai_family, get_in_addr(ai->ai_addr), s, sizeof(s));
17 return s;
18}
19
24class tcp_listener : public noncopyable {
25 std::shared_ptr<event_loop> loop_;
26 int fd_;
27 tcp_listener(std::shared_ptr<event_loop> loop, int fd)
28 : loop_(loop), fd_(fd) {}
29
30public:
36 int fd() const { return fd_; }
37
47 static tcp_listener listen(std::shared_ptr<event_loop> loop,
48 std::string const &hostname,
49 std::string const &port, int backlog = 128) {
50 struct addrinfo hints, *servinfo, *p;
51 ::bzero(&hints, sizeof(hints));
52 hints.ai_family = AF_UNSPEC;
53 hints.ai_socktype = SOCK_STREAM;
54 hints.ai_flags = AI_PASSIVE;
55
56 if (auto rc = getaddrinfo(hostname.empty() ? nullptr : hostname.c_str(),
57 port.c_str(), &hints, &servinfo);
58 rc != 0) {
59 throw_with("getaddrinfo: %s", gai_strerror(rc));
60 }
61 for (p = servinfo; p != nullptr; p = p->ai_next) {
62 try {
63 int fd = ::socket(p->ai_family, p->ai_flags, p->ai_protocol);
64 check_errno(fd, "failed to create socket");
65 {
66 int32_t yes = 1;
67 check_rc(
68 ::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)),
69 "failed to set reuse address");
70 }
71 auto const &ip = get_in_addr_string(p);
72 check_errno(::bind(fd, p->ai_addr, p->ai_addrlen), "failed to bind");
73 check_errno(::listen(fd, backlog), "failed to listen");
74 URINGPP_LOG_DEBUG("binding %s:%s", ip.c_str(), port.c_str());
75 ::freeaddrinfo(servinfo);
76 return tcp_listener(loop, fd);
77 } catch (std::runtime_error &e) {
78 URINGPP_LOG_ERROR("%s", e.what());
79 continue;
80 }
81 break;
82 }
83 throw std::runtime_error("try all addresses, failed to listen");
84 }
85
91 tcp_listener(tcp_listener &&other) noexcept
92 : loop_(std::move(other.loop_)), fd_(std::exchange(other.fd_, -1)) {}
93
101 ip_address addr;
102 auto fd = co_await loop_->accept(
103 fd_, reinterpret_cast<struct sockaddr *>(&addr.ss_), &addr.len_);
104 check_nerrno(fd, "failed to accept connection");
105 co_return std::make_pair(addr, socket(loop_, fd));
106 }
107
114 task<std::pair<ip_address, socket>> accept(std::shared_ptr<event_loop> loop) {
115 ip_address addr;
116 auto fd = co_await loop_->accept(
117 fd_, reinterpret_cast<struct sockaddr *>(&addr.ss_), &addr.len_);
118 check_nerrno(fd, "failed to accept connection");
119 co_return std::make_pair(addr, socket(loop, fd));
120 }
121
128 if (fd_ > 0) {
129 co_await loop_->close(fd_);
130 fd_ = -1;
131 }
132 }
133
140 if (fd_ > 0) {
141 loop_->close_detach(fd_);
142 }
143 }
144};
145
146} // namespace uringpp
Definition: ip_address.h:28
Definition: noncopyable.h:5
Definition: socket.h:25
Listen for incoming TCP connections on a socket.
Definition: tcp_listener.h:24
static tcp_listener listen(std::shared_ptr< event_loop > loop, std::string const &hostname, std::string const &port, int backlog=128)
Listen for incoming TCP connections on the given address. It will create a socket and bind it to the ...
Definition: tcp_listener.h:47
task< void > close()
Close the listener.
Definition: tcp_listener.h:127
task< std::pair< ip_address, socket > > accept(std::shared_ptr< event_loop > loop)
Accept an incoming connection and attach it with the specified loop.
Definition: tcp_listener.h:114
tcp_listener(tcp_listener &&other) noexcept
Move construct a new listener object.
Definition: tcp_listener.h:91
~tcp_listener()
Destroy the listener object. If the socket is still open, it will be closed.
Definition: tcp_listener.h:139
task< std::pair< ip_address, socket > > accept()
Accept an incoming connection.
Definition: tcp_listener.h:100
int fd() const
Get the file descriptor of the listener.
Definition: tcp_listener.h:36
Definition: task.h:53