結果だけでなく過程も見てください

たい焼きさんの日々の奮闘を綴る日記です。

ちょっとしたTCP/IP通信でいろいろテストしたいときのコードテンプレ(Windows/Linux両方コピペだけでビルド可能!)

タイトル通りです。

  • テスト用です
  • コピって適当に改造して使いましょう
  • エラー処理は無いに等しいです
  • アドレスはIPv4しか使えません
  • 一部機能が未実装です
  • 2時間くらいでとりあえず上げたものです。徐々に修正していきます(これいつものやつや!)。

ビルド

Windows

Visual Studio Community 2015で動作確認済みです。
追加のライブラリとして「ws2_32.lib」を追加しといてください。
(プロジェクトのプロパティ→リンカー→入力→追加の依存ファイル)
あとは普通にビルドできます。

Linux

gcc 4.8.5で動作確認済みです。

gcc ./main.cpp -o main -lstdc++ -lpthread

使用方法

サーバー
実行ファイル server <リッスンするIPアドレス> <リッスンするポート>

■例
./main server 192.168.1.10 19876
クライアント
実行ファイル client <接続先IPアドレス> <接続先ポート> <接続元IPアドレス> <接続元ポート> <サーバーに送るメッセージ>

※接続元

■例
./main client 192.168.1.10 19876 0.0.0.0 0 ahaha

ソースコード

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>

#ifdef WIN32
    #include <WinSock2.h>
    #include <Windows.h>
    #include <process.h>
#else
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <pthread.h>
#endif


#ifdef WIN32
    #define HERROR                          WSAGetLastError()
    #define SAFE_SOCK_CLOSE(sock)           if((sock) != INVALID_SOCKET){ closesocket((sock)); (sock)=INVALID_SOCKET; }
    #define SOCK_TYPE                       SOCKET
    #define SOCKLEN_TYPE                    int
    #define SLEEP_SEC(sec)                  Sleep((sec) * 1000)
    #define SET_SOCKADDR_IPV4(addr, value)  (addr).sin_addr.S_un.S_addr = (value)
#else
    #define HERROR                          h_errno
    #define INVALID_SOCKET                  (-1)
    #define SAFE_SOCK_CLOSE(sock)           if((sock) != INVALID_SOCKET){ close((sock)); (sock)=INVALID_SOCKET; }
    #define SOCK_TYPE                       int
    #define SOCKLEN_TYPE                    socklen_t
    #define SLEEP_SEC(sec)                  sleep((sec))
    #define SET_SOCKADDR_IPV4(addr, value)  (addr).sin_addr.s_addr = (value)
#endif

#define SAFE_DELETE(p)  if(p){ delete(p); (p)=NULL; }


