static void server_send(char *cmd) { int l = strlen(cmd); byte *z = alloca(l + 1); memcpy(z, cmd, l); z[l++] = '\n'; while (l) { int cnt = write(server_fd, z, l); if (cnt < 0) { if (errno == EAGAIN) wait_for_write(server_fd); else if (errno == EINTR) continue; else die("Server write error: %m"); } else { l -= cnt; z += cnt; } } }
/* send conn's ip and port which used to connect to user server * to mesage server */ void send_conn_info_to_message(struct conn_server *server) { struct list_packet *lp = allocator_malloc(&server->packet_allocator); packet_init(lp); set_length(lp, 18); set_command(lp, CMD_CONN_INFO); set_field_uint32_t(get_parameters(lp), 0, server->conn_user_ip); set_field_uint16_t(get_parameters(lp), 4, server->conn_user_port); list_add_tail(&lp->list, &(server->message_conn.send_packet_list)); wait_for_write(server->efd, server->message_conn.sfd); }
/* send offline message to status server */ void send_offline_to_status(struct conn_server *server, uint32_t uin) { struct list_packet *lp = allocator_malloc(&server->packet_allocator); packet_init(lp); set_length(lp, 24); set_command(lp, CMD_STATUS_CHANGE); set_uin(lp, uin); set_field_uint32_t(get_parameters(lp), 0, uin); set_field_uint16_t(get_parameters(lp), 10, STATUS_CHANGE_OFFLINE); list_add_tail(&lp->list, &(server->status_conn.send_packet_list)); wait_for_write(server->efd, server->status_conn.sfd); }
/* other kind of packet, check clienti's login type, * if login ok, send the packet to client */ void srv_other_packet(struct conn_server *server, struct list_packet *packet) { uint32_t uin = get_uin(packet); struct connection *conn = get_conn_by_uin(server, uin); /* need to check login status */ if (!conn || conn->type != LOGIN_OK_CONNECTION) { allocator_free(&server->packet_allocator, packet); return; } list_add_tail(&packet->list, &conn->send_packet_list); wait_for_write(server->efd, conn->sfd); }
/* client login successful, we mark the conn as login_ok, * and send the packet to client */ void srv_login_ok(struct conn_server *server, struct list_packet *packet) { uint32_t uin = get_uin(packet); struct connection *conn = get_conn_by_uin(server, uin); if (!conn) { allocator_free(&server->packet_allocator, packet); return; } conn->type = LOGIN_OK_CONNECTION; list_add_tail(&packet->list, &conn->send_packet_list); wait_for_write(server->efd, conn->sfd); }
int dac_sendall(dac_t *d, const char *data, int len) { do { int res = wait_for_write(d, 1500000); if (res < 0) { return -1; } else if (res == 0) { trace(d, "write timed out\n"); } res = send(d->conn.sock, data, len, 0); if (res == SOCKET_ERROR) { log_socket_error(d, "send"); return -1; } len -= res; data += res; } while (len); return 0; }
size_t network::stream_write( network::socket_t const &sockfd, void const *buffer, size_t buffer_size, size_t buffer_offset, size_t amount_to_send, bool *out_disconnected, int *out_error /* = NULL */) { char const *buf = (char*) buffer + buffer_offset; LLSOCKET_SET_ERROR_RESULT(0); if (buffer == NULL || buffer_size == 0 || out_disconnected == NULL || buffer_offset > buffer_size || amount_to_send > buffer_size - buffer_offset) { // no data or invalid parameter. return immediately. if (out_disconnected != NULL) *out_disconnected = false; return 0; } // by default, we have not disconnected. if we detect a disconnection // while sending, we will set this value to true. *out_disconnected = false; // enter a loop to ensure that all of the data gets sent. // we may have to issue multiple send calls to the socket. size_t retry_count = 0; size_t bytes_sent = 0; size_t bytes_total = amount_to_send; while (bytes_sent < bytes_total) { if (retry_count >= LLSOCKET_MAX_RETRIES) { // this socket has exceeded its retry count. disconnect it. network::stream_shutdown(sockfd, NULL, NULL); *out_disconnected = true; return 0; } // attempt to send as much data as we can write to the socket. int n_to_send = (int) bytes_total - (int) bytes_sent; int n_sent = send(sockfd, buf, n_to_send, 0); if (!network::socket_error(n_sent) && n_sent > 0) { // @note: do NOT reset retry_count to zero in this case. // under Windows at least, the socket will become available // again, but immediately fail. awesomeness. bytes_sent += n_sent; buf += n_sent; continue; } #if defined(_WIN32) || defined(_WIN64) // an error occurred. determine whether we are still connected. int err = WSAGetLastError(); switch (err) { case WSANOTINITIALISED: case WSAENETDOWN: case WSAENETRESET: case WSAENOTCONN: case WSAENOTSOCK: case WSAESHUTDOWN: case WSAEHOSTUNREACH: case WSAEINVAL: case WSAECONNABORTED: case WSAECONNRESET: case WSAETIMEDOUT: { // something has gone wrong with the connection. network::stream_shutdown(sockfd, NULL, NULL); LLSOCKET_SET_ERROR_RESULT(err); *out_disconnected = true; } return bytes_sent; case WSAEACCES: { // attempted to send to a broadcast address without // setting the appropriate socket options. network::stream_shutdown(sockfd, NULL, NULL); LLSOCKET_SET_ERROR_RESULT(err); *out_disconnected = true; } return bytes_sent; case WSAENOBUFS: // ENOBUFS case WSAEWOULDBLOCK: // EAGAIN { // the resource is temporarily unavailable, probably // because the socket is being flooded with data. we // wait until the socket becomes available again, or // our timeout interval elapses. if the socket becomes // available, we continue sending; if the select times // out, we forcibly disconnect the client. if (wait_for_write(sockfd, LLSOCKET_WAIT_TIMEOUT_USEC)) { // the socket is available again. retry. retry_count++; break; } else { // the wait timed out. disconnect the socket. network::stream_shutdown(sockfd, NULL, NULL); LLSOCKET_SET_ERROR_RESULT(err); *out_disconnected = true; } } return bytes_sent; default: { // unknown error; try again. LLSOCKET_SET_ERROR_RESULT(err); retry_count++; } break; } #else // an error occurred. determine whether we are still connected. int err = errno; switch (err) { case EBADF: case ECONNRESET: case ENOTCONN: case ENOTSOCK: case ETIMEDOUT: case EHOSTUNREACH: case ENETDOWN: case ENETUNREACH: case EPIPE: { // something has gone wrong with the connection. network::stream_shutdown(sockfd, NULL, NULL); LLSOCKET_SET_ERROR_RESULT(err); *out_disconnected = true; } return bytes_sent; case EACCES: { // attempted to send to a broadcast address without // setting the appropriate socket options. network::stream_shutdown(sockfd, NULL, NULL); LLSOCKET_SET_ERROR_RESULT(err); *out_disconnected = true; } return bytes_sent; case ENOBUFS: case EAGAIN: { // the resource is temporarily unavailable, probably // because the socket is being flooded with data. we // wait until the socket becomes available again, or // our timeout interval elapses. if the socket becomes // available, we continue sending; if the select times // out, we forcibly disconnect the client. if (wait_for_write(sockfd, LLSOCKET_WAIT_TIMEOUT_USEC)) { // the socket is available again. retry. retry_count++; break; } else { // the wait timed out. disconnect the socket. network::stream_shutdown(sockfd, NULL, NULL); LLSOCKET_SET_ERROR_RESULT(err); *out_disconnected = true; } } return bytes_sent; default: { // unknown error; try again. LLSOCKET_SET_ERROR_RESULT(err); retry_count++; } break; } #endif } return bytes_sent; }
/* we need to forward this packet to message server, so put it onto * message_conn's send queue */ void cmd_message(struct conn_server *server, struct connection *conn, struct list_packet *packet) { list_add_tail(&packet->list, &(server->message_conn.send_packet_list)); wait_for_write(server->efd, server->message_conn.sfd); }