]> git.localhorst.tv Git - blank.git/blob - src/net/tcp.cpp
f9e039b96aa3d160d81b5ad9ba1c6ec567a41fa0
[blank.git] / src / net / tcp.cpp
1 #include "tcp.hpp"
2
3 #include "../app/init.hpp"
4
5 #include <stdexcept>
6
7 using namespace std;
8
9
10 namespace blank {
11 namespace tcp {
12
13 Socket::Socket()
14 : sock(nullptr) {
15
16 }
17
18 Socket::Socket(unsigned short port)
19 : sock(nullptr) {
20         IPaddress ip;
21         if (SDLNet_ResolveHost(&ip, nullptr, port) == -1) {
22                 throw NetError("failed to resolve local host");
23         }
24         sock = SDLNet_TCP_Open(&ip);
25         if (!sock) {
26                 throw NetError("failed to open local socket");
27         }
28 }
29
30 Socket::Socket(TCPsocket sock)
31 : sock(sock) {
32
33 }
34
35 Socket::~Socket() noexcept {
36         if (sock) {
37                 SDLNet_TCP_Close(sock);
38         }
39 }
40
41 Socket::Socket(Socket &&other) noexcept
42 : sock(other.sock) {
43         other.sock = nullptr;
44 }
45
46 Socket &Socket::operator =(Socket &&other) noexcept {
47         swap(sock, other.sock);
48         return *this;
49 }
50
51
52 Socket Socket::Accept() noexcept {
53         return Socket(SDLNet_TCP_Accept(sock));
54 }
55
56 bool Socket::Ready() const noexcept {
57         return SDLNet_SocketReady(sock);
58 }
59
60 size_t Socket::Recv(void *buf, std::size_t max_len) {
61         const int len = SDLNet_TCP_Recv(sock, buf, max_len);
62         if (len < 0) {
63                 throw NetError("TCP socket recv");
64         }
65         return len;
66 }
67
68 size_t Socket::Send(const void *buf, size_t max_len) {
69         /// TODO: make TCP send non-blocking
70         const int len = SDLNet_TCP_Send(sock, buf, max_len);
71         if (len < int(max_len)) {
72                 throw NetError("TCP socket send");
73         }
74         return len;
75 }
76
77
78 int Socket::AddTo(SDLNet_SocketSet set) {
79         return SDLNet_TCP_AddSocket(set, sock);
80 }
81
82 int Socket::RemoveFrom(SDLNet_SocketSet set) {
83         return SDLNet_TCP_DelSocket(set, sock);
84 }
85
86
87 Pool::Pool(int max_conn, size_t buf_siz)
88 : set(SDLNet_AllocSocketSet(max_conn))
89 , buffer(buf_siz, '\0')
90 , connections()
91 , use_conn(0)
92 , max_conn(max_conn)
93 , buf_siz(buf_siz) {
94         if (!set) {
95                 throw runtime_error("failed to allocate socket set");
96         }
97 }
98
99 Pool::~Pool() noexcept {
100         SDLNet_FreeSocketSet(set);
101 }
102
103
104 void Pool::AddConnection(Socket sock, IOHandler *handler) {
105         if (FreeSlots() == 0) {
106                 Resize(TotalSlots() * 2);
107         }
108         int num = sock.AddTo(set);
109         if (num < 0) {
110                 throw NetError("failed to add socket to set");
111         }
112         use_conn = num;
113         connections.emplace_back(move(sock), handler);
114         handler->OnCreate(connections.back().first);
115 }
116
117 void Pool::Send() {
118         for (auto i = connections.begin(); i != connections.end(); ++i) {
119                 if (i->second->Closed()) {
120                         continue;
121                 }
122
123                 try {
124                         i->second->OnSend(i->first);
125                 } catch (...) {
126                         i->second->OnError(i->first);
127                 }
128         }
129 }
130
131 bool Pool::Check(unsigned long timeout) {
132         // SDL_net considers checking an empty set an error, so
133         // we're checking that ourselves
134         if (OccupiedSlots() == 0) {
135                 return false;
136         }
137
138         int num = SDLNet_CheckSockets(set, timeout);
139         if (num < 0) {
140                 throw NetError("error checking sockets");
141         }
142         return num > 0;
143 }
144
145 void Pool::Receive() {
146         for (auto i = connections.begin(); i != connections.end(); ++i) {
147                 if (!i->first.Ready() || i->second->Closed()) {
148                         continue;
149                 }
150
151                 try {
152                         i->second->OnRecv(i->first);
153                 } catch (...) {
154                         i->second->OnError(i->first);
155                 }
156         }
157 }
158
159 void Pool::Clean() {
160         for (auto i = connections.begin(); i != connections.end();) {
161                 if (i->second->Closed()) {
162                         int num = i->first.RemoveFrom(set);
163                         if (num < 0) {
164                                 throw NetError("failed to remove socket from set");
165                         }
166                         use_conn = num;
167                         i->second->OnRemove(i->first);
168                         i = connections.erase(i);
169                 } else {
170                         ++i;
171                 }
172         }
173 }
174
175
176 void Pool::Resize(int new_max) {
177         if (new_max < max_conn) {
178                 return;
179         }
180
181         int new_size = max(new_max, max_conn * 2);
182         SDLNet_SocketSet new_set(SDLNet_AllocSocketSet(new_size));
183         if (!new_set) {
184                 throw NetError("failed to allocate socket set");
185         }
186
187         for (auto &conn : connections) {
188                 if (conn.first.AddTo(new_set) == -1) {
189                         NetError error("failed to migrate socket to new set");
190                         SDLNet_FreeSocketSet(new_set);
191                         throw error;
192                 }
193         }
194
195         SDLNet_FreeSocketSet(set);
196         set = new_set;
197         max_conn = new_size;
198 }
199
200 }
201 }