試験ノート - 実装ドリル 1.0
読み取り中…
検索中…
一致する文字列を見つけられません
mini_db.cpp
1#include "mini_db.hpp"
2
3#include <arpa/inet.h>
4#include <netinet/in.h>
5#include <sys/socket.h>
6#include <unistd.h>
7
8#include <cerrno>
9#include <csignal>
10#include <cstdlib>
11#include <cstring>
12#include <fstream>
13#include <iostream>
14#include <sstream>
15#include <vector>
16
17namespace {
25volatile sig_atomic_t g_running = 1;
26
27void on_sigint(int);
28} // namespace
29
49int main(int argc, char **argv) {
50 if (argc != 3) {
51 std::cerr << "Wrong number of arguments" << std::endl;
52 return 1;
53 }
54 int port = std::atoi(argv[1]);
55 if (port <= 0 || port > 65535) {
56 std::cerr << "Wrong number of arguments" << std::endl;
57 return 1;
58 }
59 std::signal(SIGINT, on_sigint);
60 std::signal(SIGPIPE, SIG_IGN);
61
62 MiniDb server(port, argv[2]);
63 server.setup();
64 server.run();
65 return 0;
66}
67
68namespace {
79void on_sigint(int) {
80 g_running = 0;
81}
82} // namespace
83
95void fatal() {
96 std::cerr << "Fatal error" << std::endl;
97 std::exit(1);
98}
99
114MiniDb::MiniDb(int port, const std::string &path)
115 : port_(port), path_(path), listen_fd_(-1), max_fd_(-1) {
116 FD_ZERO(&active_fds_);
117}
118
130 for (std::map<int, std::string>::iterator it = buffers_.begin();
131 it != buffers_.end(); ++it) {
132 close(it->first);
133 }
134 if (listen_fd_ >= 0)
135 close(listen_fd_);
136}
137
154 load();
155
156 listen_fd_ = socket(AF_INET, SOCK_STREAM, 0);
157 if (listen_fd_ < 0)
158 fatal();
159
160 int opt = 1;
161 if (setsockopt(listen_fd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
162 fatal();
163
164 sockaddr_in addr;
165 std::memset(&addr, 0, sizeof(addr));
166 addr.sin_family = AF_INET;
167 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
168 addr.sin_port = htons(static_cast<uint16_t>(port_));
169
170 if (bind(listen_fd_, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)) < 0)
171 fatal();
172 if (listen(listen_fd_, 128) < 0)
173 fatal();
174
175 FD_SET(listen_fd_, &active_fds_);
177
178 std::cout << "ready" << std::endl;
179}
180
193 std::ifstream ifs(path_.c_str());
194 if (!ifs.is_open())
195 return;
196 std::string key, value;
197 while (ifs >> key >> value)
198 db_[key] = value;
199}
200
221 fd_set rfds;
222 while (g_running) {
223 rfds = active_fds_;
224 int rv = select(max_fd_ + 1, &rfds, NULL, NULL, NULL);
225 if (rv < 0) {
226 if (errno == EINTR)
227 continue;
228 fatal();
229 }
230 for (int fd = 0; fd <= max_fd_ && rv > 0; ++fd) {
231 if (!FD_ISSET(fd, &rfds))
232 continue;
233 --rv;
234 if (fd == listen_fd_)
236 else
237 recv_data(fd);
238 }
239 }
240 save();
241}
242
256 int fd = accept(listen_fd_, NULL, NULL);
257 if (fd < 0)
258 return;
259 FD_SET(fd, &active_fds_);
260 if (fd > max_fd_)
261 max_fd_ = fd;
262 buffers_[fd] = "";
263}
264
288void MiniDb::recv_data(int fd) {
289 char buf[1024];
290 ssize_t n = recv(fd, buf, sizeof(buf), 0);
291 if (n <= 0) {
292 if (n < 0 && errno == EINTR)
293 return;
294 disconnect(fd);
295 return;
296 }
297 std::string &acc = buffers_[fd];
298 acc.append(buf, static_cast<size_t>(n));
299 std::string::size_type pos;
300 while ((pos = acc.find('\n')) != std::string::npos) {
301 std::string line = acc.substr(0, pos);
302 acc.erase(0, pos + 1);
303 std::string resp = handle_command(line);
304 send(fd, resp.c_str(), resp.size(), 0);
305 }
306}
307
323void MiniDb::disconnect(int fd) {
324 close(fd);
325 FD_CLR(fd, &active_fds_);
326 buffers_.erase(fd);
327 while (max_fd_ > listen_fd_ && !FD_ISSET(max_fd_, &active_fds_))
328 --max_fd_;
329}
330
357std::string MiniDb::handle_command(const std::string &line) {
358 std::istringstream iss(line);
359 std::vector<std::string> tok;
360 std::string t;
361 while (iss >> t)
362 tok.push_back(t);
363
364 if (tok.empty())
365 return "2\n";
366 if (tok[0] == "POST" && tok.size() == 3) {
367 db_[tok[1]] = tok[2];
368 return "0\n";
369 }
370 if (tok[0] == "GET" && tok.size() == 2) {
371 std::map<std::string, std::string>::iterator it = db_.find(tok[1]);
372 if (it == db_.end())
373 return "1\n";
374 return "0 " + it->second + "\n";
375 }
376 if (tok[0] == "DELETE" && tok.size() == 2) {
377 if (db_.erase(tok[1]) == 0)
378 return "1\n";
379 return "0\n";
380 }
381 return "2\n";
382}
383
402void MiniDb::save() const {
403 std::ofstream ofs(path_.c_str());
404 if (!ofs.is_open())
405 return;
406 for (std::map<std::string, std::string>::const_iterator it = db_.begin();
407 it != db_.end(); ++it) {
408 ofs << it->first << " " << it->second << "\n";
409 }
410}
int max_fd_
select に渡す最大 fd
Definition mini_db.hpp:188
fd_set active_fds_
現在監視中の fd 集合
Definition mini_db.hpp:189
int listen_fd_
リスニングソケット fd
Definition mini_db.hpp:187
std::string path_
永続化先ファイル (引数)
Definition mini_db.hpp:186
int port_
待ち受けポート (引数)
Definition mini_db.hpp:185
std::map< std::string, std::string > db_
Key-Value 本体
Definition mini_db.hpp:191
std::map< int, std::string > buffers_
fd → 未完成行バッファ
Definition mini_db.hpp:190
void accept_client()
accept(2) し、active_fds_ と buffers_ を更新する。
Definition mini_db.cpp:255
volatile sig_atomic_t g_running
サーバ実行継続フラグ (SIGINT で 0 になる)
Definition mini_db.cpp:25
int main(int argc, char **argv)
プログラムのエントリポイント
Definition mini_db.cpp:49
void load()
起動時に path_ を読み、空白区切り 1 行 1 ペアで db_ を初期化。
Definition mini_db.cpp:192
void fatal()
Fatal error\n を stderr に書いて exit(1) する
Definition mini_db.cpp:95
void recv_data(int fd)
recv(2) でデータを取り込み、行ごとに handle_command → send する。
Definition mini_db.cpp:288
void save() const
現在の DB をテキスト形式で永続化する
Definition mini_db.cpp:402
void run()
サーバのメインループ。SIGINT 受信で抜け、save() してから戻る。
Definition mini_db.cpp:220
void setup()
サーバ起動準備 (DB ロード → ソケット作成 → bind → listen → "ready")
Definition mini_db.cpp:153
MiniDb(int port, const std::string &path)
ポートと永続化先パスを束縛する
Definition mini_db.cpp:114
~MiniDb()
デストラクタ (全 fd をクローズ)
Definition mini_db.cpp:129
void disconnect(int fd)
fd を close し、active_fds_ / buffers_ から外し、max_fd_ を巻き戻す。
Definition mini_db.cpp:323
std::string handle_command(const std::string &line)
1行の入力に対して、応答文字列 (0\n / 0 <v>\n / 1\n / 2\n) を返す。
Definition mini_db.cpp:357
void on_sigint(int)
SIGINT ハンドラ。g_running を 0 にするだけ。
Definition mini_db.cpp:79