PPC的C/C++和人工智能学习笔记
每一篇学习笔记,都只是为了更好地掌握和理解

Win32SDK(16-1)_SOCKET多线程TCP服务端

今天学习:win32SDK的多线程SOCKET的tcp连接服务端。

 

服务器端:

  • 可以同时接受多个客户端的连接;
  • 创建accept线程,用来接收新客户端的连接;
  • 为每个客户端都创建了收、发两个线程;
  • 使用了异步模式,但是感觉不是很好,浪费资源;
  • 为了简单,使用多字节字符集;
  • 上面一章的unicode和多字节的转换还有些问题,好像是字节数处理上;
  • 这种异步模式的通信,因为处理大量的循环,所以资源浪费严重的;

 

XTcpServer类:服务端类,采用单实例模式,主要是创建了accept线程,并用vector数组来保存所有客户端的连接数据;

XTcpSClient类:服务器每个客户端连接类,每个客户端都会新建一个该类对象,并创建收、发两个线程来处理收和发。

 

主界面:

  • 菜单:启动服务、停止服务、退出程序。
  • ListBox:LB_msg,显示系统信息。
  • 发送:ComboBox选择当前客户端,Edit选择发送的内容。
  • 断开连接按钮,ComboBox选择要断开的客户端,断开按钮,断开1个连接。(no done)
  • Checkbox,勾选是否回送相同的接收内容。

与上一篇文章的tcp客户端,可以完成通信了。

 

main.cpp 服务端主程序

//main.cpp
//01_tcp服务端,收发

#define WIN32_LEAN_AND_MEAN //解决winsock2.h写在windows.h下面的错误
#include <Windows.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include "XTcpServer.h"
#pragma comment(linker,"/subsystem:\"console\" /entry:\"WinMainCRTStartup\"")

void OnCreate(HWND hWnd);
void OnCommand(HWND hWnd, WPARAM wParam);

HINSTANCE g_hInst;
HWND g_hWnd;
XTcpServer* g_Server = NULL;
bool bReSend = false; //服务器是否回发 接收到的信息