const char* errno_str()
{
    switch ( errno )
    {
        case 1:   return "EPERM";
        case 2:   return "ENOENT";
        case 3:   return "ESRCH";
        case 4:   return "EINTR";
        case 5:   return "EIO";
        case 6:   return "ENXIO";
        case 7:   return "E2BIG";
        case 8:   return "ENOEXEC";
        case 9:   return "EBADF";
        case 10:  return "ECHILD";
        case 11:  return "EAGAIN or EWOULDBLOCK";
        case 12:  return "ENOMEM";
        case 13:  return "EACCES";
        case 14:  return "EFAULT";
        case 15:  return "ENOTBLK";
        case 16:  return "EBUSY";
        case 17:  return "EEXIST";
        case 18:  return "EXDEV";
        case 19:  return "ENODEV";
        case 20:  return "ENOTDIR";
        case 21:  return "EISDIR";
        case 22:  return "EINVAL";
        case 23:  return "ENFILE";
        case 24:  return "EMFILE";
        case 25:  return "ENOTTY";
        case 26:  return "ETXTBSY";
        case 27:  return "EFBIG";
        case 28:  return "ENOSPC";
        case 29:  return "ESPIPE";
        case 30:  return "EROFS";
        case 31:  return "EMLINK";
        case 32:  return "EPIPE";
        case 33:  return "EDOM";
        case 34:  return "ERANGE";
        case 35:  return "EDEADLK or EDEADLOCK";
        case 36:  return "ENAMETOOLONG";
        case 37:  return "ENOLCK";
        case 38:  return "ENOSYS";
        case 39:  return "ENOTEMPTY";
        case 40:  return "ELOOP";
        case 42:  return "ENOMSG";
        case 43:  return "EIDRM";
        case 44:  return "ECHRNG";
        case 45:  return "EL2NSYNC";
        case 46:  return "EL3HLT";
        case 47:  return "EL3RST";
        case 49:  return "EUNATCH";
        case 51:  return "EL2HLT";
        case 52:  return "EBADE";
        case 53:  return "EBADR";
        case 54:  return "EXFULL";
        case 56:  return "EBADRQC";
        case 57:  return "EBADSLT";
        case 60:  return "ENOSTR";
        case 61:  return "ENODATA";
        case 62:  return "ETIME";
        case 63:  return "ENOSR";
        case 64:  return "ENONET";
        case 65:  return "ENOPKG";
        case 66:  return "EREMOTE";
        case 67:  return "ENOLINK";
        case 70:  return "ECOMM";
        case 71:  return "EPROTO";
        case 72:  return "EMULTIHOP";
        case 74:  return "EBADMSG";
        case 75:  return "EOVERFLOW";
        case 76:  return "ENOTUNIQ";
        case 77:  return "EBADFD";
        case 78:  return "EREMCHG";
        case 79:  return "ELIBACC";
        case 80:  return "ELIBBAD";
        case 81:  return "ELIBSCN";
        case 82:  return "ELIBMAX";
        case 83:  return "ELIBEXEC";
        case 84:  return "EILSEQ";
        case 85:  return "ERESTART";
        case 86:  return "ESTRPIPE";
        case 87:  return "EUSERS";
        case 88:  return "ENOTSOCK";
        case 89:  return "EDESTADDRREQ";
        case 90:  return "EMSGSIZE";
        case 91:  return "EPROTOTYPE";
        case 92:  return "ENOPROTOOPT";
        case 93:  return "EPROTONOSUPPORT";
        case 94:  return "ESOCKTNOSUPPORT";
        case 95:  return "ENOTSUP or EOPNOTSUPP";
        case 96:  return "EPFNOSUPPORT";
        case 97:  return "EAFNOSUPPORT";
        case 98:  return "EADDRINUSE";
        case 99:  return "EADDRNOTAVAIL";
        case 100: return "ENETDOWN";
        case 101: return "ENETUNREACH";
        case 102: return "ENETRESET";
        case 103: return "ECONNABORTED";
        case 104: return "ECONNRESET";
        case 105: return "ENOBUFS";
        case 106: return "EISCONN";
        case 107: return "ENOTCONN";
        case 108: return "ESHUTDOWN";
        case 110: return "ETIMEDOUT";
        case 111: return "ECONNREFUSED";
        case 112: return "EHOSTDOWN";
        case 113: return "EHOSTUNREACH";
        case 114: return "EALREADY";
        case 115: return "EINPROGRESS";
        case 116: return "ESTALE";
        case 117: return "EUCLEAN";
        case 120: return "EISNAM";
        case 121: return "EREMOTEIO";
        case 122: return "EDQUOT";
        case 123: return "ENOMEDIUM";
        case 124: return "EMEDIUMTYPE";
        case 125: return "ECANCELED";
        case 126: return "ENOKEY";
        case 127: return "EKEYEXPIRED";
        case 128: return "EKEYREVOKED";
        case 129: return "EKEYREJECTED";
        default:  return "UNKNOWN";
    }
}


const char* getLocalTimeStr( time_t  curTime, 
                             char*   pszResultTime )
{
    struct tm* pt = localtime( &curTime );

    sprintf( pszResultTime, "%02d/%02d/%02d %02d:%02d:%02d", pt->tm_year + 1900, pt->tm_mon + 1, pt->tm_mday, pt->tm_hour, pt->tm_min, pt->tm_sec );

    return pszResultTime;
}


