Atrinik Client 2.5
client/socket.c
Go to the documentation of this file.
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 }