void OnCreate(HWND hWnd) {
 //1.创建菜单
 HMENU hMenuTop = CreateMenu();
 HMENU hPopMenu = CreatePopupMenu();
 AppendMenu(hPopMenu, MF_STRING, 4001, _T("启动服务器"));
 AppendMenu(hPopMenu, MF_STRING, 4002, _T("停止服务器"));
 AppendMenu(hPopMenu, MF_SEPARATOR, 0, NULL);
 AppendMenu(hPopMenu, MF_STRING, 4003, _T("退出"));
 AppendMenu(hMenuTop, MF_POPUP, (UINT_PTR)hPopMenu, _T("系统"));
 SetMenu(hWnd, hMenuTop);
 EnableMenuItem(hPopMenu, 4002, MF_DISABLED | MF_BYCOMMAND);
 //2.创建主窗口控件
 CreateWindowEx(NULL, _T("ListBox"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_HSCROLL,
 1, 1, 650, 400, hWnd, (HMENU)1001, g_hInst, NULL); //ListBox
 CreateWindowEx(NULL, _T("Edit"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE |WS_HSCROLL|WS_VSCROLL|
 ES_AUTOHSCROLL | ES_AUTOVSCROLL, 1, 410, 550, 90, hWnd, (HMENU)1002, g_hInst, NULL); //Edit发送内容
 CreateWindowEx(NULL, _T("Button"), _T("发送"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
 560, 450, 50, 40, hWnd, (HMENU)2001, g_hInst, NULL); //发送按钮
 CreateWindowEx(NULL, _T("ComboBox"), NULL, WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST,
 1, 510, 150, 120, hWnd, (HMENU)2002, g_hInst, NULL); //客户端选择 ComboBox
 CreateWindowEx(NULL, _T("Button"), _T("断开"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
 160, 510, 80, 40, hWnd, (HMENU)2003, g_hInst, NULL); //断开按钮
 CreateWindowEx(NULL, _T("Button"), _T("回送信息"), WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
 260, 510, 100, 40, hWnd, (HMENU)2004, g_hInst, NULL); //回送选择框

 //3.服务器类初始化
 g_Server= XTcpServer::GetInstance(hWnd);
}

void OnCommand(HWND hWnd, WPARAM wParam) {
 int wmID = LOWORD(wParam);
 int wmEvent = HIWORD(wParam);
 if (wmID == 4001) { //菜单,启动服务器
 EnableMenuItem(GetSubMenu(GetMenu(hWnd), 0), 4001, MF_DISABLED | MF_BYCOMMAND);
 EnableMenuItem(GetSubMenu(GetMenu(hWnd), 0), 4002, MF_ENABLED | MF_BYCOMMAND);
 if (g_Server->InitServer()) {
 g_Server->CreateAcceptThread();
 }
 }
 else if (wmID == 4002) {//菜单,关闭服务器
 EnableMenuItem(GetSubMenu(GetMenu(hWnd), 0), 4002, MF_DISABLED | MF_BYCOMMAND);
 g_Server->ReleaseServer();
 //清空listBox和ComboBox
 //SendMessage(GetDlgItem(hWnd, 1001), LB_RESETCONTENT, 0, 0);
 SendMessage(GetDlgItem(hWnd, 1001), LB_ADDSTRING, 0, (LPARAM)"服务器stop");
 SendMessage(GetDlgItem(hWnd, 2002), LB_RESETCONTENT, 0, 0);
 EnableMenuItem(GetSubMenu(GetMenu(hWnd), 0), 4001, MF_ENABLED | MF_BYCOMMAND);
 //关闭操作
 }
 else if (wmID == 2004) { //设置是否回送信息
 printf("check box\n");
 LRESULT state = SendMessage(GetDlgItem(hWnd, 2004), BM_GETCHECK, 0, 0);
 if (state == BST_CHECKED)
 bReSend = true;
 else
 bReSend = false;
 printf("bReSend=%d\n", bReSend);
 }
 else if (wmID == 2001 && wmEvent==BN_CLICKED) { //服务端向客户端发送消息,要先选择ComboBox
 //获取combobox的当前内容
 char CombSelect[1024] = { 0 };
 GetWindowText(GetDlgItem(hWnd, 2002), CombSelect, 1024);
 printf("当前comboSelect:%s\n", CombSelect);
 int nowClientID = 0;
 sscanf(CombSelect, "%d", &nowClientID);
 printf("当前ID=%d\n", nowClientID);
 if (nowClientID > 0) {
 XTcpSclient* pnowClient = g_Server->m_ClientVector[nowClientID - 1].pClient;
 char SendEdit[1024] = { 0 };
 GetWindowText(GetDlgItem(hWnd, 1002), SendEdit, 1024);
 if (strlen(SendEdit) > 0) { //有内容
 EnterCriticalSection(&pnowClient->m_csClient);
 strcpy(pnowClient->m_sendBuf, SendEdit);
 pnowClient->m_isSend = true;
 SetEvent(pnowClient->m_sendEvent);
 LeaveCriticalSection(&pnowClient->m_csClient);
 }
 }
 }
}

LRESULT CALLBACK myWinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
 switch (message) {
 case WM_CREATE:
 OnCreate(hWnd);
 break;
 case WM_COMMAND:
 OnCommand(hWnd, wParam);
 break;
 case WM_DESTROY:
 if (g_Server->m_isServerUp)
 g_Server->ReleaseServer();
 g_Server->DeleteInstance();
 PostQuitMessage(0);
 break;
 }
 return DefWindowProc(hWnd, message, wParam, lParam);
}

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR lpCmdLine, int nCmdShow) {
 g_hInst = hInstance;
 WNDCLASSEX wce = { 0 };
 wce.cbSize = sizeof(WNDCLASSEX);
 wce.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
 wce.cbClsExtra = NULL;
 wce.cbWndExtra = NULL;
 wce.hInstance = hInstance;
 wce.lpfnWndProc = myWinProc;
 wce.hIcon = NULL;
 wce.hCursor = LoadCursor(NULL, IDC_ARROW);
 wce.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
 wce.lpszMenuName = NULL;
 wce.lpszClassName = _T("myWinServer");
 wce.hIconSm = NULL;
 if (!RegisterClassEx(&wce)) return -1;
 HWND hWnd = CreateWindowEx(NULL, wce.lpszClassName, _T("TCP服务端_收发"), WS_OVERLAPPEDWINDOW,
 200, 200, 700, 700, NULL, NULL, hInstance, NULL);
 g_hWnd = hWnd;
 if (!hWnd) return -1;
 ShowWindow(hWnd, SW_SHOW);
 UpdateWindow(hWnd);
 MSG msg = { 0 };
 while (GetMessage(&msg, NULL, 0, 0)) {
 TranslateMessage(&msg);
 DispatchMessage(&msg);
 }
 UnregisterClass(wce.lpszClassName, hInstance);
 getchar(); 
 return 0;
}

 

XTcpServer.h XTcpServer类头文件

//XTcpServer类头文件
//单例模式
#pragma once
#ifndef _XTCPSERVER_H
#define _XTCPSERVER_H

#include <assert.h>
#include <WinSock2.h>
#include <process.h>
#include <stdio.h>
#include <vector>
#include "XTcpSclient.h"
#pragma comment(lib,"ws2_32.lib")
using std::vector;

#define SERVERIP "192.168.31.78"
#define SERVERPORT 5050

class XTcpServer
{
public:
 typedef struct _clientDS {
 unsigned int nClient_id;
 XTcpSclient* pClient;
 }ClientDS; //每个客户端连接的结构体
private:
 XTcpServer(HWND hWnd);
 ~XTcpServer();
public:
 static XTcpServer* GetInstance(HWND hWnd); //静态方法,获取单例
 static void DeleteInstance(); //静态方法,删除实例
 static DWORD WINAPI AcceptThread(void* pvParam); //Accept线程函数
public:
 bool InitServer(); //初始化类
 void ReleaseServer(); //销毁数据
 bool CreateAcceptThread(); //创建accept线程
public: //要修改为private
 static XTcpServer* m_pXTcpServer; //单例模式
 HWND m_hWnd; //主窗口句柄
 SOCKET m_sSocket; //服务器socket
 CRITICAL_SECTION m_csServer; //服务器临界区
 HANDLE m_hThreadAccept; //accept线程句柄
 bool m_isServerUp; //服务器是否启动
 unsigned int m_uniqueID; //标记client的唯一ID
 vector<ClientDS> m_ClientVector; //每个客户端的vector
};

#endif // _XTCPSERVER_H

XTcpServer.cpp XTcpServer类实现文件

//XTcpServer类实现文件
#include "XTcpServer.h"

XTcpServer* XTcpServer::m_pXTcpServer = NULL;

XTcpServer::XTcpServer(HWND hWnd){
 m_hWnd = hWnd; //主窗口句柄
 m_sSocket = INVALID_SOCKET; //服务器socket=INVALID_SOCKET
 m_hThreadAccept=NULL; //accept线程句柄=NULL
 m_isServerUp = false; //服务器启动标志 false
}

XTcpServer::~XTcpServer(){
}

/**
* 初始化类
*/
bool XTcpServer::InitServer() { 
 m_uniqueID = 0;
 InitializeCriticalSection(&m_csServer); //服务器临界区初始化
 //初始化socket
 WSADATA wsa = { 0 };
 WORD wVersionRequest = MAKEWORD(2, 2);
 if (WSAStartup(wVersionRequest, &wsa) != 0 ||
 (LOBYTE(wsa.wVersion) != 2 || HIBYTE(wsa.wVersion) != 2)) //失败
 return false;
 m_sSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 if (m_sSocket == INVALID_SOCKET)
 return false;
 //设置套接字非阻塞模式
 unsigned long ul = 1;
 if (ioctlsocket(m_sSocket, FIONBIO, (unsigned long*)&ul) == SOCKET_ERROR)
 return false;
 //绑定套接字
 SOCKADDR_IN serverAddr;
 serverAddr.sin_family = AF_INET;
 serverAddr.sin_port = htons(SERVERPORT);
 serverAddr.sin_addr.S_un.S_addr = INADDR_ANY;
 if (bind(m_sSocket, (SOCKADDR*)&serverAddr, sizeof(SOCKADDR)) == SOCKET_ERROR)
 return false;
 //监听(只监听2个)
 if (listen(m_sSocket, 2) == SOCKET_ERROR)
 return false;

 return true;
}
/**
* 销毁数据
*/
void XTcpServer::ReleaseServer() { 
 //关闭accept线程
 printf("准备关闭 Server accept 线程\n");
 m_isServerUp = false;
 WaitForSingleObject(m_hThreadAccept, INFINITE);
 CloseHandle(m_hThreadAccept);
 printf("Server accept 线程已经关闭!\n");
 //先关闭所有的客户端连接,退出所有的客户端线程
 int count = m_ClientVector.size();
 for (int i = 0; i < count; ++i) {
 printf("ID=%d客户端准备关闭!\n", m_ClientVector[i].pClient->m_ClientID);
 m_ClientVector[i].pClient->m_isConnected = false;
 SetEvent(m_ClientVector[i].pClient->m_sendEvent); //触发下 客户端发送线程事件
 WaitForMultipleObjects(2, m_ClientVector[i].pClient->m_arrThread, TRUE, INFINITE);
 printf("ID=%d客户端已经关闭线程!\n", m_ClientVector[i].pClient->m_ClientID);
 delete m_ClientVector[i].pClient; //销毁客户端
 printf("ID=%d客户端清除内存!\n", m_ClientVector[i].pClient->m_ClientID);
 }
 
 m_ClientVector.clear();
 DeleteCriticalSection(&m_csServer); //销毁临界区
 closesocket(m_sSocket);
 WSACleanup();
}

/** 
* 静态方法,获取单例
*/
XTcpServer* XTcpServer::GetInstance(HWND hWnd) {
 if (m_pXTcpServer == NULL)
 m_pXTcpServer = new XTcpServer(hWnd);
 assert(m_pXTcpServer != NULL);
 return m_pXTcpServer;
}

/**
* 静态方法,删除单例
*/
void XTcpServer::DeleteInstance() {
 if (m_pXTcpServer != NULL)
 delete m_pXTcpServer;
 m_pXTcpServer = NULL;
}

/**
* 创建accept线程
*/
bool XTcpServer::CreateAcceptThread() { 
 m_isServerUp = true; //设置服务器为启动
 m_hThreadAccept = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)XTcpServer::AcceptThread, (void*)this, 0, NULL);
 if (m_hThreadAccept == NULL) {
 m_isServerUp = false;
 return false;
 }
 return true;
}

