TCP Socket通信学习

学习内容,参见《Windows网络编程》 第7章 Winsock基础

服务器/客户端连接模型见下图。

TCP Socket通信学习_第1张图片

服务端流程

  • 用socket或WSASocket创建一个套接字。
  • 用bind绑定套接字到本地IP地址(多网卡时,本地有多个IP)。
  • 用listen将套接字置为监听模式。
  • 通过accept或WSAAccept来接受客户连接。
  • 用send或WSASend发送信息,用recv或WSARecv接受信息。

客户端流程

  • 用socket或WSASocket创建一个套接字。
  • 解析服务器名。
  • 用connect或WSAConnect初始化一个连接。
  • 用send或WSASend发送信息,用recv或WSARecv接受信息。

使用windows的Winsock 2编程,创建win32控制台工程,进行工程配置。

  • 工程右键Properties->Configuration Properties->Linker->Input->Additional Dependencies中添加ws2_32.lib。
  • Demo代码采用Multi-Byte方式,设置Properties->Configuration Properties->General->Character Set为Use Multi-Byte Character Set。

服务端代码,TCPServer.cpp。

// TCPServer.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <WinSock2.h>
#include <stdio.h>
#include <stdlib.h>

#define DEFAULT_PORT 5150
#define DEFAULT_BUFFER 4096

int iPort = DEFAULT_PORT;//Port to listen for clients on
BOOL bInterface = FALSE;//Listen on the specified interface
BOOL bRecvOnly = FALSE;//Receive data only; don't echo back
TCHAR szAddress[128];//Interface to listen for clients on

//Print usage information and exit
void usage()
{
    printf("usage:server[-p:x][-i:IP][-o]\n\n");
    printf("         -p:x      Port number to listen on\n");
    printf("         -i:str     Interface to listen on\n");
    printf("         -o         Don't echo the data back\n\n");

    ExitProcess(1);
}

//Parse the command line arguments, and set some global flags
//to indicate what actions to perform
void ValidateArgs(int agrc, _TCHAR** argv)
{
    for (int i = 1; i < agrc; i++)
    {
        if ((argv[i][0] == _T('-')) || (argv[i][0] == _T('/')))
        {
            switch (tolower(argv[i][1]))
            {
            case _T('p'):
                iPort = _ttoi(&argv[i][3]);
                break;
            case _T('i'):
                bInterface = TRUE;
                if (_tcslen(argv[i]) > 3)
                {
                    _tcscpy_s(szAddress, &argv[i][3]);
                }
                break;
            case _T('o'):
                //只接受数据
                bRecvOnly = TRUE;
                break;
            default:
                usage();
                break;
            }
        }
    }
}

DWORD WINAPI ClientThread(LPVOID lpParam)
{
    SOCKET sock = (SOCKET)lpParam;
    char szBuff[DEFAULT_BUFFER];

    while (true)
    {
        //Perform a blocking recv() call
        int ret = recv(sock, szBuff, DEFAULT_BUFFER, 0);
        if (0 == ret)
        {
            break;
        }
        else if (SOCKET_ERROR == ret)
        {
            printf("recv() failed:%d\n", WSAGetLastError());
            break;
        }
        szBuff[ret] = '\0';
        printf("RECV:'%s'\n", szBuff);

        //if we selected to echo the data back, do it
        if (!bRecvOnly)
        {
            int nLeft = ret;
            int idx = 0;

            //make sure we write all the data
            while (nLeft > 0)
            {
                ret = send(sock, &szBuff[idx], nLeft, 0);
                if (0 == ret)
                {
                    break;
                }
                else if (SOCKET_ERROR == ret)
                {
                    printf("send() failed:%d\n", WSAGetLastError());
                    break;
                }
                nLeft -= ret;
                idx += ret;
            }
        }
    }

    return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
    ValidateArgs(argc, argv);

    WSADATA wsd;
    if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
    {
        printf("Failed to load Winsock!\n");
        return 1;
    }

    //Create our listening socket
    SOCKET sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    if (sListen == SOCKET_ERROR)
    {
        printf("socket() failed:%d\n", WSAGetLastError());
        return 1;
    }

    //Select the local interface, and bind to it
    struct sockaddr_in local;
    if (bInterface)
    {
        local.sin_addr.s_addr = inet_addr(szAddress);
        if (local.sin_addr.s_addr == INADDR_NONE)
        {
            usage();
        }
    }
    else
    {
        //INADDR_ANY是所有网卡地址
        local.sin_addr.s_addr = htonl(INADDR_ANY);
    }
    local.sin_family = AF_INET;
    local.sin_port = htons(iPort);

    //绑定监听网卡地址和端口
    if (bind(sListen, (struct sockaddr*)&local, sizeof(local)) == SOCKET_ERROR)
    {
        printf("bind() failed:%d\n", WSAGetLastError());
    }
    //开始监听客户端
    listen(sListen, 8);

    printf("begin accept on port:%d\n", iPort);

    //In a continuous loop, wait for incoming clients.Once one
    //is detected, create a thread and pass the handle off to it
    while (true)
    {
        struct sockaddr_in client;
        int iAddrSize = sizeof(client);
        SOCKET sClient = accept(sListen, (struct sockaddr*)&client, &iAddrSize);
        if (sClient == INVALID_SOCKET)
        {
            printf("accept() failed:%d\n", WSAGetLastError());
            break;
        }
        printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));

        DWORD dwThreadId;
        HANDLE hThread = CreateThread(NULL, 0, ClientThread, (LPVOID)sClient,
            0, &dwThreadId);
        if (hThread == NULL)
        {
            printf("CreateThread() failed:%d\n", GetLastError());
            break;
        }
        CloseHandle(hThread);
    }
    closesocket(sListen);

    WSACleanup();

    return 0;
}

 