class NetworkInfo
{
public:

    ~NetworkInfo()
    {

#ifdef WIN32
        if ( bWSASetup )
        {
            WSACleanup();
            bWSASetup = false;
        }
#endif

        SAFE_SOCK_CLOSE( sock0 );
    }

#ifdef WIN32
    WSADATA             wsaData;
    bool                bWSASetup;
#endif

    /* listening socket */
    SOCK_TYPE           sock0;

    /* サーバーならリッスンするアドレス情報     */
    /* クライアントなら送信先アドレス情報とする */
    struct sockaddr_in  addr;

    /* オプション。クライアントのときだけ使われる。クライアント側アドレスとポート。 */
    struct sockaddr_in  client_addr_forsend;


    NetworkInfo() :
#ifdef WIN32
        wsaData(),
        bWSASetup( false ),
#endif
        sock0( INVALID_SOCKET ),
        addr(),
        client_addr_forsend()
    {
    }

};


/* サーバーかクライアントの種別列挙子 */
enum NodeType
{
    eUnknown = 0,
    eServer,
    eClient
};


/* 受信関数で必要な情報 */
struct RecvInfo_t
{
    /* 受信処理で使用するソケット */
    SOCK_TYPE  recvSock_;

    RecvInfo_t( SOCK_TYPE recvSock ):
        recvSock_( recvSock )
    {
    }
};


/* 受信関数 */
#ifdef WIN32
  unsigned __stdcall recvThread( void *p )
#else
  void* recvThread( void *p )
#endif
{
    RecvInfo_t* pRecvInfo = (RecvInfo_t *)p;

    char  inbuf[2048];   /* 受信バッファ     */
    char  atimeWk[64];   /* 時刻表示ワーク用 */

    memset( inbuf, 0, sizeof( inbuf ) );
    memset( atimeWk, 0, sizeof( atimeWk ) );

    /* 情報を取得してメモリ解放しておく */
    SOCK_TYPE sock = pRecvInfo->recvSock_;
    SAFE_DELETE( pRecvInfo );

    /* 受信処理。同期とする。出力結果が混ざって表示されるが気にしない */
    recv( sock, inbuf, sizeof( inbuf ), 0 );
    printf( "%s Received Message:%s\n", getLocalTimeStr(time(NULL), atimeWk), inbuf );

    SAFE_SOCK_CLOSE( sock );

#ifdef WIN32
    return 0;
#else
    pthread_exit( NULL );
#endif
}