/**
* 静态方法,Accept线程函数
*/
DWORD WINAPI XTcpServer::AcceptThread(void* pvParam) { 
 printf("Server Accept 线程开启\n");
 SOCKET sAccept= INVALID_SOCKET; //接受客户端连接的socket
 SOCKADDR_IN clientAddr = { 0 }; //客户端socket地址
 XTcpServer* Ser = (XTcpServer*)pvParam;
 int len = sizeof(SOCKADDR_IN);
 while (Ser->m_isServerUp) {
 sAccept = accept(Ser->m_sSocket, (SOCKADDR*)&clientAddr, &len);
 if (sAccept == INVALID_SOCKET) {
 int err = WSAGetLastError(); //非阻塞模式?真的好吗,这里线程循环,好像会浪费资源。。
 if (err == WSAEWOULDBLOCK) { //因为是非阻塞模式,这个错误表示 没有客户端来连接
 printf("Accept Thread 没有客户端连接\n");
 Sleep(500); //切换出线程
 continue;
 }
 else { //真的出现错误了
 printf("Accept Thread accept错误!\n"); //可以测试:在连接数超过设定的时候,是不是出现错误
 return 0; //退出线程
 }
 }
 else { //accept连接成功了
 //处理客户端的连接
 //添加vector等等
 //客户端对象 new
 //客户端对象 线程 启动
 EnterCriticalSection(&Ser->m_csServer);
 ++Ser->m_uniqueID; //唯一id ++
 XTcpSclient* pClient = new XTcpSclient(Ser->m_uniqueID,sAccept, clientAddr);
 if (pClient == NULL) {
 LeaveCriticalSection(&Ser->m_csServer);
 printf("new XTcpSclient Error!\n");
 exit(0);
 }
 XTcpServer::ClientDS* pClientDS = new XTcpServer::ClientDS;
 if (pClientDS == NULL) {
 LeaveCriticalSection(&Ser->m_csServer);
 printf("new ClientDS Error!\n");
 exit(0);
 }
 pClientDS->nClient_id = Ser->m_uniqueID;
 pClientDS->pClient = pClient;
 Ser->m_ClientVector.push_back(*pClientDS); //加入clientVector
 LeaveCriticalSection(&Ser->m_csServer);
 delete pClientDS; //销毁 pClientDS内存
 printf("Accept OK:\n");
 pClient->Start();
 //添加到界面 ComboBox中
 char CombShow[1024] = { 0 };
 sprintf(CombShow, "%d %s(%d)", pClient->m_ClientID, inet_ntoa(pClient->m_addr.sin_addr), ntohs(pClient->m_addr.sin_port));
 printf("accept:%s\n", CombShow);
 SendMessage(GetDlgItem(Ser->m_hWnd, 2002), CB_ADDSTRING, 0, (LPARAM)CombShow);
 SendMessage(GetDlgItem(Ser->m_hWnd, 1001), LB_ADDSTRING, 0, (LPARAM)CombShow);
 //ValidateRect(Ser->m_hWnd, NULL);
 }

 }
 printf("Server Accept线程关闭\n");
 return 0;
}

 

