2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Boost Asioで空きポートを開いてみた

Posted at

今日yumetodoちゃんとSkypeしてたらおもむろにこんなこと言われました

boost asioで指定したポートが使えない時に自動的に別のポートを開く方法考えてくれ。ただし常に排他モードとする

いやいや、急に言われてもって思いましたね
まあ…私も今でこそhttplibでしか通信書いてないですけど、昔はboost asio使ってたので、手法自体が思い浮かばなかったわけではないんですね

boost::asio::ip::tcp::endpoint ep(boost::asio::ip::tcp::v4(), 0)

はい、endpointクラスのコンストラクターの第2引数を0にして、あとはacceptorに渡してやればいいだけなんです
とりあえず排他の話は置いといて、指定ポートが使えない時にっていうのがちょっとどうすっかなってなりましたね
だって引数でポート0渡した後にどのポートが空いてるのか知る術を私は知らなかったわけですから
そんなこんなで始まったググりTime
目的の情報はくっそあっさり見つかりました

まあ絶対誰かしらやろうと思うよね
StackOverflowで私がやりたいことドンピシャで質問されてましたwwwww
How do I correctly randomly assign a port to a test HTTP server using boost asio?

ほうほう…。鍵はここか…

// (前略)
acceptor.bind(endPoint);

m_acceptor.listen();        

boost::asio::ip::tcp::endpoint le = acceptor.local_endpoint(); //THIS LINE SOLVES IT
port = le.port();

試しにやってみた結果、どのポートが使われてるのかコンソールでちゃんと確認することができました
さて、今回の課題は「指定したポートが使えない時に自動的に別のポートを開く」
つまり、一度は指定ポートでトライしないといけないんです。
じゃあどうやってトライするのって話ですね。
答えはシンプル

boost::asio::ip::tcp::acceptor acceptor(ios, tcp::endpoint(tcp::v4(), default_port));

もし空いてなければこいつが例外投げるので、そいつをcatchしてやるだけです。
つまりこういうことですね

inline boost::asio::ip::tcp::acceptor create_acceptor(boost::asio::io_service& ios, const unsigned short& default_port) {
	try {
		return boost::asio::ip::tcp::acceptor(ios, tcp::endpoint(tcp::v4(), default_port), reuse_address);
	}
	catch (std::exception) {
		return boost::asio::ip::tcp::acceptor(ios, tcp::endpoint(tcp::v4(), 0), reuse_address);
	}
}

try-catch書かずに行けたらもっと良かったけど、まあしゃあないな
そして、排他モードという課題も込みで書き上げたコードがこれです

# include <iostream>
# include <boost/asio.hpp>
# include <boost/bind.hpp>
# include <stdexcept>
namespace asio = boost::asio;
using asio::ip::tcp;

void on_accept(const boost::system::error_code& error) {
	if (error) {
		std::cout << "accept failed: " << error.message() << std::endl;
	}
	else {
		std::cout << "accept correct!" << std::endl;
	}
}

inline tcp::acceptor create_acceptor(asio::io_service& ios, const unsigned short& default_port, const bool reuse_address = true) {
	try {
		return tcp::acceptor(ios, tcp::endpoint(tcp::v4(), default_port), reuse_address);
	}
	catch (std::exception) {
		return tcp::acceptor(ios, tcp::endpoint(tcp::v4(), 0), reuse_address);
	}
}

int main() {
	try {
		asio::io_service ios{};
		tcp::acceptor acceptor = create_acceptor(ios, 3306, false);
		acceptor.listen();
		tcp::endpoint e = acceptor.local_endpoint();
		std::cout << e.port() << std::endl; // ここでポートを表示する
		tcp::socket socket(ios);
		acceptor.async_accept(socket, boost::bind(&on_accept, asio::placeholders::error));
		ios.run();
	}
	catch (const std::exception& er) {
		std::cout << er.what() << std::endl;
	}
	return 0;
}

以上!
スネークケースで書いたのもう6年ぶりじゃないかな…
Java始めて以来、全部キャメルケースで書いてるからなぁ…
え?lambda使え?文句はyumetodoちゃんへどうぞ。言ってない話がもう1個隠れてるから

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?