/* vim: ts=4 sw=4 sts=4 ff=unix fenc=utf-8 : * ===================================================================== * sc_socket.c * Copyright (c) 2003 - 2011 sys0tem * LICENSE : * LGPL (GNU Lesser General Public License - Version 3,29 June 2007) * http://www.gnu.org/copyleft/lesser.html * or * EPL (Eclipse Public License - v1.0) * http://www.eclipse.org/legal/epl-v10.html * ===================================================================== */ #include <sc_socket.h> #include <sc_string.h> /* ===================================================================== * プロトタイプ宣言 * ===================================================================== */ #if (SC_isWindows) static void SC_Socket_cleanup(void); #endif /** * ソケットスタートアップ * * @return true/false (初期化成功/失敗) */ bool SC_Socket_init(void) { #if (SC_isWindows) static bool isInit = false; WSADATA wsaData; int ret; if (isInit) { /* 初期化済みならなにもしない */ return true; } isInit = true; ret = WSAStartup(MAKEWORD(2,0), &wsaData); if (ret != 0) { /* 初期化エラー */ return false; } atexit(SC_Socket_cleanup); #endif return true; } /** * ソケットに関するエラー番号を取得します。 * * @return エラー番号 */ int SC_getSocketError(void) { #if (SC_isWindows) return WSAGetLastError(); #else return SC_getError(); #endif } /** * 指定されたホスト、サービスにTCPにて接続します。 * * @param host ホスト名またはIPアドレス * @param service サービス名またはポート番号 * @return ソケット */ socket_t SC_Socket_tcpConnect(const char* host, const char* service) { int sockfd, ret; struct addrinfo hints, *rp, *result; memset(&hints, 0x00, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; ret = getaddrinfo(host, service, &hints, &result); if (ret != 0) { return INVALID_SOCKET; } for (rp = result; rp != NULL; rp = rp->ai_next) { sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sockfd < 0) { /* 生成失敗 */ continue; } ret = connect(sockfd, rp->ai_addr, rp->ai_addrlen); if (ret != SOCKET_ERROR) { /* 接続成功 */ break; } /* 接続失敗 */ socket_close(sockfd); } if (rp == NULL) { freeaddrinfo(result); return INVALID_SOCKET; } freeaddrinfo(result); return sockfd; } /** * 指定されたアドレス、サービスにて待ち受けます。 * * @param host ホスト名またはIPアドレス * @param service サービス名またはポート番号 * @param addrlenp アドレス構造体サイズ格納用ポインタ * @param backlog バックログ数 * @return ソケット */ socket_t SC_Socket_tcpListen( const char* host, const char* service, socklen_t* addrlenp, int backlog) { int sockfd, ret; struct addrinfo hints, *rp, *result; const int on = 1; memset(&hints, 0x00, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; ret = getaddrinfo(host, service, &hints, &result); if (ret != 0) { return INVALID_SOCKET; } for (rp = result; rp != NULL; rp = rp->ai_next) { sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sockfd < 0) { /* 生成失敗 */ continue; } /* bind の前の setsockopt は常に成功のためノーチェック */ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, sizeof(on)); ret = bind(sockfd, rp->ai_addr, rp->ai_addrlen); if (ret == 0) { /* bind 成功 */ break; } /* 接続失敗 */ socket_close(sockfd); } if (rp == NULL) { freeaddrinfo(result); return INVALID_SOCKET; } ret = listen(sockfd, backlog); if (ret == SOCKET_ERROR) { freeaddrinfo(result); return INVALID_SOCKET; } if (addrlenp != NULL) { *addrlenp = rp->ai_addrlen; } freeaddrinfo(result); return sockfd; } /** * 指定されたアドレス、サービスにて待ち受けます。 * * @param host ホスト名またはIPアドレス * @param service サービス名またはポート番号 * @param saptr アドレスポインタ * @param lenp アドレス構造体サイズ格納用ポインタ * @return ソケット */ socket_t SC_Socket_udpClient(const char* host, const char* service, struct sockaddr** saptr, socklen_t* lenp) { int sockfd, ret; struct addrinfo hints, *rp, *result; memset(&hints, 0x00, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; ret = getaddrinfo(host, service, &hints, &result); if (ret != 0) { return INVALID_SOCKET; } for (rp = result; rp != NULL; rp = rp->ai_next) { sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sockfd >= 0) { /* 生成成功 */ break; } } if (rp == NULL) { freeaddrinfo(result); return INVALID_SOCKET; } *saptr = (struct sockaddr*) malloc(rp->ai_addrlen); if (*saptr == NULL) { freeaddrinfo(result); return INVALID_SOCKET; } memcpy(*saptr, rp->ai_addr, rp->ai_addrlen); *lenp = rp->ai_addrlen; freeaddrinfo(result); return sockfd; } /** * 指定されたアドレス、サービスにて待ち受けます。 * * @param host ホスト名またはIPアドレス * @param service サービス名またはポート番号 * @param addrlenp アドレス構造体サイズ格納用ポインタ * @return ソケット */ socket_t SC_Socket_udpServer( const char* host, const char* service, socklen_t* addrlenp) { int sockfd, ret; struct addrinfo hints, *rp, *result; memset(&hints, 0x00, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; ret = getaddrinfo(host, service, &hints, &result); if (ret != 0) { return INVALID_SOCKET; } for (rp = result; rp != NULL; rp = rp->ai_next) { sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sockfd < 0) { /* 生成失敗 */ continue; } ret = bind(sockfd, rp->ai_addr, rp->ai_addrlen); if (ret == 0) { /* bind 成功 */ break; } /* 失敗 */ socket_close(sockfd); } if (rp == NULL) { freeaddrinfo(result); return INVALID_SOCKET; } if (addrlenp != NULL) { *addrlenp = rp->ai_addrlen; } freeaddrinfo(result); return sockfd; } /** * ソケットをクローズする。 * * @param sockfd ソケットディスクリプタ */ bool SC_Socket_close(socket_t sockfd) { int ret; int readSize; char buff[SC_SOCKET_TEMPBUFF_SIZE]; ret = shutdown(sockfd, SHUT_WR); if (ret == 0) { do { /* 届いているデータをすべて読み取る */ readSize = recv(sockfd, buff, sizeof(buff), 0); } while (readSize > 0); } ret = socket_close(sockfd); if (ret == 0) { return true; } return false; } /* ===================================================================== * 以下、内部でのみ使用する関数 * ===================================================================== */ /** * ソケットクリーンアップ */ #if (SC_isWindows) static void SC_Socket_cleanup(void) { WSACleanup(); } #endif