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