PageRenderTime 27ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/tags/0.2-release/src/conn.cc

https://gitlab.com/netsphere/daemonshogi
C++ | 555 lines | 386 code | 102 blank | 67 comment | 72 complexity | 80915c0aab3995164c3b048c1b0015f5 MD5 | raw file
  1. // daemonshogi - a shogi program.
  2. // TCP/IP library.
  3. // Copyright (c) 2008 HORIKAWA Hisashi.
  4. // リファレンス
  5. // http://msdn.microsoft.com/en-us/library/ms740673(VS.85).aspx
  6. #define GTK_DISABLE_DEPRECATED 1
  7. #include <config.h>
  8. #ifdef _WINDOWS
  9. #define WIN32_LEAN_AND_MEAN
  10. #define _WIN32_WINNT 0x0501
  11. #include <winsock2.h>
  12. #include <ws2tcpip.h>
  13. #else // UNIX
  14. #include <sys/socket.h>
  15. #include <netdb.h>
  16. #include <unistd.h>
  17. #include <errno.h>
  18. #define closesocket close
  19. #define WSAGetLastError() errno
  20. #define WSACleanup()
  21. #endif
  22. #include <gtk/gtk.h>
  23. #include <stdlib.h>
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include <assert.h>
  27. #include <string>
  28. #include <list>
  29. using namespace std;
  30. G_BEGIN_DECLS
  31. #include "support.h"
  32. G_END_DECLS
  33. #include "conn.h"
  34. #include "canvas.h"
  35. #include "si/ui.h"
  36. /** ログウィンドウに表示 */
  37. void network_log_append_message(const char* format, ...)
  38. {
  39. char buf[1000];
  40. va_list ap;
  41. GtkTextBuffer* textbuf;
  42. GtkWidget* network_textview;
  43. va_start(ap, format);
  44. vsprintf(buf, format, ap);
  45. va_end(ap);
  46. network_textview = lookup_widget(g_canvas->network_log_window,
  47. "network_textview");
  48. assert(network_textview);
  49. // TODO: きちんと末尾に追加する。
  50. textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(network_textview));
  51. gtk_text_buffer_insert_at_cursor(textbuf, buf, -1);
  52. }
  53. /** 受信データを行に分ける */
  54. typedef list<string> RecvList;
  55. /** 行に分けた受信データ */
  56. static RecvList recv_list;
  57. static int wait_state = 0;
  58. static gint io_watch = 0;
  59. static int server_fd = -1;
  60. static guint idle_id = 0;
  61. /** 受信した指し手 */
  62. struct NetTe {
  63. int special; // 投了、時間切れなど
  64. TE te;
  65. int sec; // 秒数
  66. NetTe(): special(0), sec(0) { memset(&te, 0, sizeof(TE)); }
  67. };
  68. typedef list<NetTe> NetTeList;
  69. static NetTeList net_te_list;
  70. /** サーバとの接続を切断。 */
  71. void disconnect_server()
  72. {
  73. if (server_fd < 0)
  74. return;
  75. recv_list.clear();
  76. wait_state = 0;
  77. net_te_list.clear();
  78. g_source_remove(idle_id);
  79. idle_id = 0;
  80. gdk_input_remove(io_watch);
  81. io_watch = 0;
  82. closesocket(server_fd);
  83. server_fd = -1;
  84. GtkWidget* item = lookup_widget(g_canvas->window, "connect_server");
  85. assert(item);
  86. gtk_widget_set_sensitive(item, TRUE);
  87. daemon_canvas_change_mode(g_canvas, D_CANVAS_MODE_BOOK);
  88. }
  89. /** 0終端の文字列を送信 */
  90. static void csa_send(const char* line)
  91. {
  92. printf("send: %s", line); // DEBUG
  93. int ret = ::send(server_fd, line, strlen(line), 0);
  94. if (ret < 0) {
  95. printf("%s: socket write error.\n", __func__);
  96. exit(1);
  97. }
  98. }
  99. static string username, password;
  100. extern int network_game_start;
  101. /** ログイン待ち */
  102. static void state_1_read()
  103. {
  104. assert(recv_list.size() > 0);
  105. printf("%s: first line = %s\n", __func__, recv_list.front().c_str());
  106. string s = "LOGIN:" + username + " OK";
  107. if (recv_list.front() == s) {
  108. network_log_append_message("%s login ok.\n", username.c_str());
  109. recv_list.pop_front();
  110. wait_state = 2;
  111. // 引き続き待つ
  112. }
  113. else {
  114. network_log_append_message("'%s' fail login.\n", username.c_str());
  115. network_game_start = -1;
  116. }
  117. }
  118. extern char net_playername[2][100];
  119. extern char net_my_turn;
  120. static int game_total_time;
  121. /** ゲーム開始 */
  122. static void state_2_read()
  123. {
  124. assert(g_canvas);
  125. printf("%s\n", __func__); // DEBUG
  126. bool finished = false;
  127. RecvList::iterator it;
  128. for (it = recv_list.begin(); it != recv_list.end(); it++) {
  129. if (!it->find("Name+:", 0)) {
  130. strcpy(net_playername[0], string(*it, 6).c_str());
  131. }
  132. else if (!it->find("Name-:", 0)) {
  133. strcpy(net_playername[1], string(*it, 6).c_str());
  134. }
  135. else if (!it->find("Your_Turn:", 0)) {
  136. net_my_turn = string(*it, 10)[0];
  137. assert(net_my_turn == '+' || net_my_turn == '-');
  138. }
  139. else if (!it->find("Total_Time:", 0)) {
  140. game_total_time = 0;
  141. // TODO:
  142. }
  143. else if (*it == "END Game_Summary") {
  144. finished = true;
  145. break;
  146. }
  147. }
  148. if (!finished) {
  149. printf("data partial.\n"); // DEBUG
  150. return; // まだ全部届いていない
  151. }
  152. printf("receive summary.\n"); // DEBUG
  153. recv_list.erase(recv_list.begin(), ++it);
  154. csa_send("AGREE\n");
  155. wait_state = 3;
  156. }
  157. /** ゲーム開始 */
  158. static void state_3_read()
  159. {
  160. printf("%s: line = %s\n", __func__, recv_list.front().c_str()); // DEBUG
  161. string line = recv_list.front();
  162. recv_list.pop_front();
  163. if (line.find("START:", 0) == string::npos) {
  164. // reject ?
  165. network_log_append_message("reject game.\n");
  166. network_game_start = -1;
  167. return;
  168. }
  169. network_game_start = 1;
  170. wait_state = 4;
  171. }
  172. const char csa_koma_str[][3] = {
  173. "", "FU", "KY", "KE", "GI", "KI", "KA", "HI",
  174. "OU", "TO", "NY", "NK", "NG", "", "UM", "RY",
  175. };
  176. /** 手を作る. daemon_record_load_csa_te() を参考に。 */
  177. static void parse_te(const char* line, TE* te, int* sec)
  178. {
  179. char c;
  180. char koma_str[3];
  181. int p;
  182. assert(te);
  183. assert(sec);
  184. // xyが逆
  185. c = line[0];
  186. te->fm = ((line[2] - 0x30) << 4) + line[1] - 0x30;
  187. te->to = ((line[4] - 0x30) << 4) + line[3] - 0x30;
  188. sscanf(line + 5, "%2s,T%d", koma_str, sec);
  189. for (p = 1; p < 0xf; p++) {
  190. if (strcmp(koma_str, csa_koma_str[p]) == 0)
  191. break;
  192. }
  193. if (te->fm == 0) {
  194. // 駒打ち
  195. te->nari = 0;
  196. te->uti = p;
  197. }
  198. else {
  199. // 移動
  200. te->uti = 0;
  201. int pf = daemon_dboard_get_board(&g_canvas->board,
  202. te->fm & 0xf,
  203. te->fm >> 4) & 0xf;
  204. te->nari = pf <= 8 && p >= 9 ? 1 : 0;
  205. }
  206. printf("%s: from = %x, to = %x\n", __func__, te->fm, te->to);
  207. }
  208. extern GAME g_game;
  209. /** 相手の手を受信 */
  210. static void state_4_read()
  211. {
  212. printf("%s: front = '%s'\n", __func__, recv_list.front().c_str()); // DEBUG
  213. string line = recv_list.front();
  214. recv_list.pop_front();
  215. // TODO: 時間切れなどの検査
  216. // 勝ったとき
  217. // %TORYO
  218. // #RESIGN
  219. // #WIN
  220. NetTe net_te;
  221. if (!line.find("%TORYO", 0))
  222. net_te.special = SI_TORYO;
  223. else if (line == "#RESIGN")
  224. net_te.special = -1;
  225. else if (line == "#WIN")
  226. net_te.special = -2;
  227. else if (line == "#LOSE")
  228. net_te.special = -3;
  229. else {
  230. net_te.special = 0;
  231. parse_te(line.c_str(), &net_te.te, &net_te.sec);
  232. }
  233. net_te_list.push_back(net_te);
  234. }
  235. /** アイドル状態のコールバック */
  236. static gboolean on_idle(gpointer data)
  237. {
  238. if (recv_list.size() == 0) {
  239. printf("%s: remove on_idle.\n", __func__); // DEBUG
  240. idle_id = 0;
  241. return FALSE; // 受信するまで休む
  242. }
  243. switch (wait_state) {
  244. case 1:
  245. // ログイン待ち
  246. state_1_read();
  247. break;
  248. case 2:
  249. // ゲーム情報待ち
  250. state_2_read();
  251. break;
  252. case 3:
  253. // ゲーム開始待ち
  254. state_3_read();
  255. break;
  256. case 4:
  257. // 相手の手を待つ
  258. state_4_read();
  259. break;
  260. default:
  261. printf("wait_state = %d\n", wait_state); // DEBUG
  262. // assert(0);
  263. break;
  264. }
  265. return TRUE; // 引き続き呼び出してもらう
  266. }
  267. extern GAME g_game;
  268. /** ネットワーク経由の手を格納.
  269. ブロックする */
  270. INPUTSTATUS daemon_input_next_network_impl(BOARD* bo, TE* te)
  271. {
  272. // 手が設定されるまで待つ
  273. while (net_te_list.size() == 0) {
  274. pending_loop();
  275. usleep(50);
  276. }
  277. NetTe net_te = net_te_list.front();
  278. net_te_list.pop_front();
  279. if (net_te.special != 0) {
  280. // 中断または投了
  281. disconnect_server(); // TODO: もう少し丁寧に
  282. return (INPUTSTATUS) net_te.special;
  283. }
  284. else {
  285. *te = net_te.te;
  286. return SI_NORMAL;
  287. }
  288. }
  289. static string sock_buffer;
  290. /** ソケットのコールバック関数.
  291. 行を切り出して、recv_list に追加する. */
  292. static void on_socket_read(gpointer data, gint source,
  293. GdkInputCondition condition)
  294. {
  295. char buf[1000];
  296. int r = ::recv(server_fd, buf, sizeof(buf) - 1, 0);
  297. if (r < 0) {
  298. // エラー発生
  299. printf("%s: socket recv error.\n", __func__); // DEBUG
  300. exit(1);
  301. }
  302. else if (r == 0) {
  303. // 接続先が閉じた
  304. network_log_append_message("server shutdown.\n");
  305. disconnect_server();
  306. return;
  307. }
  308. buf[r] = '\0';
  309. sock_buffer += buf;
  310. // 行を切り出す. 末尾の改行は削除する
  311. int idx;
  312. while ((idx = sock_buffer.find("\n", 0)) != string::npos) {
  313. string l = string(sock_buffer, 0, idx);
  314. printf("recv: %s\n", l.c_str()); // DEBUG
  315. recv_list.push_back(l);
  316. sock_buffer.erase(0, idx + 1);
  317. }
  318. if (!idle_id && recv_list.size() > 0) {
  319. printf("%s: add on_idle.\n", __func__); // DEBUG
  320. idle_id = g_idle_add(on_idle, NULL);
  321. }
  322. }
  323. /** WinSockの初期化
  324. @return 成功したとき1
  325. */
  326. static int init_socket()
  327. {
  328. #ifdef _WINDOWS
  329. WSADATA wsaData;
  330. int r = ::WSAStartup(MAKEWORD(2, 2), &wsaData);
  331. if (r != 0) {
  332. printf("WSAStartup() failed: %d\n", r);
  333. exit(1);
  334. }
  335. #endif
  336. return 1;
  337. }
  338. static int socket_inited = 0;
  339. /** サーバに接続する。
  340. @param hostname IPv4 or IPv6 ホスト名
  341. @param service ポート番号の文字列
  342. @return 成功したら1
  343. */
  344. int connect_to_server(const char* hostname, const char* service)
  345. {
  346. struct addrinfo hints;
  347. struct addrinfo* res = NULL;
  348. struct addrinfo* ai;
  349. if (!socket_inited) {
  350. socket_inited = init_socket();
  351. if (!socket_inited)
  352. return 0;
  353. }
  354. // ホスト情報を得る
  355. memset(&hints, 0, sizeof(hints));
  356. hints.ai_family = AF_UNSPEC; // IPv4/IPv6両対応
  357. hints.ai_socktype = SOCK_STREAM;
  358. hints.ai_flags = NI_NUMERICSERV; // AI_NUMERICSERV // serviceはポート番号に限る
  359. int r = getaddrinfo(hostname, service, &hints, &res);
  360. if (r != 0) {
  361. printf("getaddrinfo() failed: %s\n", gai_strerror(r));
  362. return 0;
  363. }
  364. // 接続する
  365. int sockfd = -1;
  366. for (ai = res; ai; ai = ai->ai_next) {
  367. sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
  368. if (sockfd < 0) {
  369. printf("Error at socket(): %d\n", WSAGetLastError());
  370. break; // 致命的エラー
  371. }
  372. if (::connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
  373. closesocket(sockfd);
  374. sockfd = -1;
  375. continue; // 別のアドレスを試す
  376. }
  377. // ok
  378. break;
  379. }
  380. freeaddrinfo(res);
  381. if (sockfd > 0) {
  382. server_fd = sockfd;
  383. // コールバック関数を登録する
  384. io_watch = gdk_input_add(sockfd,
  385. GDK_INPUT_READ,
  386. on_socket_read,
  387. NULL);
  388. printf("io_watch = %d\n", io_watch); // DEBUG
  389. return 1;
  390. }
  391. else
  392. return 0;
  393. }
  394. ///////////////////////////////////////////////////////////////////
  395. // CSAプロトコル
  396. /** ログイン要求を送信する.
  397. @return 1 成功
  398. */
  399. int csa_send_login(const char* username_, const char* password_)
  400. {
  401. username = username_; password = password_;
  402. char buf[1000];
  403. sprintf(buf, "LOGIN %s %s\n", username_, password_);
  404. wait_state = 1;
  405. csa_send(buf);
  406. return 1;
  407. }
  408. /** 指し手を送信.
  409. daemon_record_output_csa_te() を参考に。 */
  410. int csa_send_move(const DBoard* board, int is_sente, const TE* te)
  411. {
  412. char buf[100];
  413. if (te->uti) {
  414. // 打ち
  415. sprintf(buf, "%c%d%d%d%d%s\n",
  416. is_sente ? '+' : '-',
  417. 0, 0,
  418. te->to & 0xf, te->to >> 4,
  419. csa_koma_str[te->uti]);
  420. }
  421. else {
  422. // 移動
  423. int p = daemon_dboard_get_board(board, te->fm & 0xf, te->fm >> 4);
  424. if (te->nari) p += 8;
  425. sprintf(buf, "%c%d%d%d%d%s\n",
  426. is_sente ? '+' : '-',
  427. te->fm & 0xf, te->fm >> 4,
  428. te->to & 0xf, te->to >> 4,
  429. csa_koma_str[p & 0xf]);
  430. }
  431. csa_send(buf);
  432. return 1;
  433. }
  434. /** 投了メッセージを送信 */
  435. int csa_send_toryo()
  436. {
  437. csa_send("%TORYO\n");
  438. return 1;
  439. }
  440. /** 手を送信した結果(経過秒数など)を得る.
  441. ブロックする。 */
  442. int csa_wait_for_result(int* elapsed_time, int* minmax_return)
  443. {
  444. printf("%s\n", __func__); // DEBUG
  445. while (net_te_list.size() == 0)
  446. pending_loop();
  447. NetTe net_te = net_te_list.front();
  448. net_te_list.pop_front();
  449. *minmax_return = net_te.special ? net_te.special : SI_NORMAL;
  450. *elapsed_time = net_te.sec;
  451. printf("my time = %d\n", *elapsed_time); // DEBUG
  452. return 1;
  453. }