|
Atrinik Client 2.5
|
00001 /************************************************************************ 00002 * Atrinik, a Multiplayer Online Role Playing Game * 00003 * * 00004 * Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team * 00005 * * 00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game) * 00007 * and Crossfire (Multiplayer game for X-windows). * 00008 * * 00009 * This program is free software; you can redistribute it and/or modify * 00010 * it under the terms of the GNU General Public License as published by * 00011 * the Free Software Foundation; either version 2 of the License, or * 00012 * (at your option) any later version. * 00013 * * 00014 * This program is distributed in the hope that it will be useful, * 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00017 * GNU General Public License for more details. * 00018 * * 00019 * You should have received a copy of the GNU General Public License * 00020 * along with this program; if not, write to the Free Software * 00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * 00022 * * 00023 * The author can be reached at admin@atrinik.org * 00024 ************************************************************************/ 00025 00030 #include <global.h> 00031 00032 static SDL_Thread *input_thread; 00033 static SDL_mutex *input_buffer_mutex; 00034 static SDL_cond *input_buffer_cond; 00035 00036 static SDL_Thread *output_thread; 00037 static SDL_mutex *output_buffer_mutex; 00038 static SDL_cond *output_buffer_cond; 00039 00042 static SDL_mutex *socket_mutex; 00043 00046 static int abort_thread = 0; 00047 00048 /* start is the first waiting item in queue, end is the most recent enqueued */ 00049 static command_buffer *input_queue_start = NULL, *input_queue_end = NULL; 00050 static command_buffer *output_queue_start = NULL, *output_queue_end = NULL; 00051 00059 static command_buffer *command_buffer_new(size_t len, uint8 *data) 00060 { 00061 command_buffer *buf = (command_buffer *) malloc(sizeof(command_buffer) + len + 1); 00062 00063 buf->next = buf->prev = NULL; 00064 buf->len = len; 00065 00066 if (data) 00067 { 00068 memcpy(buf->data, data, len); 00069 } 00070 00071 buf->data[len] = '\0'; 00072 return buf; 00073 } 00074 00078 void command_buffer_free(command_buffer *buf) 00079 { 00080 free(buf); 00081 } 00082 00085 static void command_buffer_enqueue(command_buffer *buf, command_buffer **queue_start, command_buffer **queue_end) 00086 { 00087 buf->next = NULL; 00088 buf->prev = *queue_end; 00089 00090 if (*queue_start == NULL) 00091 { 00092 *queue_start = buf; 00093 } 00094 00095 if (buf->prev) 00096 { 00097 buf->prev->next = buf; 00098 } 00099 00100 *queue_end = buf; 00101 } 00102 00105 static command_buffer *command_buffer_dequeue(command_buffer **queue_start, command_buffer **queue_end) 00106 { 00107 command_buffer *buf = *queue_start; 00108 00109 if (buf) 00110 { 00111 *queue_start = buf->next; 00112 00113 if (buf->next) 00114 { 00115 buf->next->prev = NULL; 00116 } 00117 else 00118 { 00119 *queue_end = NULL; 00120 } 00121 } 00122 00123 return buf; 00124 } 00125 00130 int send_command_binary(uint8 cmd, uint8 *body, unsigned int len) 00131 { 00132 command_buffer *buf; 00133 00134 if (body) 00135 { 00136 buf = command_buffer_new(len, body); 00137 } 00138 else 00139 { 00140 uint8 tmp[3]; 00141 00142 len = 0x8001; 00143 /* Packet order is obviously big-endian for length data. */ 00144 tmp[0] = (len >> 8) & 0xFF; 00145 tmp[1] = len & 0xFF; 00146 tmp[2] = cmd; 00147 buf = command_buffer_new(3, tmp); 00148 } 00149 00150 if (!buf) 00151 { 00152 socket_close(&csocket); 00153 return -1; 00154 } 00155 00156 SDL_LockMutex(output_buffer_mutex); 00157 command_buffer_enqueue(buf, &output_queue_start, &output_queue_end); 00158 SDL_CondSignal(output_buffer_cond); 00159 SDL_UnlockMutex(output_buffer_mutex); 00160 00161 return 0; 00162 } 00163 00166 int send_socklist(SockList msg) 00167 { 00168 command_buffer *buf = command_buffer_new(msg.len + 2, NULL); 00169 00170 if (!buf) 00171 { 00172 socket_close(&csocket); 00173 return -1; 00174 } 00175 00176 memcpy(buf->data + 2, msg.buf, msg.len); 00177 00178 buf->data[0] = (uint8) ((msg.len >> 8) & 0xFF); 00179 buf->data[1] = ((uint32) (msg.len)) & 0xFF; 00180 00181 SDL_LockMutex(output_buffer_mutex); 00182 command_buffer_enqueue(buf, &output_queue_start, &output_queue_end); 00183 SDL_CondSignal(output_buffer_cond); 00184 SDL_UnlockMutex(output_buffer_mutex); 00185 00186 return 0; 00187 } 00188 00193 command_buffer *get_next_input_command() 00194 { 00195 command_buffer *buf; 00196 00197 SDL_LockMutex(input_buffer_mutex); 00198 buf = command_buffer_dequeue(&input_queue_start, &input_queue_end); 00199 SDL_UnlockMutex(input_buffer_mutex); 00200 return buf; 00201 } 00202 00203 static int reader_thread_loop(void *dummy) 00204 { 00205 static uint8 *readbuf = NULL; 00206 static int readbuf_size = 256; 00207 int readbuf_len = 0; 00208 int header_len = 0; 00209 int cmd_len = -1; 00210 00211 (void) dummy; 00212 LOG(llevInfo, "Reader thread started.\n"); 00213 00214 if (!readbuf) 00215 { 00216 readbuf = malloc(readbuf_size); 00217 } 00218 00219 while (!abort_thread) 00220 { 00221 int ret; 00222 int toread; 00223 00224 /* First, try to read a command length sequence */ 00225 if (readbuf_len < 2) 00226 { 00227 /* Three-byte length? */ 00228 if (readbuf_len > 0 && (readbuf[0] & 0x80)) 00229 { 00230 toread = 3 - readbuf_len; 00231 } 00232 else 00233 { 00234 toread = 2 - readbuf_len; 00235 } 00236 } 00237 else if (readbuf_len == 2 && (readbuf[0] & 0x80)) 00238 { 00239 toread = 1; 00240 } 00241 else 00242 { 00243 /* If we have a finished header, get the packet size from it. */ 00244 if (readbuf_len <= 3) 00245 { 00246 uint8 *p = readbuf; 00247 00248 header_len = (*p & 0x80) ? 3 : 2; 00249 cmd_len = 0; 00250 00251 if (header_len == 3) 00252 { 00253 cmd_len += ((int) (*p++) & 0x7f) << 16; 00254 } 00255 00256 cmd_len += ((int) (*p++)) << 8; 00257 cmd_len += ((int) (*p++)); 00258 } 00259 00260 toread = cmd_len + header_len - readbuf_len; 00261 00262 if (readbuf_len + toread > readbuf_size) 00263 { 00264 uint8 *tmp = readbuf; 00265 00266 readbuf_size = readbuf_len + toread; 00267 readbuf = (uint8 *) malloc(readbuf_size); 00268 memcpy(readbuf, tmp, readbuf_len); 00269 free(tmp); 00270 } 00271 } 00272 00273 ret = recv(csocket.fd, (char *) readbuf + readbuf_len, toread, 0); 00274 00275 /* End of file */ 00276 if (ret == 0) 00277 { 00278 LOG(llevInfo, "Reader thread got EOF trying to read %d bytes.\n", toread); 00279 break; 00280 } 00281 else if (ret == -1) 00282 { 00283 /* IO error */ 00284 #ifdef WIN32 00285 LOG(llevInfo, "Reader thread got error %d\n", WSAGetLastError()); 00286 #else 00287 LOG(llevInfo, "Reader thread got error %d: %s\n", errno, strerror(errno)); 00288 #endif 00289 break; 00290 } 00291 else 00292 { 00293 readbuf_len += ret; 00294 } 00295 00296 /* Finished with a command? */ 00297 if (readbuf_len == cmd_len + header_len && !abort_thread) 00298 { 00299 command_buffer *buf = command_buffer_new(readbuf_len - header_len, readbuf + header_len); 00300 00301 if (!buf) 00302 { 00303 break; 00304 } 00305 00306 SDL_LockMutex(input_buffer_mutex); 00307 command_buffer_enqueue(buf, &input_queue_start, &input_queue_end); 00308 SDL_CondSignal(input_buffer_cond); 00309 SDL_UnlockMutex(input_buffer_mutex); 00310 00311 cmd_len = -1; 00312 header_len = 0; 00313 readbuf_len = 0; 00314 } 00315 } 00316 00317 socket_close(&csocket); 00318 free(readbuf); 00319 readbuf = NULL; 00320 LOG(llevInfo, "Reader thread stopped.\n"); 00321 return -1; 00322 } 00323 00330 static int writer_thread_loop(void *dummy) 00331 { 00332 command_buffer *buf = NULL; 00333 00334 (void) dummy; 00335 LOG(llevInfo, "Writer thread started.\n"); 00336 00337 while (!abort_thread) 00338 { 00339 int written = 0; 00340 00341 SDL_LockMutex(output_buffer_mutex); 00342 00343 while (output_queue_start == NULL && !abort_thread) 00344 { 00345 SDL_CondWait(output_buffer_cond, output_buffer_mutex); 00346 } 00347 00348 buf = command_buffer_dequeue(&output_queue_start, &output_queue_end); 00349 SDL_UnlockMutex(output_buffer_mutex); 00350 00351 while (buf && written < buf->len && !abort_thread) 00352 { 00353 int ret = send(csocket.fd, (const char *) buf->data + written, buf->len - written, 0); 00354 00355 if (ret == 0) 00356 { 00357 LOG(llevInfo, "Writer thread got EOF.\n"); 00358 break; 00359 } 00360 else if (ret == -1) 00361 { 00362 /* IO error */ 00363 #ifdef WIN32 00364 LOG(llevInfo, "Writer thread got error %d\n", WSAGetLastError()); 00365 #else 00366 LOG(llevInfo, "Writer thread got error %d: %s\n", errno, strerror(errno)); 00367 #endif 00368 break; 00369 } 00370 else 00371 { 00372 written += ret; 00373 } 00374 } 00375 00376 if (buf) 00377 { 00378 command_buffer_free(buf); 00379 buf = NULL; 00380 } 00381 } 00382 00383 if (buf) 00384 { 00385 command_buffer_free(buf); 00386 } 00387 00388 socket_close(&csocket); 00389 LOG(llevInfo, "Writer thread stopped.\n"); 00390 return 0; 00391 } 00392 00395 void socket_thread_start() 00396 { 00397 LOG(llevInfo, "Starting socket threads.\n"); 00398 00399 if (input_buffer_cond == NULL) 00400 { 00401 input_buffer_cond = SDL_CreateCond(); 00402 input_buffer_mutex = SDL_CreateMutex(); 00403 output_buffer_cond = SDL_CreateCond(); 00404 output_buffer_mutex = SDL_CreateMutex(); 00405 socket_mutex = SDL_CreateMutex(); 00406 } 00407 00408 abort_thread = 0; 00409 00410 input_thread = SDL_CreateThread(reader_thread_loop, NULL); 00411 00412 if (input_thread == NULL) 00413 { 00414 LOG(llevError, "socket_thread_start(): Unable to start socket thread: %s\n", SDL_GetError()); 00415 } 00416 00417 output_thread = SDL_CreateThread(writer_thread_loop, NULL); 00418 00419 if (output_thread == NULL) 00420 { 00421 LOG(llevError, "socket_thread_start(): Unable to start socket thread: %s\n", SDL_GetError()); 00422 } 00423 } 00424 00428 void socket_thread_stop() 00429 { 00430 LOG(llevInfo, "Stopping socket threads.\n"); 00431 00432 socket_close(&csocket); 00433 00434 SDL_WaitThread(output_thread, NULL); 00435 SDL_WaitThread(input_thread, NULL); 00436 00437 input_thread = output_thread = NULL; 00438 } 00439 00446 int handle_socket_shutdown() 00447 { 00448 if (abort_thread) 00449 { 00450 socket_thread_stop(); 00451 abort_thread = 0; 00452 00453 /* Empty all queues */ 00454 while (input_queue_start) 00455 { 00456 command_buffer_free(command_buffer_dequeue(&input_queue_start, &input_queue_end)); 00457 } 00458 00459 while (output_queue_start) 00460 { 00461 command_buffer_free(command_buffer_dequeue(&output_queue_start, &output_queue_end)); 00462 } 00463 00464 LOG(llevInfo, "Connection lost.\n"); 00465 return 1; 00466 } 00467 00468 return 0; 00469 } 00470 00474 int socket_get_error() 00475 { 00476 #ifdef WIN32 00477 return WSAGetLastError(); 00478 #else 00479 return errno; 00480 #endif 00481 } 00482 00486 int socket_close(struct ClientSocket *csock) 00487 { 00488 SDL_LockMutex(socket_mutex); 00489 00490 if (csock->fd == -1) 00491 { 00492 SDL_UnlockMutex(socket_mutex); 00493 return 1; 00494 } 00495 00496 #ifdef LINUX 00497 if (shutdown(csock->fd, SHUT_RDWR)) 00498 { 00499 perror("shutdown"); 00500 } 00501 00502 if (close(csock->fd)) 00503 { 00504 perror("close"); 00505 } 00506 #else 00507 shutdown(csock->fd, 2); 00508 closesocket(csock->fd); 00509 #endif 00510 00511 csock->fd = -1; 00512 00513 abort_thread = 1; 00514 00515 /* Poke anyone waiting at a cond */ 00516 SDL_CondSignal(input_buffer_cond); 00517 SDL_CondSignal(output_buffer_cond); 00518 00519 SDL_UnlockMutex(socket_mutex); 00520 00521 return 1; 00522 } 00523 00527 int socket_initialize() 00528 { 00529 #ifdef WIN32 00530 WSADATA w; 00531 WORD wVersionRequested = MAKEWORD(2, 2); 00532 int error; 00533 00534 csocket.fd = -1; 00535 error = WSAStartup(wVersionRequested, &w); 00536 00537 if (error) 00538 { 00539 wVersionRequested = MAKEWORD(2, 0); 00540 error = WSAStartup(wVersionRequested, &w); 00541 00542 if (error) 00543 { 00544 wVersionRequested = MAKEWORD(1, 1); 00545 error = WSAStartup(wVersionRequested, &w); 00546 00547 if (error) 00548 { 00549 LOG(llevBug, "socket_initialize(): Error initializing WinSock: %d.\n", error); 00550 return 0; 00551 } 00552 } 00553 } 00554 00555 LOG(llevInfo, "Using socket version %x.\n", w.wVersion); 00556 #endif 00557 return 1; 00558 } 00559 00562 void socket_deinitialize() 00563 { 00564 if (csocket.fd != -1) 00565 { 00566 socket_close(&csocket); 00567 } 00568 00569 #ifdef WIN32 00570 WSACleanup(); 00571 #endif 00572 } 00573 00580 static int socket_create(int *fd, char *host, int port) 00581 { 00582 uint32 start_timer; 00583 00584 /* Use new (getaddrinfo()) or old (gethostbyname()) socket API */ 00585 #if !defined(HAVE_GETADDRINFO) || defined(WIN32) 00586 /* This method is preferable unless IPv6 is required, due to buggy distros. */ 00587 struct sockaddr_in addr; 00588 #ifndef WIN32 00589 struct protoent *protox; 00590 int flags; 00591 00592 protox = getprotobyname("tcp"); 00593 00594 if (!protox) 00595 { 00596 LOG(llevBug, "Error getting protobyname (tcp)\n"); 00597 return 0; 00598 } 00599 00600 *fd = socket(PF_INET, SOCK_STREAM, protox->p_proto); 00601 00602 if (*fd == -1) 00603 { 00604 LOG(llevBug, "socket_create(): Could not create socket.\n"); 00605 return 0; 00606 } 00607 #else 00608 int error = 0, SocketStatusErrorNr; 00609 u_long temp; 00610 00611 *fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 00612 #endif 00613 00614 addr.sin_family = AF_INET; 00615 addr.sin_port = htons((unsigned short) port); 00616 00617 if (isdigit(*host)) 00618 { 00619 addr.sin_addr.s_addr = inet_addr(host); 00620 } 00621 else 00622 { 00623 struct hostent *hostbn = gethostbyname(host); 00624 00625 if (!hostbn) 00626 { 00627 *fd = -1; 00628 return 0; 00629 } 00630 00631 memcpy(&addr.sin_addr, hostbn->h_addr, hostbn->h_length); 00632 } 00633 00634 #ifndef WIN32 00635 /* Set non-blocking. */ 00636 flags = fcntl(*fd, F_GETFL); 00637 00638 if (fcntl(*fd, F_SETFL, flags | O_NONBLOCK) == -1) 00639 { 00640 LOG(llevBug, "socket_create(): Error on switching to non-blocking. fcntl %x.\n", fcntl(*fd, F_GETFL)); 00641 *fd = -1; 00642 return 0; 00643 } 00644 #else 00645 temp = 1; 00646 00647 /* Set non-blocking. */ 00648 if (ioctlsocket(*fd, FIONBIO, &temp) == -1) 00649 { 00650 LOG(llevBug, "socket_create(): Error on switching to non-blocking.\n"); 00651 *fd = -1; 00652 return 0; 00653 } 00654 #endif 00655 00656 /* Try to connect. */ 00657 start_timer = SDL_GetTicks(); 00658 00659 while (connect(*fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) 00660 { 00661 SDL_Delay(3); 00662 00663 if (start_timer + SOCKET_TIMEOUT_MS < SDL_GetTicks()) 00664 { 00665 *fd = -1; 00666 return 0; 00667 } 00668 00669 #ifdef WIN32 00670 SocketStatusErrorNr = WSAGetLastError(); 00671 00672 /* Connected. */ 00673 if (SocketStatusErrorNr == WSAEISCONN) 00674 { 00675 break; 00676 } 00677 00678 if (SocketStatusErrorNr == WSAEWOULDBLOCK || SocketStatusErrorNr == WSAEALREADY || (SocketStatusErrorNr == WSAEINVAL && error)) 00679 { 00680 error = 1; 00681 continue; 00682 } 00683 00684 LOG(llevBug, "Connect error: %d\n", SocketStatusErrorNr); 00685 *fd = -1; 00686 return 0; 00687 #endif 00688 } 00689 00690 #ifndef WIN32 00691 /* Set back to blocking. */ 00692 if (fcntl(*fd, F_SETFL, flags) == -1) 00693 { 00694 LOG(llevBug, "socket_create(): Error on switching to blocking. fcntl %x.\n", fcntl(*fd, F_GETFL)); 00695 *fd = -1; 00696 return 0; 00697 } 00698 #else 00699 temp = 0; 00700 00701 /* Set back to blocking. */ 00702 if (ioctlsocket(*fd, FIONBIO, &temp) == -1) 00703 { 00704 LOG(llevBug, "socket_create(): Error on switching to blocking.\n"); 00705 *fd = -1; 00706 return 0; 00707 } 00708 #endif 00709 #else 00710 struct addrinfo hints; 00711 struct addrinfo *res = NULL, *ai; 00712 char port_str[6], hostaddr[40]; 00713 00714 snprintf(port_str, sizeof(port_str), "%d", port); 00715 memset(&hints, 0, sizeof(hints)); 00716 00717 hints.ai_family = AF_UNSPEC; 00718 hints.ai_socktype = SOCK_STREAM; 00719 hints.ai_flags = AI_NUMERICSERV; 00720 00721 if (getaddrinfo(host, port_str, &hints, &res) != 0) 00722 { 00723 return 0; 00724 } 00725 00726 for (ai = res; ai; ai = ai->ai_next) 00727 { 00728 getnameinfo(ai->ai_addr, ai->ai_addrlen, hostaddr, sizeof(hostaddr), NULL, 0, NI_NUMERICHOST); 00729 *fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 00730 00731 if (*fd == -1) 00732 { 00733 continue; 00734 } 00735 00736 /* Set non-blocking. */ 00737 flags = fcntl(*fd, F_GETFL); 00738 00739 if (fcntl(*fd, F_SETFL, flags | O_NONBLOCK) == -1) 00740 { 00741 LOG(llevBug, "socket_create(): Error on switching to non-blocking. fcntl %x.\n", fcntl(*fd, F_GETFL)); 00742 *fd = -1; 00743 return 0; 00744 } 00745 00746 /* Try to connect. */ 00747 start_timer = SDL_GetTicks(); 00748 00749 while (connect(*fd, ai->ai_addr, ai->ai_addrlen) != 0) 00750 { 00751 SDL_Delay(3); 00752 00753 if (start_timer + SOCKET_TIMEOUT_MS < SDL_GetTicks()) 00754 { 00755 close(*fd); 00756 *fd = -1; 00757 break; 00758 } 00759 } 00760 00761 /* Set back to blocking. */ 00762 if (*fd != -1 && fcntl(*fd, F_SETFL, flags) == -1) 00763 { 00764 LOG(llevBug, "socket_create(): Error on switching to blocking. fcntl %x.\n", fcntl(*fd, F_GETFL)); 00765 *fd = -1; 00766 return 0; 00767 } 00768 00769 if (*fd != -1) 00770 { 00771 break; 00772 } 00773 } 00774 00775 freeaddrinfo(res); 00776 00777 if (*fd == -1) 00778 { 00779 return 0; 00780 } 00781 #endif 00782 00783 return 1; 00784 } 00785 00792 int socket_open(struct ClientSocket *csock, char *host, int port) 00793 { 00794 int oldbufsize, newbufsize = 65535; 00795 socklen_t buflen = sizeof(int); 00796 struct linger linger_opt; 00797 00798 LOG(llevInfo, "Connecting to %s:%d...\n", host, port); 00799 00800 if (!socket_create(&csock->fd, host, port)) 00801 { 00802 LOG(llevDebug, "Can't connect to server %s:%d.\n", host, port); 00803 return 0; 00804 } 00805 00806 linger_opt.l_onoff = 1; 00807 linger_opt.l_linger = 5; 00808 00809 if (setsockopt(csock->fd, SOL_SOCKET, SO_LINGER, (char *) &linger_opt, sizeof(struct linger))) 00810 { 00811 LOG(llevBug, "Error on setsockopt LINGER\n"); 00812 } 00813 00814 if (setting_get_int(OPT_CAT_CLIENT, OPT_MINIMIZE_LATENCY)) 00815 { 00816 int tmp = 1; 00817 00818 if (setsockopt(csock->fd, IPPROTO_TCP, TCP_NODELAY, (char *) &tmp, sizeof(tmp)) == -1) 00819 { 00820 LOG(llevBug, "socket_open(): Error setting TCP_NODELAY."); 00821 } 00822 } 00823 00824 if (getsockopt(csock->fd, SOL_SOCKET, SO_RCVBUF, (char *) &oldbufsize, &buflen) == -1) 00825 { 00826 oldbufsize = 0; 00827 } 00828 00829 if (oldbufsize < newbufsize) 00830 { 00831 if (setsockopt(csock->fd, SOL_SOCKET, SO_RCVBUF, (char *) &newbufsize, sizeof(&newbufsize))) 00832 { 00833 LOG(llevBug, "socket_open(): Unable to set output buf size to %d", newbufsize); 00834 setsockopt(csock->fd, SOL_SOCKET, SO_RCVBUF, (char *) &oldbufsize, sizeof(&oldbufsize)); 00835 } 00836 } 00837 00838 LOG(llevInfo, "Connected to %s:%d\n", host, port); 00839 00840 return 1; 00841 }
1.7.4