Boost.Asioで簡単なチャット
簡単なチャットプログラムを書いてみました。Boost.AsioとWinsock2の2通り書いてみました。とにかく簡単に書こうとしたので同期通信しています。なので一方的にメッセージを送ることはできず、必ずサーバ側とクライアント側が交互にメッセージを送信しなければなりません。
どちらかが"end"というメッセージを送ったら接続を切り、終了します。
WinSockで書いたチャットプログラム
#include <winsock2.h> #include <iostream> #include <string> using namespace std; #pragma comment(lib, "WSock32.lib") int client(); int server(); int main() { char c; cout << "クライアント?サーバー?c/s\n>"; cin >> c; if (c == 'c') { client(); } else if (c == 's') { server(); } } int client() { WSADATA wsadata;//ソケット情報を格納する構造体 //WinSockの初期化 if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0) { cout << "winsock startup failed.\n"; return 1; } //指定したサービスプロバイダへのTCPソケットを作成する。 const auto sock = socket(PF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { cout << "couldn't open a socket.\n"; WSACleanup(); return 1; } //ホスト情報を取得 cout << "サーバー名\n>"; string server_name; cin >> server_name; auto host = gethostbyname(server_name.c_str()); if (host == nullptr) { auto addr = inet_addr(server_name.c_str()); host = gethostbyaddr(reinterpret_cast<char*>(&addr), 4, AF_INET); if (host == nullptr) { cout << server_name << " is not found.\n"; WSACleanup(); return 1; } } //IPv4のソケットアドレス情報を設定する sockaddr_in sockadd; memset(&sockadd, 0, sizeof(sockadd)); sockadd.sin_family = AF_INET; cout << "ポート番号\n>"; u_short port = 0; cin >> port; sockadd.sin_port = htons(port); sockadd.sin_addr = *(reinterpret_cast<LPIN_ADDR>(*(host->h_addr_list))); //指定したソケットへ接続する if (connect(sock, reinterpret_cast<PSOCKADDR>(&sockadd), sizeof(sockadd)) != 0) { cout << "connect failure.\n"; closesocket(sock); WSACleanup(); return 1; } //メッセージを送受信 string buffer; while (true) { cout << "送信\n>"; cin >> buffer; send(sock, buffer.c_str(), buffer.size(), 0); if (buffer == "end") { break; } cout << "サーバーから返事を待っています\n"; char buf[1024]; memset(buf, '\0', sizeof(buf)); const auto rec = recv(sock, buf, sizeof(buf)-1, 0); if (rec == SOCKET_ERROR) { cout << "エラーです\n"; break; } buf[rec] = '\0'; if (strcmp(buf, "end") == 0) { cout << "サーバーが接続を切りました\n"; break; } cout << "受信:" << buf << endl; } //ソケットの送受信を切断する shutdown(sock, SD_BOTH); //ソケットを破棄する closesocket(sock); //WinSockの解放 WSACleanup(); cout << "終了します\n"; return 0; } int server() { WSADATA wsadata;//ソケット情報を格納する構造体 //WinSockの初期化 if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0) { cout << "winsock startup failed.\n"; return 1; } //指定したサービスプロバイダへのTCPソケットを作成する。 const auto listen_socket = socket(PF_INET, SOCK_STREAM, 0); if (listen_socket == INVALID_SOCKET) { cout << "couldn't open a socket.\n"; WSACleanup(); return 1; } //IPv4のソケットアドレス情報を設定する sockaddr_in sockadd; memset(&sockadd, 0, sizeof(sockadd)); sockadd.sin_family = AF_INET; cout << "ポート番号\n>"; u_short port = 0; cin >> port; sockadd.sin_port = htons(port); sockadd.sin_addr.s_addr = INADDR_ANY; //ソケットに名前をつける if (bind(listen_socket, reinterpret_cast<sockaddr*>(&sockadd), sizeof(sockadd)) == SOCKET_ERROR) { cout << "bind failure.\n"; closesocket(listen_socket); WSACleanup(); return 1; } //接続待ち状態にする if (listen(listen_socket, 0) == SOCKET_ERROR) { cout << "listen failure.\n"; closesocket(listen_socket); WSACleanup(); return 1; } //クライアントからの接続を受け入れる decltype(sockadd) from; int from_length = sizeof(from); SOCKET sock; sock = accept(listen_socket, reinterpret_cast<sockaddr*>(&from), &from_length); if (sock == INVALID_SOCKET) { cout << "accept failure.\n"; closesocket(listen_socket); WSACleanup(); return 1; } cout << inet_ntoa(from.sin_addr) << "が接続してきました\n"; closesocket(listen_socket); //メッセージを送受信 string buffer; while (true) { cout << "クライアントからの受信を待っています\n>"; char buf[1024]; memset(buf, '\0', sizeof(buf)); const auto rec = recv(sock, buf, sizeof(buf)-1, 0); if (rec == SOCKET_ERROR) { cout << "エラーです\n"; break; } buf[rec] = '\0'; if (strcmp(buf, "end") == 0) { cout << "クライアントが接続を切りました\n"; break; } cout << "受信:" << buf << endl; cout << "送信\n>"; cin >> buffer; send(sock, buffer.c_str(), buffer.size(), 0); if (buffer == "end") { break; } } //ソケットの送受信を切断する shutdown(sock, SD_BOTH); //ソケットを破棄する closesocket(sock); //WinSockの解放 WSACleanup(); cout << "終了します\n"; return 0; }
Boost.Asioで書いたチャットプログラム
#include <boost/asio.hpp> #include <iostream> #include <string> using namespace std; namespace asio = boost::asio; namespace ip = asio::ip; int client(); int server(); int main() { char c; cout << "クライアント?サーバー?c/s\n>"; cin >> c; if (c == 'c') { client(); } else if (c == 's') { server(); } } int client() try { asio::io_service io_service; //TCPソケットを作成する ip::tcp::socket sock(io_service); //ホスト情報を取得 cout << "サーバー名\n>"; string server_name; cin >> server_name; cout << "ポート番号\n>"; u_short port = 0; cin >> port; sock.connect(ip::tcp::endpoint(ip::address::from_string(server_name), port)); //メッセージを送受信 string buffer; while (true) { cout << "送信\n>"; cin >> buffer; asio::write(sock, asio::buffer(buffer)); if (buffer == "end") { break; } cout << "サーバーから返事を待っています\n"; asio::streambuf receive_buffer; boost::system::error_code error; asio::read(sock, receive_buffer, asio::transfer_at_least(1), error); if (error && error != asio::error::eof) { std::cout << "receive failed: " << error.message() << std::endl; } else if (asio::buffer_cast<const char*>(receive_buffer.data()) == string("end")) { cout << "サーバーが接続を切りました\n"; break; } cout << "受信:" << &receive_buffer << endl; } return 0; } catch (exception& e) { cout << e.what(); return 1; } int server() try { asio::io_service io_service; //TCPソケットを作成する ip::tcp::socket sock(io_service); //IPv4のソケットアドレス情報を設定する cout << "ポート番号\n>"; u_short port = 0; cin >> port; ip::tcp::acceptor acceptor(io_service, ip::tcp::endpoint(ip::tcp::v4(), port)); //クライアントからの接続を受け入れる acceptor.accept(sock); //メッセージを送受信 string buffer; while (true) { cout << "クライアントからの受信を待っています\n>"; asio::streambuf receive_buffer; boost::system::error_code error; asio::read(sock, receive_buffer, asio::transfer_at_least(1), error); if (error && error != asio::error::eof) { std::cout << "receive failed: " << error.message() << std::endl; } else if (asio::buffer_cast<const char*>(receive_buffer.data()) == string("end")) { cout << "クライアントが接続を切りました\n"; break; } cout << "受信:" << &receive_buffer << endl; cout << "送信\n>"; cin >> buffer; asio::write(sock, asio::buffer(buffer)); if (buffer == "end") { break; } } return 0; } catch (exception& e) { cout << e.what(); return 1; }
次ネットワークプログラムを書く時は非同期通信に挑戦してみようともいます。