客户端代码,TCPClient.cpp。

// TCPClient.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <WinSock2.h>
#include <stdio.h>
#include <stdlib.h>

#define DEFAULT_COUNT 5
#define DEFAULT_PORT 5150
#define DEFAULT_BUFFER 2048
#define DEFAULT_MESSAGE _T("This is a test of the emergency broadcasting system")

TCHAR szServer[128];
TCHAR szMessage[1024];

int iPort = DEFAULT_PORT;
DWORD dwCount = DEFAULT_COUNT;
BOOL bSendOnly = FALSE;

//Print usage information and exit
void usage()
{
    printf("usage:client[-p:x][-s:IP][-n:x][-o]\n\n");
    printf("  -p:x     Remote port to send to \n");
    printf("  -s:IP    Server's IP address or host name\n");
    printf("  -n:x     Number of times to send message\n");
    printf("  -o        Send messages only; don't receive\n");

    ExitProcess(1);
}

//Parse the command line arguments, and set some global flags
//to indicate what actions to perform
void ValidateArgs(int argc, _TCHAR** argv)
{
    for (int i = 1; i < argc; i++)
    {
        if ((argv[i][0] == _T('-')) || (argv[i][0] == _T('/')))
        {
            switch (tolower(argv[i][1]))
            {
            case _T('p'):
                if (_tcslen(argv[i]) > 3)
                {
                    iPort = _ttoi(&argv[i][3]);
                }
                break;
            case _T('s'):
                if (_tcslen(argv[i]) > 3)
                {
                    _tcscpy_s(szServer, &argv[i][3]);
                }
                break;
            case _T('n'):
                if (_tcslen(argv[i]) > 3)
                {
                    dwCount = _ttol(&argv[i][3]);
                }
                break;
            case _T('o'):
                bSendOnly = TRUE;
                break;
            default:
                usage();
                break;
            }
        }
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    ValidateArgs(argc, argv);

    WSADATA wsd;
    if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)
    {
        printf("Failed to load Winsock library!\n");
        return 1;
    }

    _tcscpy_s(szMessage, DEFAULT_MESSAGE);

    //Create the socket, and attempt to connect to the server
    SOCKET sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    if (sClient == INVALID_SOCKET)
    {
        printf("socket() failed:%d\n", WSAGetLastError());
        return 1;
    }

    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(iPort);

    //If the supplied server address wasn't in the form
    //"aaa.bbb.ccc.ddd,"it's a host name, so try to resolve it
    int nServerLen = (int)_tcslen(szServer);
    if (0 == nServerLen)
    {
        struct hostent* host = gethostbyname(szServer);
        if (host == NULL)
        {
            printf("Unable to resolve server:%s\n", szServer);
            return 1;
        }

        CopyMemory(&server.sin_addr, host->h_addr_list[0], host->h_length);
    }
    else
    {
        server.sin_addr.s_addr = inet_addr(szServer);
    }

    if (connect(sClient, (struct sockaddr*)&server, sizeof(server))
        == SOCKET_ERROR)
    {
        printf("connect() failed:%d\n", WSAGetLastError());
        return 1;
    }

    //Send and receive data
    char szBuffer[DEFAULT_BUFFER];
    int ret = 0;

    for (int i = 0; i < (int)dwCount; i++)
    {
        ret = send(sClient, szMessage, (int)_tcslen(szMessage), 0);
        if (ret == 0)
        {
            break;
        }
        else if (ret == SOCKET_ERROR)
        {
            printf("send() failed:%d\n", WSAGetLastError());
            break;
        }

        printf("Send %d bytes\n", ret);
        if (!bSendOnly)
        {
            ret = recv(sClient, szBuffer, DEFAULT_BUFFER, 0);
            if (ret == 0)
            {
                break;
            }
            else if (ret == SOCKET_ERROR)
            {
                printf("recv() failed:%d\n", WSAGetLastError());
                break;
            }

            szBuffer[ret] = _T('\0');
            printf("RECV [%d bytes]:'%s'\n", ret, szBuffer);
        }
    }
    closesocket(sClient);

    WSACleanup();

    return 0;
}

你可能感兴趣的:(TCP Socket通信学习)