/*
* me <NodeType> <listening addr> <listening port>
*      or
* me <NodeType> <dest addr> <dest port> <src addr> <src port> <msg>
*
* <NodeType> "server" or "client"
*
* Only IPv4.
*/
int main( int argc, char* argv[] )
{
    if ( argc <= 1 )
    {
        fprintf( stderr, "Bad args num.\n" );
        return 1;
    }

    NodeType eNodeType = eUnknown;

    if ( strcmp( argv[1], "server" ) == 0 )
    {
        eNodeType = eServer;

        if ( argc != 4 )
        {
            fprintf( stderr, "Bad args num(Server)(%d).\n", argc );
            return 1;
        }
    }
    else if ( strcmp( argv[1], "client" ) == 0 )
    {
        eNodeType = eClient;

        if ( argc != 7 )
        {
            fprintf( stderr, "Bad args num(Client)(%d).\n", argc );
            return 1;
        }
    }
    else
    {
        fprintf( stderr, "Bad NodeType.\n" );
        return 2;
    }

    int          nFuncRet = 0;   /* Function return code */
    NetworkInfo  netInfo;
    char         atimeWk[64];

    memset( atimeWk, 0, sizeof(atimeWk) );

    
#ifdef WIN32
    nFuncRet = WSAStartup( MAKEWORD( 2, 0 ), &(netInfo.wsaData) );
    netInfo.bWSASetup = true;
    if ( nFuncRet != 0 )
    {
        fprintf( stderr, "%s WSAStartup error(%d)(h_errno:%d)(errno:%d(%s))\n", getLocalTimeStr(time(NULL), atimeWk), nFuncRet, HERROR, errno, errno_str() );
        return 3;
    }
#endif


    if ( eNodeType == eServer )
    {
        /* argv */
        /* [1]:ノード種別       */
        /* [2]:リッスンアドレス */
        /* [3]:リッスンポート   */

        const char* pszAddr    = argv[2];
        const char* pszPortNum = argv[3];


        /* リッスンソケットの作成 */
        netInfo.sock0 = socket( AF_INET, SOCK_STREAM, 0 );
        if ( netInfo.sock0 == INVALID_SOCKET )
        {
            fprintf( stderr, "%s listen socket error(h_errno:%d)(errno:%d(%s))\n", getLocalTimeStr(time(NULL), atimeWk), HERROR, errno, errno_str() );
            return 4;
        }

        /* バインドリッスンするアドレスとポート設定 */
        netInfo.addr.sin_family = AF_INET;
        netInfo.addr.sin_port   = htons( atoi( pszPortNum ) );    /* リッスンポート TODO:引数を信じろ! */
        SET_SOCKADDR_IPV4( netInfo.addr, inet_addr( pszAddr ) );  /* バインドアドレス TODO:引数を信じろ! */


        /* サーバーの場合はポート再利用可能 */
        {
            int nYes = 1;
            setsockopt( netInfo.sock0, SOL_SOCKET, SO_REUSEADDR, (const char *)&nYes, sizeof( nYes ) );
        }


        nFuncRet = bind( netInfo.sock0, (struct sockaddr *)&(netInfo.addr), sizeof( netInfo.addr ) );
        if ( nFuncRet == -1 )
        {
            fprintf( stderr, "%s bind(recv) error(h_errno:%d)(errno:%d(%s))\n", getLocalTimeStr( time( NULL ), atimeWk ), HERROR, errno, errno_str() );
            return 8;
        }


        nFuncRet = listen( netInfo.sock0, 5 );  /* backlogは5 */
        if ( nFuncRet == -1 )
        {
            fprintf( stderr, "%s listen error(h_errno:%d)(errno:%d(%s))\n", getLocalTimeStr( time( NULL ), atimeWk ), HERROR, errno, errno_str() );
            return 9;
        }


        /* recv loop */
        while (1)
        {
            int                 sock        = INVALID_SOCKET;
            struct sockaddr_in  client      = {0};
            SOCKLEN_TYPE        nClientLen  = sizeof( client );

            sock = accept( netInfo.sock0, (struct sockaddr *)&client, &nClientLen );
            if ( sock == INVALID_SOCKET )
            {
                fprintf( stderr, "%s accept error(h_errno:%d)(errno:%d(%s))\n", getLocalTimeStr( time( NULL ), atimeWk ), HERROR, errno, errno_str() );
                continue;
            }

            /* 処理用スレッドを起動 */
            RecvInfo_t* pRecvInfo = NULL;
            try
            {
                pRecvInfo = new RecvInfo_t( sock );
            }
            catch (...)
            {
                /* bad memory alloc */
                SAFE_SOCK_CLOSE( sock );
                continue;
            }

#ifdef WIN32
            unsigned int thID = 0;

            HANDLE hThread = (HANDLE)_beginthreadex( NULL,
                                                     0,
                                                     recvThread,
                                                     pRecvInfo,
                                                     0  /*CREATE_SUSPENDED*/,
                                                     &thID );
            if ( hThread == 0 )
            {
                fprintf( stderr, "%s _beginthreadex error(h_errno:%d)(errno:%d(%s))\n", getLocalTimeStr( time( NULL ), atimeWk ), HERROR, errno, errno_str() );

                SAFE_SOCK_CLOSE( sock );
                continue;
            }

            /* Windowsではデフォルトでpthread_detach相当の設定になっている(たぶん) */
#else
            pthread_t th = 0;

            nFuncRet = pthread_create( &th, NULL, recvThread, (void *)pRecvInfo );
            if (nFuncRet != 0)
            {
                fprintf( stderr, "%s pthread_create error(h_errno:%d)(errno:%d(%s))\n", getLocalTimeStr( time( NULL ), atimeWk ), HERROR, errno, errno_str() );

                SAFE_SOCK_CLOSE( sock );
                continue;
            }

            (void)pthread_detach( th );
#endif
        }

    }
    else
    {
        /* argv */
        /* [1]:ノード種別       */
        /* [2]:接続先アドレス   */
        /* [3]:接続先ポート     */
        /* [4]:接続元アドレス   */
        /* [5]:接続元ポート     */
        /* [6]:メッセージ       */

        const char* pszDestAddr    = argv[2];
        const char* pszDestPortNum = argv[3];
        const char* pszSrcAddr     = argv[4];
        const char* pszSrcPortNum  = argv[5];
        const char* pszMessage     = argv[6];


        int   sock = 0;
        char  sendmsg[256];

        memset( sendmsg, 0, sizeof( sendmsg ) );

        /* 接続先アドレス情報 */
        netInfo.addr.sin_family = AF_INET;
        netInfo.addr.sin_port = htons( atoi(pszDestPortNum) );       /* TODO:引数を信じて! */
        SET_SOCKADDR_IPV4( netInfo.addr, inet_addr(pszDestAddr) );   /* TODO:引数を信じて! */


        /* 接続元アドレス情報 */
        netInfo.client_addr_forsend.sin_family = AF_INET;
        netInfo.client_addr_forsend.sin_port = htons( atoi( pszSrcPortNum ) );    /* TODO:引数を信じて! */
        SET_SOCKADDR_IPV4( netInfo.client_addr_forsend, inet_addr(pszSrcAddr) );  /* TODO:引数を信じて! */


        sock = socket( AF_INET, SOCK_STREAM, 0 );
        if (sock == INVALID_SOCKET)
        {
            fprintf( stderr, "%s socket(send) h_errno:%d errno:%d(%s)\n", getLocalTimeStr( time( NULL ), atimeWk ), HERROR, errno, errno_str() );

            return 10;
        }


        /* TODO:将来的にクライアントの場合のポート再利用可能は設定するかしないか設定できるようにしたい */
        {
            int nYes = 1;
            setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&nYes, sizeof( nYes ) );
        }


        /* TODO:将来的に接続元アドレスを必ずbindするかどうかはオプション化したい */
        nFuncRet = bind( sock, (struct sockaddr *)&(netInfo.client_addr_forsend), sizeof(netInfo.client_addr_forsend) );
        if (nFuncRet == -1)
        {
            fprintf( stderr, "%s bind(send) error(h_errno:%d)(errno:%d(%s))\n", getLocalTimeStr( time( NULL ), atimeWk ), HERROR, errno, errno_str() );

            return 11;
        }


        nFuncRet = connect( sock, (struct sockaddr *)&(netInfo.addr), sizeof( netInfo.addr ) );
        if ( nFuncRet == -1 )
        {
            fprintf( stderr, "%s connect error(h_errno:%d)(errno:%d(%s))\n", getLocalTimeStr( time( NULL ), atimeWk ), HERROR, errno, errno_str() );
            
            return 12;
        }


        memset( sendmsg, 0, sizeof(sendmsg) );
        strncpy( sendmsg, pszMessage, sizeof(sendmsg)-1 );

        (void)send( sock, sendmsg, (int)strlen( sendmsg ), 0 );

        SAFE_SOCK_CLOSE( sock );
    }


    return 0;
}

(旧)受信側 非マルチスレッド版

main.cpp 直

プライバシーポリシー お問い合わせ