XTcpSclient.h XTcpSclient类头文件

//服务端程序的XTcpSclient类头文件
//处理服务端的每个客户端连接
#pragma once
#ifndef _XTCPSCLIENT_H
#define _XTCPSCLIENT_H

#include <WinSock2.h>
#include <stdio.h>
#include <process.h>

extern bool bReSend; //用了全局变量,从主程序中引入,不是很好
extern HWND g_hWnd; //用了全局变量,从主程序中引入,不是很好

class XTcpSclient
{
public:
 XTcpSclient(const unsigned int id,const SOCKET sock,const SOCKADDR_IN &addr);
 ~XTcpSclient();
 void Start();
public:
 static DWORD WINAPI sendThread(void* pvParam); //发送线程函数
 static DWORD WINAPI recvThread(void* pvParam); //接收线程函数
public: //要修改为private
 unsigned int m_ClientID;//每个客户端的ID
 HANDLE m_sendEvent; //发送线程控制事件,手动事件对象,初始化为未触发状态
 CRITICAL_SECTION m_csClient; //用来锁定发送buf
 HANDLE m_arrThread[2]; //0-sendThread 1-recvThread
 HANDLE &m_sendThread;
 HANDLE &m_recvThread;
 SOCKET m_socket; //每个客户端对应的socket
 SOCKADDR_IN m_addr; //每个客户端对应的addr
 bool m_isConnected; //是否连接
 bool m_isSend; //是否有数据发送
 bool m_isExit; //是否退出
 char m_sendBuf[1024]; //发送缓冲区
};

