一个简易的TCP服务器程序(将收到的字符发回给客户端)

版权声明:本文为博主原创文章,允许转载,转载请注明出处。 https://blog.csdn.net/KnightOnHourse/article/details/79969803
/*-----------------------------------------------------------------

运行该程序相当于启动了服务器,用telnet客户端来连接服务器。

命令:telnet 127.0.0.1 9999
-----------------------------------------------------------------*/
#include <Windows.h>
#include "resource.h"
#include  <process.h>

#pragma comment(lib,"Ws2_32.lib")

#define TCP_PORT  9999  //监听端口
#define F_STOP    1


TCHAR szAppName[] = TEXT("TcpEcho");
int g_iThreadCount = 0;
HWND hWnd = NULL; //对话框句柄
int g_dwFlag=0; //退出标志

BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
    DialogBox(hInstance, TEXT("TCPECHO"), NULL, DlgProc);
    return 0;
}

//通信服务线程,每个客户端登录的连接将产生一个线程
unsigned int WINAPI ServiceThread(void* lpParam){
    SOCKET hSrvSock = (SOCKET)lpParam;
    char szBuff[512];int iRet;
    FD_SET stFds; TIMEVAL stTv;//WaitFor Socket
    ++g_iThreadCount;
    SetDlgItemInt(hWnd, IDC_COUNT, g_iThreadCount, FALSE);
    while(!(g_dwFlag & F_STOP)){
        //select函数用于判断套接字是否有可读、可读或是否有异常
        //如果检测的就绪时,会返回就数据套接字的数量
        //如果因超时而返回时,返回值是0
        //如果因异常(如连接断开)时,返回SOCKET_ERROR
        stFds.fd_count = 1; stFds.fd_array[0] = hSrvSock;
        stTv.tv_sec = 0; stTv.tv_usec = 200 * 1000;//200ms;
        //因默认recv和send是阻塞的,用该函数来检测
        //当套接字可读或可写时再去recv或send以防止
        //该线程被阻塞。因为一旦被阻塞,当用户关闭
        //应用程序时,g_dwFlag退出标志就无法被检测到,
        //而如果客户端没发送数据时,会使该线程
        //无法退出。所以用select加以检测。
        iRet = select(0, &stFds, NULL, NULL, &stTv);
        if(SOCKET_ERROR == iRet)            break;
        if(iRet){
            memset(szBuff, 0, sizeof(char)*512);//clear buffer
            if(SOCKET_ERROR == recv(hSrvSock, szBuff, sizeof(szBuff), 0))        break;
            if(SOCKET_ERROR == send(hSrvSock, szBuff, iRet, 0))                        break;
        }
    }
    closesocket(hSrvSock);
    --g_iThreadCount;
    SetDlgItemInt(hWnd, IDC_COUNT, g_iThreadCount, FALSE);
    return TRUE;
}

//监听线程
unsigned int  WINAPI ListenThread(void* lpParam){
    TCHAR szErrBind[]  = TEXT("无法绑定到TCP端口9999,请检查是否有其它程序在使用!");
     //创建socket
    SOCKET hListenSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    *(SOCKET*)lpParam = hListenSock;
    //绑定socket
    SOCKADDR_IN stSA; memset(&stSA, 0, sizeof(SOCKADDR_IN));
    stSA.sin_port = htons(9999); stSA.sin_family = AF_INET; stSA.sin_addr.S_un.S_addr = INADDR_ANY;
    if(bind(hListenSock, (PSOCKADDR)&stSA, sizeof(SOCKADDR_IN))){//返回0表示无错误,是成功的。
        MessageBox(hWnd, szErrBind, szAppName, MB_OK|MB_ICONSTOP);
        closesocket(hListenSock);
        return FALSE;
    }
    //开始监听,等待连接并为每个连接创建一个新的服务线程
    listen(hListenSock, 5);
    while(TRUE){
        SOCKET hServiceSock = accept(hListenSock, NULL, 0);
        if(hServiceSock == INVALID_SOCKET)        break;
        unsigned int dwThreadId;
        HANDLE hServiceThread = (HANDLE)_beginthreadex(NULL, 0, &ServiceThread, (LPVOID)(hServiceSock), 0, &dwThreadId);
        CloseHandle(hServiceThread);//线程是内核对象,关闭表示不需用操作了(如唤醒、挂机)。
    }
    closesocket(hListenSock);
    return TRUE;
}

BOOL CALLBACK DlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    WSADATA stWSA;
    static SOCKET hListenSocket = NULL;
    static HANDLE hListenThread = NULL;

    switch (message)
    {
    case WM_INITDIALOG:
        hWnd = hwnd;
        //载入WS2_32.DLL动态链0x0002:MAKEWORD(2,0)
        WSAStartup(MAKEWORD(2,0), &stWSA);//动态库的信息返回到WSAdata变量中
        //创建监听线程
        unsigned int dwThreadId;
        hListenThread = (HANDLE)_beginthreadex(NULL, 0, &ListenThread, (LPVOID)(&hListenSocket), 0, &dwThreadId);
        //只是关闭了一个线程句柄对象,表示我不再使用该句柄,即不对这个句柄对
         //应的线程做任何干预了(如挂起或唤醒)。并没有结束线程。
        CloseHandle(hListenThread);                     
        return TRUE;
    case WM_CLOSE:
        //当未有客户端连接时,该socket在线程中创建,且未退出线程。
        //所以要在这里监听socket,此时会将accept返回失败,监听线程退出。
        closesocket(hListenSocket);

        g_dwFlag |= F_STOP;   //设置退出标志,以便让服务线程中止      
        while (g_iThreadCount > 0); //等待服务线程关闭

        WSACleanup();
        EndDialog(hwnd, 0);
        return TRUE;
    }
    return FALSE;

}


rc>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

TCPECHO DIALOGEX 0, 0, 165, 36
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "TcpEcho服务器"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    LTEXT           "当前连线的客户端数量:",IDC_STATIC,15,15,89,8
    LTEXT           "0",IDC_COUNT,109,15,44,8

END

resource.h>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

#define IDC_COUNT                       1001




阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页