#1
by にこよん » 5年前
win10 vC++でwinsock2の通信が成功しません
ローカルホストでは繋がるのですが、外部PCから接続しようとするとエラー(10060)を返されます
2週間ぐらい調べたり改造したりしているのですが、一向に状況は変わらないので推測される原因だけでも教えてほしいです
ファイアーウォールやウイルスセキュリティーは切ってからテストしています
クラスにまとめたものですが、使い方は関数名の通り上から順に呼び出すだけです
class SERVERとclass CLIENTで通信したいです
class UDP_COMMはスルーしてくださいm(__)m
コード:
//ヘッダ
#ifndef DEF_COMMLIB //二重include防止
#define DEF_COMMLIB
#define _WINSOCK_DEPRECATED_NO_WARNINGS //警告を消す
#pragma comment(lib, "ws2_32.lib") //通信関係のライブラリを読み込む
#include <winsock2.h> //通信ヘッダ
#include <ws2tcpip.h>
#include <tchar.h> //_Tの定義
//エラー
#define COMM_LIB_NO_ERROR 0 //正常に終了
#define COMM_LIB_ERROR -1 //異常終了
//初期化モード
#define SERVER_PROGRAM 0 //サーバープログラムモードで初期化する
#define CLIENT_PROGRAM 1 //クライアントプログラムモードで初期化する
//使用するポート番号
#define PORT 25420 //使用するポート番号
#define SOCKET_MAX 20 //同時に通信できる最大数
//#define MAX_DATA_SIZE 262144 //データ通信で使用できる最大サイズ
#define MAX_DATA_SIZE 65536 //データ通信で使用できる最大サイズ
#define WSA_ERR(str, ...) WSAErrorLog(str, _T(__FUNCTION__), __LINE__, __VA_ARGS__) //エラー時のマクロ
#define CALL_ONCE(src) {static bool call_flag = false; if(!call_flag) {src;} call_flag = true;} //一度だけ処理する時のマクロ
//winsockを初期化する(エラーで終了必須
int CommLibInit();
//winsockの終了処理を行う
int CommLibEnd();
//チャットログを書き出す
void AddChatLog(const char *LogMessage, ...);
//サーバー用ログを書き出す
void AddServerLog(const char *LogMessage, ...);
//一時的にログを表示する
void AddTempLog(const char *LogMessage, ...);
//エラー情報をファイルに書き出す
void WSAErrorLog(const char *errorMessage, LPCTSTR lpszFuncName, int lineN, ...);
class SERVER {
SOCKET sock_lobby; //通信要請を受け取るソケット
sockaddr_in addr; //ロビーのアドレス情報
SOCKET sock; //クライアントとの通信を行うソケット
sockaddr_in client_addr; //クライアント情報
SOCKET udp_sock;
sockaddr_in udp_addr; //ロビーのアドレス情報
hostent *host; //ホストの情報(IP取得用)
public:
//コンストラクタ
SERVER();
//サーバーのスタートアップ処理
int Start();
//クライアントからの通信を受け取る
int Accept();
//データを送信する
int Send(const TCHAR *data, const int size);
//データを受信する
int Recv(char data[]);
//ホストネームを取得する
int GetHostName(char name[]);
};
class CLIENT {
struct sockaddr_in server_addr;
SOCKET sock;
public:
//コンストラクタ
CLIENT();
//データをサーバーに送信する
int Send(const TCHAR *data, int size);
//データをサーバーから受信する
int Recv(char data[]);
};
class UDP_COMM {
SOCKET sock; //通信を行うソケット
sockaddr_in addr; //アドレス情報
public:
//UDP通信のスタートアップ処理(0=サーバー
int Start(int program_kind);
//データを送信する
int Send(const TCHAR *data, const int size);
//データを受信する
int Recv(char data[]);
//UDP通信を終了する
int End();
};
#endif
コード:
//.cpp
#define LOG_PRINTF_S true //コンソールアプリケーション画面出力を行うか
#include "CommLib.h"
#include <stdio.h>
#include <iostream>
#define CHAR_ADD_KEY (31) //文字列を保護するキー
#if _DEBUG
static char host_name[64] = { //ホストサーバーのIPアドレス
'l' - CHAR_ADD_KEY,
'o' - CHAR_ADD_KEY,
'c' - CHAR_ADD_KEY,
'a' - CHAR_ADD_KEY,
'l' - CHAR_ADD_KEY,
'h' - CHAR_ADD_KEY,
'o' - CHAR_ADD_KEY,
's' - CHAR_ADD_KEY,
't' - CHAR_ADD_KEY,
0x00
};//localhost
#else
static char host_name[64] = { //ホストサーバーのIPアドレス
'i' - CHAR_ADD_KEY,
'p' - CHAR_ADD_KEY,
0x00
};
#endif
//チャットログを書き出す
void AddChatLog(const char *LogMessage, ...) {
va_list VaList;
char String[1024]; //変換後の文字列を格納する配列
va_start(VaList, LogMessage); //va_list の準備( 第二引数は引数『...』の直前の引数を記述します )
vsprintf_s(String, sizeof(String), LogMessage, VaList); //書式文字列 + 可変長引数リストから書式文字列に従って文字配列に変換
va_end(VaList); //va_list の後始末
char date[128];
SYSTEMTIME ddata; //日付情報の変数を定義
GetLocalTime(&ddata); //日付情報取得
sprintf_s(date, "%04d/%02d/%02d %02d:%02d (%02d)", ddata.wYear, ddata.wMonth, ddata.wDay, ddata.wHour, ddata.wMinute, ddata.wSecond); //日付情報を文字列にする
char message[1024];
sprintf_s(message, "%s:%s\n", date, String); //全ての文字列を統合する
FILE *fp;
errno_t err;
err = fopen_s(&fp, "GameChat.log", "a"); //書き込みでログファイルを開く
if (err != NULL) return; //ファイルを開いたときにエラーが返ってきたら戻る
fprintf_s(fp, message); //エラーメッセージを出力する
fclose(fp); //ファイルを閉じる
}
//サーバー用ログを書き出す
void AddServerLog(const char *LogMessage, ...) {
va_list VaList;
char String[1024]; //変換後の文字列を格納する配列
va_start(VaList, LogMessage); //va_list の準備( 第二引数は引数『...』の直前の引数を記述します )
vsprintf_s(String, sizeof(String), LogMessage, VaList); //書式文字列 + 可変長引数リストから書式文字列に従って文字配列に変換
va_end(VaList); //va_list の後始末
char date[128];
SYSTEMTIME ddata; //日付情報の変数を定義
GetLocalTime(&ddata); //日付情報取得
sprintf_s(date, "%04d/%02d/%02d %02d:%02d:%02d ", ddata.wYear, ddata.wMonth, ddata.wDay, ddata.wHour, ddata.wMinute, ddata.wSecond); //日付情報を文字列にする
char message[1024];
sprintf_s(message, "%s %s\n", date, String); //全ての文字列を統合する
FILE *fp;
errno_t err;
static int a = 0;
if (a == 2) {
err = fopen_s(&fp, "Server.log", "w");
fclose(fp);
a = 1;
}
err = fopen_s(&fp, "Server.log", "a"); //書き込みでログファイルを開く
if (err != NULL) return; //ファイルを開いたときにエラーが返ってきたら戻る
fprintf_s(fp, message); //エラーメッセージを出力する
fclose(fp); //ファイルを閉じる
#if LOG_PRINTF_S //もし、コンソールアプリケーションなら
printf_s(message); //メッセージをウインドウに書き出す
#endif
}
//winsockを初期化する
int CommLibInit() {
WSADATA wsaData; //初期化情報を格納する構造体
int err = WSAStartup(MAKEWORD(2, 3), &wsaData);
if (err != 0) {
WSA_ERR("winsockの初期化に失敗しました");
switch (err) {
case WSASYSNOTREADY:
WSA_ERR("WSASYSNOTREADY ネットワークサブシステムがネットワークへの接続を準備できていません");
break;
case WSAVERNOTSUPPORTED:
WSA_ERR("WSAVERNOTSUPPORTED 要求されたwinsockのバージョンがサポートされていません");
break;
case WSAEINPROGRESS:
WSA_ERR("WSAEINPROGRESS ブロッキング操作の実行中であるか、 またはサービスプロバイダがコールバック関数を処理しています");
break;
case WSAEPROCLIM:
WSA_ERR("WSAEPROCLIM winsockが処理できる最大プロセス数に達しました");
break;
case WSAEFAULT:
WSA_ERR("WSAEFAULT 第二引数である lpWSAData は有効なポインタではありません");
break;
default:
WSA_ERR("UNKNOWN 不明なエラーです");
break;
}
return COMM_LIB_ERROR;
}
for (int i = 0; host_name[i] != 0x00; i++) host_name[i] += CHAR_ADD_KEY; //使う前に文字列を複合化する
AddTempLog("通信エンジンの初期化に成功しました");
return COMM_LIB_NO_ERROR;
}
//winsockを終了する
int CommLibEnd() {
WSACleanup();
return 0;
}
//画面にログを表示する
void AddTempLog(const char *LogMessage, ...) {
va_list VaList;
char String[1024]; //変換後の文字列を格納する配列
va_start(VaList, LogMessage); //va_list の準備( 第二引数は引数『...』の直前の引数を記述します )
vsprintf_s(String, sizeof(String), LogMessage, VaList); //書式文字列 + 可変長引数リストから書式文字列に従って文字配列に変換
va_end(VaList); //va_list の後始末
char date[128];
SYSTEMTIME ddata; //日付情報の変数を定義
GetLocalTime(&ddata); //日付情報取得
sprintf_s(date, "%04d/%02d/%02d %02d:%02d:%02d", ddata.wYear, ddata.wMonth, ddata.wDay, ddata.wHour, ddata.wMinute, ddata.wSecond); //日付情報を文字列にする
char message[MAX_DATA_SIZE + MAX_DATA_SIZE];
sprintf_s(message, "%s %s\n", date, String); //全ての文字列を統合する
#if LOG_PRINTF_S //もし、コンソールアプリケーションなら
printf_s(message); //メッセージをウインドウに書き出す
#endif
}
//エラー情報をファイルに書き出す
void WSAErrorLog(const char *errorMessage, LPCTSTR lpszFuncName, int lineN, ...) {
va_list VaList;
char ErrMessage[2048]; //変換後の文字列を格納する配列
va_start(VaList, lineN); //va_list の準備( 第二引数は引数『...』の直前の引数を記述します )
vsprintf_s(ErrMessage, sizeof(ErrMessage), errorMessage, VaList); //書式文字列 + 可変長引数リストから書式文字列に従って文字配列に変換
va_end(VaList); //va_list の後始末
static int old_line[3] = { 0 };
char funcName[512];
char date[512];
char message[1024];
sprintf_s(funcName, "%s(%04d)", lpszFuncName, lineN); //関数の名前を文字列にする
SYSTEMTIME ddata; //日付情報の変数を定義
GetLocalTime(&ddata); //日付情報取得
sprintf_s(date, "%04d/%02d/%02d %02d:%02d", ddata.wYear, ddata.wMonth, ddata.wDay, ddata.wHour, ddata.wMinute); //日付情報を文字列にする
AddTempLog(ErrMessage); //クライアントは一時ログにメッセージを追加
sprintf_s(message, "%s - % -30s「%s」\n", date, funcName, ErrMessage); //全ての情報を一つの文字列にする
if (old_line[0] != lineN) old_line[0] = lineN; //前の行と違う行なら現在の行を保存する
else if (old_line[1] != lineN) old_line[1] = lineN; //2連続で同じ行なら現在の行を1に保存する
else if (old_line[2] != lineN) old_line[2] = lineN; //3連続で同じ行なら現在の行を2に保存する
else if (old_line[3] != lineN) {
old_line[3] = lineN; //4連続で同じ行なら現在の行を3に保存する
sprintf_s(message, "%s - % -30s「%s」\n", date, "ERRsystem(0)", "持続的エラーの可能性が非常に高いためログの発行を一時停止します");
printf_s(message); //エラーメッセージを画面に出力する
FILE *fp;
errno_t err;
err = fopen_s(&fp, "error_ws2.log", "a"); //書き込みでログファイルを開く
if (err != NULL) return; //ファイルを開いたときにエラーが返ってきたら戻る
fprintf_s(fp, message); //エラーメッセージを出力する
fclose(fp); //ファイルを閉じる
return;
}
else if (old_line[3] == lineN) { //4連続以降ならエラーを出力せずに戻る
return;
}
FILE *fp;
errno_t err;
err = fopen_s(&fp, "error_ws2.log", "a"); //書き込みでログファイルを開く
if (err != NULL) return; //ファイルを開いたときにエラーが返ってきたら戻る
fprintf_s(fp, message); //エラーメッセージを出力する
fclose(fp); //ファイルを閉じる
}
void dump(void *vp, int n) {
unsigned char *p = (unsigned char *)vp;
for (int i = 0; i < n; i++) printf(" %02x", p[i]);
putchar('\n');
}
SERVER::SERVER() {
}
int SERVER::Start() {
sock_lobby = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //ソケットの作成
if (sock_lobby == INVALID_SOCKET) {
WSA_ERR("ソケットの作成に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
else AddTempLog("ロビーソケットを作成しました");
bool yes = true; //1が登録されたポインタを作成
setsockopt(sock_lobby, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes)); //TIME_WAIT状態への対策(ソケットオプションの設定
addr.sin_family = AF_INET; //ソケットの設定をする
addr.sin_port = htons(PORT); //使用するポート
addr.sin_addr.s_addr = htonl(INADDR_ANY); //接続するアドレス
if (bind(sock_lobby, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
WSA_ERR("ローカルアドレスとソケットの関連付けに失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
else AddTempLog("ローカルアドレスとソケットの関連付けが完了しました");
u_long val = 1; //ノンブロッキング(非同期通信)に設定する
ioctlsocket(sock_lobby, FIONBIO, &val);
if (listen(sock_lobby, 5) != 0) { //TCPクライアントからの接続要求を待てる状態にする
WSA_ERR("ソケットをリッスン状態にできませんでした(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
else AddTempLog("ソケットを通信受付状態にしました");
AddTempLog("ロビーソケットの初期化が正常に終了しました port=%d", PORT);
return COMM_LIB_NO_ERROR;
}
int SERVER::Accept() {
int len = sizeof(client_addr); //TCPクライアントからの接続要求を受け付ける
sock = accept(sock_lobby, (struct sockaddr *)&client_addr, &len);
if (sock == INVALID_SOCKET) { //ソケットの値が正常ではない
if (WSAGetLastError() != WSAEWOULDBLOCK) { //要請がない場合ではなかったら
WSA_ERR("クライアントからの接続に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
}
else { //接続要請があれば
host = gethostbyaddr((const char *)&client_addr.sin_addr, sizeof(addr.sin_addr), AF_INET);
if (host == NULL) { //失敗したらエラーを表示して終了
WSA_ERR("ホスト情報の取得に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
//AddTempLog("accepted connection from [%s], ip=%s, port=%d", host->h_name, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
int connection = GetPrivateProfileInt("HOST_NAME", host->h_name, 0, ".\\host_list.txt");
if(!connection) WritePrivateProfileString("HOST_NAME", host->h_name, "1", ".\\host_list.txt"); //ホスト情報をデータベースに登録する
return 1;
}
return COMM_LIB_NO_ERROR;
}
int SERVER::Recv(char data[]) {
char buf[MAX_DATA_SIZE];
memset(buf, 0, sizeof(buf));
int n = recv(sock, buf, sizeof(char)*MAX_DATA_SIZE, 0);
if (n < 0) {
if (WSAGetLastError() != WSAEWOULDBLOCK) { //何らかのエラーが発生している
WSA_ERR("データの受信に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
}
else if (n != 0) { //データが届いたら
//AddTempLog("データを受信しました-%d「%.64s」", n, buf);
memcpy(data, buf, n);
return n;
}
return COMM_LIB_NO_ERROR;
}
int SERVER::Send(const TCHAR *data, int size) {
if (send(sock, data, /*strlen(data)*/size, 0) < 1) {
WSA_ERR("データの送信に失敗しました(%d)", WSAGetLastError());
closesocket(sock); //TCPセッションの終了
memset(&sock, 0, sizeof(sock));
return COMM_LIB_ERROR;
}
//else AddTempLog("データを送信しました-%d「%.64s」",size, data);
closesocket(sock); //TCPセッションの終了
memset(&sock, 0, sizeof(sock));
return COMM_LIB_NO_ERROR;
}
int SERVER::GetHostName(char name[]) {
memcpy(name, host->h_name, strlen(host->h_name));
return 0;
}
CLIENT::CLIENT() {
}
int CLIENT::Recv(char data[]) {
char buf[MAX_DATA_SIZE];
memset(buf, 0, sizeof(buf));
int n = recv(sock, buf, sizeof(buf), 0);
if (n < 0) {
if (WSAGetLastError() != WSAEWOULDBLOCK) { //何らかのエラーが発生している
WSA_ERR("データの受信に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
}
else if (n != 0) { //データが届いたら
//AddTempLog("データを受信しました-%d「%.64s」", n, buf);
closesocket(sock); //TCPセッションの終了
memset(&sock, 0, sizeof(sock));
memcpy(data, buf, n);
return n;
}
return COMM_LIB_NO_ERROR;
}
int CLIENT::Send(const TCHAR *data, int size) {
sock = socket(AF_INET, SOCK_STREAM, 0); //ソケットの作成
if (sock == INVALID_SOCKET) {
WSA_ERR("ソケットの作成に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
//else AddTempLog("ソケットを作成しました");
server_addr.sin_family = AF_INET; //ソケットの設定をする
server_addr.sin_port = htons(PORT); //使用するポート
hostent *host = gethostbyname(host_name); //リリース時にはホストIPを使う
if (host == NULL) {
WSA_ERR("サーバー情報がありません");
return COMM_LIB_ERROR;
}
struct in_addr in;
memcpy(&in, host->h_addr, host->h_length);
//std::cout << inet_ntoa(in) << std::endl;
//server_addr.sin_addr.S_un.S_addr = *(BYTE*)host->h_addr_list[0]; //サーバーIPアドレスを取得する
//AddTempLog("サーバー情報の取得に成功しました");
server_addr.sin_addr.s_addr = inet_addr(inet_ntoa(in));
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0) { //サーバに接続を試みる
WSA_ERR("サーバーに接続できませんでした(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
//else AddTempLog("サーバーに接続しました");
if (send(sock, data, size, 0) < 1) {
WSA_ERR("データの送信に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
//else AddTempLog("データを送信しました-%d「%s」", size, data);
return COMM_LIB_NO_ERROR;
}
int UDP_COMM::Start(int program_kind) {
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == INVALID_SOCKET) {
WSA_ERR("UDPソケットの生成に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
else AddTempLog("UDPソケットの生成に成功しました");
if (!program_kind) {
//ファイルディスクリプターで参照されるソケットにオプションの設定
//SOL_SOCKET : ソケット API 層
//SO_REUSEADDR : bindが失敗するのを回避
int on = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) == SOCKET_ERROR)
{
WSA_ERR("オプションの設定に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
}
memset(&addr, 0, sizeof(addr)); //アドレスの初期化
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
if (!program_kind) {
addr.sin_addr.S_un.S_addr = INADDR_ANY; //サーバーの設定
addr.sin_addr.s_addr = htonl(INADDR_ANY); //IPアドレスを格納したin_addr構造体
}
else { //クライアントの設定
//PHOSTENT phostent = gethostbyname(host_name);
//if (phostent == NULL) {
// WSA_ERR("サーバー情報がありません");
// return COMM_LIB_ERROR;
//}
//memcpy(&addr, phostent->h_addr_list[0], 4);
hostent *host = gethostbyname(host_name); //リリース時にはホストIPを使う
if (host == NULL) {
WSA_ERR("サーバー情報がありません");
return COMM_LIB_ERROR;
}
addr.sin_addr.S_un.S_addr = *(unsigned int *)host->h_addr_list[0]; //サーバーIPアドレスを取得する
AddTempLog("サーバー情報の取得に成功しました");
}
if (!program_kind) { //サーバーの場合は情報が来るのを待つ状態にする
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
WSA_ERR("ローカルアドレスとUDPソケットの関連付けに失敗しました(%d)", WSAGetLastError());
closesocket(sock);
return COMM_LIB_ERROR;
}
else AddTempLog("ローカルアドレスとUDPソケットの関連付けが完了しました");
u_long val = 1; //ノンブロッキング(非同期通信)に設定する
ioctlsocket(sock, FIONBIO, &val);
}
AddTempLog("UDPソケットの初期化が正常に終了しました port=%d", PORT);
return COMM_LIB_NO_ERROR;
}
int UDP_COMM::Recv(char data[]) {
char buf[MAX_DATA_SIZE];
memset(buf, '\0', sizeof(buf));
int n = recv(sock, buf, sizeof(buf), 0);
if (n < 0) {
if (WSAGetLastError() != WSAEWOULDBLOCK) { //何らかのエラーが発生している
WSA_ERR("データの受信に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
}
else if (n != 0) { //データが届いたら
for (int i = 0; i < n; i++) data[i] = buf[i];
//AddTempLog("データを受信しました size=%d", n);
return n;
}
return COMM_LIB_NO_ERROR;
}
int UDP_COMM::Send(const TCHAR *data, const int size) {
if (sendto(sock, data, size, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
WSA_ERR("データの送信に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
else {
//AddTempLog("データを送信しました size=%d",size);
}
return COMM_LIB_NO_ERROR;
}
int UDP_COMM::End() {
closesocket(sock);
memset(&sock, 0, sizeof(sock));
return COMM_LIB_NO_ERROR;
}
win10 vC++でwinsock2の通信が成功しません
ローカルホストでは繋がるのですが、外部PCから接続しようとするとエラー(10060)を返されます
2週間ぐらい調べたり改造したりしているのですが、一向に状況は変わらないので推測される原因だけでも教えてほしいです
ファイアーウォールやウイルスセキュリティーは切ってからテストしています
クラスにまとめたものですが、使い方は関数名の通り上から順に呼び出すだけです
class SERVERとclass CLIENTで通信したいです
class UDP_COMMはスルーしてくださいm(__)m
[code]
//ヘッダ
#ifndef DEF_COMMLIB //二重include防止
#define DEF_COMMLIB
#define _WINSOCK_DEPRECATED_NO_WARNINGS //警告を消す
#pragma comment(lib, "ws2_32.lib") //通信関係のライブラリを読み込む
#include <winsock2.h> //通信ヘッダ
#include <ws2tcpip.h>
#include <tchar.h> //_Tの定義
//エラー
#define COMM_LIB_NO_ERROR 0 //正常に終了
#define COMM_LIB_ERROR -1 //異常終了
//初期化モード
#define SERVER_PROGRAM 0 //サーバープログラムモードで初期化する
#define CLIENT_PROGRAM 1 //クライアントプログラムモードで初期化する
//使用するポート番号
#define PORT 25420 //使用するポート番号
#define SOCKET_MAX 20 //同時に通信できる最大数
//#define MAX_DATA_SIZE 262144 //データ通信で使用できる最大サイズ
#define MAX_DATA_SIZE 65536 //データ通信で使用できる最大サイズ
#define WSA_ERR(str, ...) WSAErrorLog(str, _T(__FUNCTION__), __LINE__, __VA_ARGS__) //エラー時のマクロ
#define CALL_ONCE(src) {static bool call_flag = false; if(!call_flag) {src;} call_flag = true;} //一度だけ処理する時のマクロ
//winsockを初期化する(エラーで終了必須
int CommLibInit();
//winsockの終了処理を行う
int CommLibEnd();
//チャットログを書き出す
void AddChatLog(const char *LogMessage, ...);
//サーバー用ログを書き出す
void AddServerLog(const char *LogMessage, ...);
//一時的にログを表示する
void AddTempLog(const char *LogMessage, ...);
//エラー情報をファイルに書き出す
void WSAErrorLog(const char *errorMessage, LPCTSTR lpszFuncName, int lineN, ...);
class SERVER {
SOCKET sock_lobby; //通信要請を受け取るソケット
sockaddr_in addr; //ロビーのアドレス情報
SOCKET sock; //クライアントとの通信を行うソケット
sockaddr_in client_addr; //クライアント情報
SOCKET udp_sock;
sockaddr_in udp_addr; //ロビーのアドレス情報
hostent *host; //ホストの情報(IP取得用)
public:
//コンストラクタ
SERVER();
//サーバーのスタートアップ処理
int Start();
//クライアントからの通信を受け取る
int Accept();
//データを送信する
int Send(const TCHAR *data, const int size);
//データを受信する
int Recv(char data[]);
//ホストネームを取得する
int GetHostName(char name[]);
};
class CLIENT {
struct sockaddr_in server_addr;
SOCKET sock;
public:
//コンストラクタ
CLIENT();
//データをサーバーに送信する
int Send(const TCHAR *data, int size);
//データをサーバーから受信する
int Recv(char data[]);
};
class UDP_COMM {
SOCKET sock; //通信を行うソケット
sockaddr_in addr; //アドレス情報
public:
//UDP通信のスタートアップ処理(0=サーバー
int Start(int program_kind);
//データを送信する
int Send(const TCHAR *data, const int size);
//データを受信する
int Recv(char data[]);
//UDP通信を終了する
int End();
};
#endif
[/code]
[code]
//.cpp
#define LOG_PRINTF_S true //コンソールアプリケーション画面出力を行うか
#include "CommLib.h"
#include <stdio.h>
#include <iostream>
#define CHAR_ADD_KEY (31) //文字列を保護するキー
#if _DEBUG
static char host_name[64] = { //ホストサーバーのIPアドレス
'l' - CHAR_ADD_KEY,
'o' - CHAR_ADD_KEY,
'c' - CHAR_ADD_KEY,
'a' - CHAR_ADD_KEY,
'l' - CHAR_ADD_KEY,
'h' - CHAR_ADD_KEY,
'o' - CHAR_ADD_KEY,
's' - CHAR_ADD_KEY,
't' - CHAR_ADD_KEY,
0x00
};//localhost
#else
static char host_name[64] = { //ホストサーバーのIPアドレス
'i' - CHAR_ADD_KEY,
'p' - CHAR_ADD_KEY,
0x00
};
#endif
//チャットログを書き出す
void AddChatLog(const char *LogMessage, ...) {
va_list VaList;
char String[1024]; //変換後の文字列を格納する配列
va_start(VaList, LogMessage); //va_list の準備( 第二引数は引数『...』の直前の引数を記述します )
vsprintf_s(String, sizeof(String), LogMessage, VaList); //書式文字列 + 可変長引数リストから書式文字列に従って文字配列に変換
va_end(VaList); //va_list の後始末
char date[128];
SYSTEMTIME ddata; //日付情報の変数を定義
GetLocalTime(&ddata); //日付情報取得
sprintf_s(date, "%04d/%02d/%02d %02d:%02d (%02d)", ddata.wYear, ddata.wMonth, ddata.wDay, ddata.wHour, ddata.wMinute, ddata.wSecond); //日付情報を文字列にする
char message[1024];
sprintf_s(message, "%s:%s\n", date, String); //全ての文字列を統合する
FILE *fp;
errno_t err;
err = fopen_s(&fp, "GameChat.log", "a"); //書き込みでログファイルを開く
if (err != NULL) return; //ファイルを開いたときにエラーが返ってきたら戻る
fprintf_s(fp, message); //エラーメッセージを出力する
fclose(fp); //ファイルを閉じる
}
//サーバー用ログを書き出す
void AddServerLog(const char *LogMessage, ...) {
va_list VaList;
char String[1024]; //変換後の文字列を格納する配列
va_start(VaList, LogMessage); //va_list の準備( 第二引数は引数『...』の直前の引数を記述します )
vsprintf_s(String, sizeof(String), LogMessage, VaList); //書式文字列 + 可変長引数リストから書式文字列に従って文字配列に変換
va_end(VaList); //va_list の後始末
char date[128];
SYSTEMTIME ddata; //日付情報の変数を定義
GetLocalTime(&ddata); //日付情報取得
sprintf_s(date, "%04d/%02d/%02d %02d:%02d:%02d ", ddata.wYear, ddata.wMonth, ddata.wDay, ddata.wHour, ddata.wMinute, ddata.wSecond); //日付情報を文字列にする
char message[1024];
sprintf_s(message, "%s %s\n", date, String); //全ての文字列を統合する
FILE *fp;
errno_t err;
static int a = 0;
if (a == 2) {
err = fopen_s(&fp, "Server.log", "w");
fclose(fp);
a = 1;
}
err = fopen_s(&fp, "Server.log", "a"); //書き込みでログファイルを開く
if (err != NULL) return; //ファイルを開いたときにエラーが返ってきたら戻る
fprintf_s(fp, message); //エラーメッセージを出力する
fclose(fp); //ファイルを閉じる
#if LOG_PRINTF_S //もし、コンソールアプリケーションなら
printf_s(message); //メッセージをウインドウに書き出す
#endif
}
//winsockを初期化する
int CommLibInit() {
WSADATA wsaData; //初期化情報を格納する構造体
int err = WSAStartup(MAKEWORD(2, 3), &wsaData);
if (err != 0) {
WSA_ERR("winsockの初期化に失敗しました");
switch (err) {
case WSASYSNOTREADY:
WSA_ERR("WSASYSNOTREADY ネットワークサブシステムがネットワークへの接続を準備できていません");
break;
case WSAVERNOTSUPPORTED:
WSA_ERR("WSAVERNOTSUPPORTED 要求されたwinsockのバージョンがサポートされていません");
break;
case WSAEINPROGRESS:
WSA_ERR("WSAEINPROGRESS ブロッキング操作の実行中であるか、 またはサービスプロバイダがコールバック関数を処理しています");
break;
case WSAEPROCLIM:
WSA_ERR("WSAEPROCLIM winsockが処理できる最大プロセス数に達しました");
break;
case WSAEFAULT:
WSA_ERR("WSAEFAULT 第二引数である lpWSAData は有効なポインタではありません");
break;
default:
WSA_ERR("UNKNOWN 不明なエラーです");
break;
}
return COMM_LIB_ERROR;
}
for (int i = 0; host_name[i] != 0x00; i++) host_name[i] += CHAR_ADD_KEY; //使う前に文字列を複合化する
AddTempLog("通信エンジンの初期化に成功しました");
return COMM_LIB_NO_ERROR;
}
//winsockを終了する
int CommLibEnd() {
WSACleanup();
return 0;
}
//画面にログを表示する
void AddTempLog(const char *LogMessage, ...) {
va_list VaList;
char String[1024]; //変換後の文字列を格納する配列
va_start(VaList, LogMessage); //va_list の準備( 第二引数は引数『...』の直前の引数を記述します )
vsprintf_s(String, sizeof(String), LogMessage, VaList); //書式文字列 + 可変長引数リストから書式文字列に従って文字配列に変換
va_end(VaList); //va_list の後始末
char date[128];
SYSTEMTIME ddata; //日付情報の変数を定義
GetLocalTime(&ddata); //日付情報取得
sprintf_s(date, "%04d/%02d/%02d %02d:%02d:%02d", ddata.wYear, ddata.wMonth, ddata.wDay, ddata.wHour, ddata.wMinute, ddata.wSecond); //日付情報を文字列にする
char message[MAX_DATA_SIZE + MAX_DATA_SIZE];
sprintf_s(message, "%s %s\n", date, String); //全ての文字列を統合する
#if LOG_PRINTF_S //もし、コンソールアプリケーションなら
printf_s(message); //メッセージをウインドウに書き出す
#endif
}
//エラー情報をファイルに書き出す
void WSAErrorLog(const char *errorMessage, LPCTSTR lpszFuncName, int lineN, ...) {
va_list VaList;
char ErrMessage[2048]; //変換後の文字列を格納する配列
va_start(VaList, lineN); //va_list の準備( 第二引数は引数『...』の直前の引数を記述します )
vsprintf_s(ErrMessage, sizeof(ErrMessage), errorMessage, VaList); //書式文字列 + 可変長引数リストから書式文字列に従って文字配列に変換
va_end(VaList); //va_list の後始末
static int old_line[3] = { 0 };
char funcName[512];
char date[512];
char message[1024];
sprintf_s(funcName, "%s(%04d)", lpszFuncName, lineN); //関数の名前を文字列にする
SYSTEMTIME ddata; //日付情報の変数を定義
GetLocalTime(&ddata); //日付情報取得
sprintf_s(date, "%04d/%02d/%02d %02d:%02d", ddata.wYear, ddata.wMonth, ddata.wDay, ddata.wHour, ddata.wMinute); //日付情報を文字列にする
AddTempLog(ErrMessage); //クライアントは一時ログにメッセージを追加
sprintf_s(message, "%s - % -30s「%s」\n", date, funcName, ErrMessage); //全ての情報を一つの文字列にする
if (old_line[0] != lineN) old_line[0] = lineN; //前の行と違う行なら現在の行を保存する
else if (old_line[1] != lineN) old_line[1] = lineN; //2連続で同じ行なら現在の行を1に保存する
else if (old_line[2] != lineN) old_line[2] = lineN; //3連続で同じ行なら現在の行を2に保存する
else if (old_line[3] != lineN) {
old_line[3] = lineN; //4連続で同じ行なら現在の行を3に保存する
sprintf_s(message, "%s - % -30s「%s」\n", date, "ERRsystem(0)", "持続的エラーの可能性が非常に高いためログの発行を一時停止します");
printf_s(message); //エラーメッセージを画面に出力する
FILE *fp;
errno_t err;
err = fopen_s(&fp, "error_ws2.log", "a"); //書き込みでログファイルを開く
if (err != NULL) return; //ファイルを開いたときにエラーが返ってきたら戻る
fprintf_s(fp, message); //エラーメッセージを出力する
fclose(fp); //ファイルを閉じる
return;
}
else if (old_line[3] == lineN) { //4連続以降ならエラーを出力せずに戻る
return;
}
FILE *fp;
errno_t err;
err = fopen_s(&fp, "error_ws2.log", "a"); //書き込みでログファイルを開く
if (err != NULL) return; //ファイルを開いたときにエラーが返ってきたら戻る
fprintf_s(fp, message); //エラーメッセージを出力する
fclose(fp); //ファイルを閉じる
}
void dump(void *vp, int n) {
unsigned char *p = (unsigned char *)vp;
for (int i = 0; i < n; i++) printf(" %02x", p[i]);
putchar('\n');
}
SERVER::SERVER() {
}
int SERVER::Start() {
sock_lobby = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //ソケットの作成
if (sock_lobby == INVALID_SOCKET) {
WSA_ERR("ソケットの作成に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
else AddTempLog("ロビーソケットを作成しました");
bool yes = true; //1が登録されたポインタを作成
setsockopt(sock_lobby, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes)); //TIME_WAIT状態への対策(ソケットオプションの設定
addr.sin_family = AF_INET; //ソケットの設定をする
addr.sin_port = htons(PORT); //使用するポート
addr.sin_addr.s_addr = htonl(INADDR_ANY); //接続するアドレス
if (bind(sock_lobby, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
WSA_ERR("ローカルアドレスとソケットの関連付けに失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
else AddTempLog("ローカルアドレスとソケットの関連付けが完了しました");
u_long val = 1; //ノンブロッキング(非同期通信)に設定する
ioctlsocket(sock_lobby, FIONBIO, &val);
if (listen(sock_lobby, 5) != 0) { //TCPクライアントからの接続要求を待てる状態にする
WSA_ERR("ソケットをリッスン状態にできませんでした(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
else AddTempLog("ソケットを通信受付状態にしました");
AddTempLog("ロビーソケットの初期化が正常に終了しました port=%d", PORT);
return COMM_LIB_NO_ERROR;
}
int SERVER::Accept() {
int len = sizeof(client_addr); //TCPクライアントからの接続要求を受け付ける
sock = accept(sock_lobby, (struct sockaddr *)&client_addr, &len);
if (sock == INVALID_SOCKET) { //ソケットの値が正常ではない
if (WSAGetLastError() != WSAEWOULDBLOCK) { //要請がない場合ではなかったら
WSA_ERR("クライアントからの接続に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
}
else { //接続要請があれば
host = gethostbyaddr((const char *)&client_addr.sin_addr, sizeof(addr.sin_addr), AF_INET);
if (host == NULL) { //失敗したらエラーを表示して終了
WSA_ERR("ホスト情報の取得に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
//AddTempLog("accepted connection from [%s], ip=%s, port=%d", host->h_name, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
int connection = GetPrivateProfileInt("HOST_NAME", host->h_name, 0, ".\\host_list.txt");
if(!connection) WritePrivateProfileString("HOST_NAME", host->h_name, "1", ".\\host_list.txt"); //ホスト情報をデータベースに登録する
return 1;
}
return COMM_LIB_NO_ERROR;
}
int SERVER::Recv(char data[]) {
char buf[MAX_DATA_SIZE];
memset(buf, 0, sizeof(buf));
int n = recv(sock, buf, sizeof(char)*MAX_DATA_SIZE, 0);
if (n < 0) {
if (WSAGetLastError() != WSAEWOULDBLOCK) { //何らかのエラーが発生している
WSA_ERR("データの受信に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
}
else if (n != 0) { //データが届いたら
//AddTempLog("データを受信しました-%d「%.64s」", n, buf);
memcpy(data, buf, n);
return n;
}
return COMM_LIB_NO_ERROR;
}
int SERVER::Send(const TCHAR *data, int size) {
if (send(sock, data, /*strlen(data)*/size, 0) < 1) {
WSA_ERR("データの送信に失敗しました(%d)", WSAGetLastError());
closesocket(sock); //TCPセッションの終了
memset(&sock, 0, sizeof(sock));
return COMM_LIB_ERROR;
}
//else AddTempLog("データを送信しました-%d「%.64s」",size, data);
closesocket(sock); //TCPセッションの終了
memset(&sock, 0, sizeof(sock));
return COMM_LIB_NO_ERROR;
}
int SERVER::GetHostName(char name[]) {
memcpy(name, host->h_name, strlen(host->h_name));
return 0;
}
CLIENT::CLIENT() {
}
int CLIENT::Recv(char data[]) {
char buf[MAX_DATA_SIZE];
memset(buf, 0, sizeof(buf));
int n = recv(sock, buf, sizeof(buf), 0);
if (n < 0) {
if (WSAGetLastError() != WSAEWOULDBLOCK) { //何らかのエラーが発生している
WSA_ERR("データの受信に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
}
else if (n != 0) { //データが届いたら
//AddTempLog("データを受信しました-%d「%.64s」", n, buf);
closesocket(sock); //TCPセッションの終了
memset(&sock, 0, sizeof(sock));
memcpy(data, buf, n);
return n;
}
return COMM_LIB_NO_ERROR;
}
int CLIENT::Send(const TCHAR *data, int size) {
sock = socket(AF_INET, SOCK_STREAM, 0); //ソケットの作成
if (sock == INVALID_SOCKET) {
WSA_ERR("ソケットの作成に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
//else AddTempLog("ソケットを作成しました");
server_addr.sin_family = AF_INET; //ソケットの設定をする
server_addr.sin_port = htons(PORT); //使用するポート
hostent *host = gethostbyname(host_name); //リリース時にはホストIPを使う
if (host == NULL) {
WSA_ERR("サーバー情報がありません");
return COMM_LIB_ERROR;
}
struct in_addr in;
memcpy(&in, host->h_addr, host->h_length);
//std::cout << inet_ntoa(in) << std::endl;
//server_addr.sin_addr.S_un.S_addr = *(BYTE*)host->h_addr_list[0]; //サーバーIPアドレスを取得する
//AddTempLog("サーバー情報の取得に成功しました");
server_addr.sin_addr.s_addr = inet_addr(inet_ntoa(in));
if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0) { //サーバに接続を試みる
WSA_ERR("サーバーに接続できませんでした(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
//else AddTempLog("サーバーに接続しました");
if (send(sock, data, size, 0) < 1) {
WSA_ERR("データの送信に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
//else AddTempLog("データを送信しました-%d「%s」", size, data);
return COMM_LIB_NO_ERROR;
}
int UDP_COMM::Start(int program_kind) {
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == INVALID_SOCKET) {
WSA_ERR("UDPソケットの生成に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
else AddTempLog("UDPソケットの生成に成功しました");
if (!program_kind) {
//ファイルディスクリプターで参照されるソケットにオプションの設定
//SOL_SOCKET : ソケット API 層
//SO_REUSEADDR : bindが失敗するのを回避
int on = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) == SOCKET_ERROR)
{
WSA_ERR("オプションの設定に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
}
memset(&addr, 0, sizeof(addr)); //アドレスの初期化
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
if (!program_kind) {
addr.sin_addr.S_un.S_addr = INADDR_ANY; //サーバーの設定
addr.sin_addr.s_addr = htonl(INADDR_ANY); //IPアドレスを格納したin_addr構造体
}
else { //クライアントの設定
//PHOSTENT phostent = gethostbyname(host_name);
//if (phostent == NULL) {
// WSA_ERR("サーバー情報がありません");
// return COMM_LIB_ERROR;
//}
//memcpy(&addr, phostent->h_addr_list[0], 4);
hostent *host = gethostbyname(host_name); //リリース時にはホストIPを使う
if (host == NULL) {
WSA_ERR("サーバー情報がありません");
return COMM_LIB_ERROR;
}
addr.sin_addr.S_un.S_addr = *(unsigned int *)host->h_addr_list[0]; //サーバーIPアドレスを取得する
AddTempLog("サーバー情報の取得に成功しました");
}
if (!program_kind) { //サーバーの場合は情報が来るのを待つ状態にする
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
WSA_ERR("ローカルアドレスとUDPソケットの関連付けに失敗しました(%d)", WSAGetLastError());
closesocket(sock);
return COMM_LIB_ERROR;
}
else AddTempLog("ローカルアドレスとUDPソケットの関連付けが完了しました");
u_long val = 1; //ノンブロッキング(非同期通信)に設定する
ioctlsocket(sock, FIONBIO, &val);
}
AddTempLog("UDPソケットの初期化が正常に終了しました port=%d", PORT);
return COMM_LIB_NO_ERROR;
}
int UDP_COMM::Recv(char data[]) {
char buf[MAX_DATA_SIZE];
memset(buf, '\0', sizeof(buf));
int n = recv(sock, buf, sizeof(buf), 0);
if (n < 0) {
if (WSAGetLastError() != WSAEWOULDBLOCK) { //何らかのエラーが発生している
WSA_ERR("データの受信に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
}
else if (n != 0) { //データが届いたら
for (int i = 0; i < n; i++) data[i] = buf[i];
//AddTempLog("データを受信しました size=%d", n);
return n;
}
return COMM_LIB_NO_ERROR;
}
int UDP_COMM::Send(const TCHAR *data, const int size) {
if (sendto(sock, data, size, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
WSA_ERR("データの送信に失敗しました(%d)", WSAGetLastError());
return COMM_LIB_ERROR;
}
else {
//AddTempLog("データを送信しました size=%d",size);
}
return COMM_LIB_NO_ERROR;
}
int UDP_COMM::End() {
closesocket(sock);
memset(&sock, 0, sizeof(sock));
return COMM_LIB_NO_ERROR;
}
[/code]