#endif // !_XTCPSCLIENT_H

 

XTcpSclient.cpp XTcpSclient类实现文件

#include "XTcpSclient.h"

XTcpSclient::XTcpSclient(const unsigned int id, const SOCKET sock,const SOCKADDR_IN &addr):
 m_sendThread(m_arrThread[0]), m_recvThread(m_arrThread[1])
{
 m_ClientID = id;
 m_socket = sock;
 m_addr = addr;
 m_isConnected = false;
 m_isSend = false;
 m_isExit = false;
 m_sendThread = NULL;
 m_recvThread = NULL;
 m_sendEvent = CreateEvent(NULL, TRUE, FALSE, NULL); //手动事件对象,初始化为未触发状态
 InitializeCriticalSection(&m_csClient); //初始化 临界区
}

XTcpSclient::~XTcpSclient()
{
 CloseHandle(m_sendEvent);
 closesocket(m_socket);
 m_socket = INVALID_SOCKET;
 DeleteCriticalSection(&m_csClient); //删除临界区
 //是否要等线程结束?
}

void XTcpSclient::Start() { //启动收发线程
 m_isConnected = true;
 m_sendThread = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)XTcpSclient::sendThread, (void*)this, 0, NULL);
 if (m_sendThread == NULL) {
 printf("Send Thread Create ERROR\n");
 return;
 }
 m_recvThread = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)XTcpSclient::recvThread, (void*)this, 0, NULL);
 if (m_recvThread == NULL) {
 printf("Recv Thread Create ERROR\n");
 return;
 }
 printf("Send and Recv Thread Create OK!\n");
}

DWORD WINAPI XTcpSclient::sendThread(void* pvParam) { //发送线程函数
 printf("send Thread first IN\n");
 XTcpSclient* cli = (XTcpSclient*)pvParam;
 while (cli->m_isConnected) {
 printf("send Thread:准备发送\n"); //这样好像浪费太多资源了,需要事件来控制
 WaitForSingleObject(cli->m_sendEvent, INFINITE); //等send手动事件
 if (!cli->m_isConnected || cli->m_isExit)
 break;
 if (cli->m_isSend) { //需要发送
 EnterCriticalSection(&cli->m_csClient);
 if (send(cli->m_socket, cli->m_sendBuf, strlen(cli->m_sendBuf), 0) == SOCKET_ERROR) {
 int err = WSAGetLastError();
 if (err == WSAEWOULDBLOCK) {
 LeaveCriticalSection(&cli->m_csClient);
 printf("send Thread: WSAEWOULDBLOCK\n");
 continue;
 }
 //WSAENETDOWN == nErrCode ||WSAETIMEDOUT == nErrCode ||WSAECONNRESET == nErrCode)//客户端关闭了连接
 else {
 LeaveCriticalSection(&cli->m_csClient);
 ResetEvent(cli->m_sendEvent);
 cli->m_isConnected = false;
 cli->m_isSend = false;
 cli->m_isExit = true;
 break; //退出线程了。
 }
 }
 //成功发送了,要释放临界区啥的
 LeaveCriticalSection(&cli->m_csClient);
 ResetEvent(cli->m_sendEvent);
 printf("ID:%d,send Thread: send ok\n",cli->m_ClientID);
 char ListShow[2048] = { 0 };
 sprintf(ListShow, "ID=%d,发送:%s", cli->m_ClientID, cli->m_sendBuf);
 SendMessage(GetDlgItem(g_hWnd, 1001), LB_ADDSTRING, 0, (LPARAM)ListShow);
 cli->m_isSend = false;
 }
 }
 printf("send Thread Exit\n");
 return 0;
}
DWORD WINAPI XTcpSclient::recvThread(void* pvParam) { //接收线程函数
 printf("recv Thread First IN\n");
 XTcpSclient* cli = (XTcpSclient*)pvParam;
 char recvBuf[1024]; //接收缓冲区
 while (cli->m_isConnected) {
 //printf("Recv Thread:resend=%d\n", bReSend);
 int res = recv(cli->m_socket, recvBuf, 1023, 0);
 if (res == SOCKET_ERROR) {
 int err = WSAGetLastError();
 if (err == WSAEWOULDBLOCK) { //异步模式的接收,线程一直在这里循环,感觉浪费资源啊?
 //printf("recv Thread:WSAEWOULDBLOCK\n"); //确实cpu占用不小。
 Sleep(0); //所以加了Sleep(0)
 continue;
 }
 else if (err == WSAENETDOWN || err == WSAETIMEDOUT || err == WSAECONNRESET) {
 //客户端断开了连接
 printf("recv Thread:ERR:客户端断开了连接");
 cli->m_isConnected = false;
 break; //线程退出?
 }
 }
 if (res == 0) {//客户端关闭了连接
 printf("recv Thread:res==0:客户端断开了连接");
 cli->m_isConnected = false;
 break; //线程退出?
 }
 if (res > 0) { //正确收到了数据
 char* pClientIP = inet_ntoa(cli->m_addr.sin_addr);
 WORD ClientPort = ntohs(cli->m_addr.sin_port);
 printf("IP:%s,Port:%d,接收:%s\n", pClientIP, ClientPort, recvBuf);
 char ListShow[2048] = { 0 };
 sprintf(ListShow, "ID=%d,IP:%s,Port:%d,接收:%s", cli->m_ClientID, pClientIP, ClientPort, recvBuf);
 SendMessage(GetDlgItem(g_hWnd, 1001), LB_ADDSTRING, 0, (LPARAM)ListShow);
 if (bReSend) { //需要回发
 EnterCriticalSection(&cli->m_csClient);
 //1、接收到的数据送入 sendBuf
 strcpy(cli->m_sendBuf, recvBuf);
 //2、触发发送事件
 cli->m_isSend = true;
 LeaveCriticalSection(&cli->m_csClient);
 SetEvent(cli->m_sendEvent);
 }
 }
 }
 return 0;
}

 

 

 

 

(2017-06-30 www.vsppc.com)

学习笔记未经允许不得转载:PPC的C/C++和人工智能学习笔记 » Win32SDK(16-1)_SOCKET多线程TCP服务端

分享到:更多 ()

评论 抢沙发

评论前必须登录!