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

Win32SDK(16)_SOCKET多线程TCP客户端

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

 

要解决的问题:

  • 为了不影响主线程(UI线程)的响应,连接、发送、接收分别用3个线程来实现。
  • 主动连接服务器、主动中断服务器连接。
  • 连接意外中断的时候,要能及时发现并处理。
  • 通过内核事件来控制线程的运行,包括正常的等待和退出。
  • 客户端的SOCKET连接分为同步和异步两种处理。

 

主程序main.cpp

//main.cpp
//01_tcp客户端,收发
//分别用单独的线程,收和发

#define WIN32_LEAN_AND_MEAN //解决winsock2.h写在windows.h下面的错误
#include <Windows.h>
#include <tchar.h>
#include <stdlib.h>
#include "XTcpClient.h"

#pragma comment(linker,"/subsystem:\"console\" /entry:\"wWinMainCRTStartup\"")

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

HINSTANCE g_hInst;
XTcpClient Xclient;

void OnCreate(HWND hWnd) {
 //1.初始化界面:ListBox,Edit,Button
 CreateWindowEx(NULL, _T("ListBox"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER|WS_VSCROLL|WS_HSCROLL,
 1, 1, 400, 300, hWnd, (HMENU)1001, g_hInst, NULL);
 CreateWindowEx(NULL, _T("Edit"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER|ES_AUTOHSCROLL|
 ES_AUTOVSCROLL|WS_HSCROLL|WS_VSCROLL|ES_MULTILINE,
 1, 410, 400, 100, hWnd, (HMENU)1002, g_hInst, NULL);//发送内容
 CreateWindowEx(NULL, _T("Edit"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,
 1, 340, 120, 30, hWnd, (HMENU)1003, g_hInst, NULL); //Server IP
 CreateWindowEx(NULL, _T("Edit"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,
 150, 340, 50, 30, hWnd, (HMENU)1004, g_hInst, NULL);//Server port
 CreateWindowEx(NULL, _T("Button"), _T("连接"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
 10, 530, 60, 30, hWnd, (HMENU)2001, g_hInst, NULL);
 CreateWindowEx(NULL, _T("Button"), _T("断开"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
 100, 530, 60, 30, hWnd, (HMENU)2002, g_hInst, NULL);
 CreateWindowEx(NULL, _T("Button"), _T("发送信息"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
 200, 530, 80, 30, hWnd, (HMENU)2003, g_hInst, NULL);
 CreateWindowEx(NULL, _T("Button"), _T("关闭线程"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
 300, 530, 80, 30, hWnd, (HMENU)2004, g_hInst, NULL);
 EnableWindow(GetDlgItem(hWnd, 2002), FALSE); //断开 按钮禁止
 EnableWindow(GetDlgItem(hWnd, 2003), FALSE); //发送 按钮禁止
 SendMessage(GetDlgItem(hWnd, 1003), WM_SETTEXT, 0, (LPARAM)_T("192.168.31.78"));
 SendMessage(GetDlgItem(hWnd, 1004), WM_SETTEXT, 0, (LPARAM)_T("5050"));
 //2.初始化 XTcpClient 类
 if (Xclient.InitClient()) {
 printf("XClient初始化成功。\n");
 SendMessage(GetDlgItem(hWnd, 1001), LB_INSERTSTRING, 0, (LPARAM)_T("客户端初始化成功。"));
 }
 Xclient.SetHwnd(hWnd); //把主窗口句柄传入 Xclient
}

void OnCommand(HWND hWnd, WPARAM wParam) {
 int wmID = LOWORD(wParam);
 int wmEvent = HIWORD(wParam);
 if (wmID == 2001 && wmEvent == BN_CLICKED) { //连接按钮点击
 EnableWindow(GetDlgItem(hWnd, 2001), FALSE); //连接 按钮禁止
 SetServerIpPort(hWnd);
 if (!Xclient.ConnectServer()) {
 printf("创建连接线程失败。\n");
 SendMessage(GetDlgItem(hWnd, 1001), LB_INSERTSTRING, 0, (LPARAM)_T("创建连接线程失败。"));
 }
 }
 else if (wmID == 2002 && wmEvent == BN_CLICKED) { //断开连接
 EnableWindow(GetDlgItem(hWnd, 2002), FALSE);
 EnableWindow(GetDlgItem(hWnd, 2003), FALSE);
 EnterCriticalSection(&Xclient.m_cs_send); //关闭socket用临界区
 if (Xclient.m_Socket_Client != INVALID_SOCKET) {
 closesocket(Xclient.m_Socket_Client);
 WSACleanup();
 Xclient.m_Socket_Client = INVALID_SOCKET;
 }
 LeaveCriticalSection(&Xclient.m_cs_send);
 EnableWindow(GetDlgItem(hWnd, 2001), TRUE);
 }
 else if (wmID == 2003 && wmEvent == BN_CLICKED) { //发送按钮点击
 //转换:edit内容为多字节到 Xclient.m_Sendbuf,需要锁,然后触发发送事件启动线程
 TCHAR strEditSend[4096] = { 0 };
 GetWindowText(GetDlgItem(hWnd, 1002), strEditSend, 4096);
 printf("send length=%d\n", _tcslen(strEditSend));
 if (_tcslen(strEditSend) > 0) {
 EnterCriticalSection(&Xclient.m_cs_send); //进入临界区
 WideCharToMultiByte(CP_ACP, 0, strEditSend, _tcslen(strEditSend), Xclient.m_SendBuf, _tcslen(strEditSend), NULL, NULL);
 Xclient.m_SendBuf[_tcslen(strEditSend)] = '\0';
 printf("转换后的sendbuf->%s\n", Xclient.m_SendBuf);
 Xclient.m_isSend = true; //标志send状态
 LeaveCriticalSection(&Xclient.m_cs_send);
 SetEvent(Xclient.m_send_event); //触发发送事件,通知发送线程
 }
 }
 else if (wmID == 2004 && wmEvent == BN_CLICKED) { //关闭线程
 //1.关闭send线程
 Xclient.m_isQuit = true;
 SetEvent(Xclient.m_send_event);
 //2.关闭recv线程
 SetEvent(Xclient.m_recv_event);
 }
}

void SetServerIpPort(HWND hWnd) {
 TCHAR strEditIp[20] = { 0 }; //存放edit控件IP的字符串
 TCHAR strEditPort[10] = { 0 }; //存放edit控件PORT的字符串
 char strIp[20] = { 0 };
 char strPort[10] = { 0 };
 GetWindowText(GetDlgItem(hWnd, 1003), strEditIp, 20);
 GetWindowText(GetDlgItem(hWnd, 1004), strEditPort, 10);
#ifdef _UNICODE
 WideCharToMultiByte(CP_ACP, 0, strEditIp, _tcslen(strEditIp), strIp, _tcslen(strEditIp), NULL, NULL);
 WideCharToMultiByte(CP_ACP, 0, strEditPort, _tcslen(strEditPort), strPort, _tcslen(strEditPort), NULL, NULL);
#else
 strcpy(str, strTmp);
#endif
 strIp[_tcslen(strEditIp)] = '\0';
 strPort[_tcslen(strEditPort)] = '\0';
 Xclient.SetServerIpPort(strIp, atoi(strPort));
 TCHAR strShow[50] = { 0 };
 wsprintf(strShow,_T("ServerIP:%s,Port:%s"), strEditIp, strEditPort);
 SendMessage(GetDlgItem(hWnd, 1001), LB_INSERTSTRING, 0, (LPARAM)strShow);
}

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:
 WaitForMultipleObjects(2, Xclient.m_ThreadArr, TRUE, INFINITE);
 printf("线程已经全部退出了!");
 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("myWin");
 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);
 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;
}

 

XTcpClient类头文件(Tcp的客户端)

//XTcpClient类头文件,Tcp的客户端
#pragma once
#ifndef _XTCPCLIENT_H
#define _XTCPCLIENT_H

#include <stdio.h>
#include <WinSock2.h>
#include <Windows.h>
#include <tchar.h>
#include <process.h> //_beginthreadex需要
#pragma comment(lib,"ws2_32.lib")

#define MAX_BUF_LENGTH 4096 //缓冲区buf的大小

class XTcpClient
{
public:
 XTcpClient();
 ~XTcpClient();
public:
 static DWORD WINAPI ConnThread(void* pvParam); //连接线程函数
 static DWORD WINAPI SendThread(void* pvParam); //发送线程
 static DWORD WINAPI RecvThread(void* pvParam); //接收线程
public:
 bool InitClient(); //初始化XTcpClient
 void ReleaseClient(); //释放XTcpClient的资源
 bool ConnectServer(); //创建连接线程
 bool CreateSendRecvThread(); //创建收和发的线程
public:
 void ShowMessage(TCHAR* msg); //将信息显示在主窗口
 void SetHwnd(HWND hWnd); //设置主窗口的HWND
 void SetServerIpPort(char* _ip, int _port); //设置服务器IP和Port
public: //要修改为 private
 char m_ServerIP[16]; //服务器IP
 int m_ServerPort; //服务器Port
 CRITICAL_SECTION m_cs_send; //临界区,用来锁定发送buf
 HANDLE m_send_event; //发送事件对象
 HANDLE m_recv_event; //接收事件对象
 SOCKET m_Socket_Client; //客户端套接字
 char m_SendBuf[MAX_BUF_LENGTH]; //发送buf
 char m_RecvBuf[MAX_BUF_LENGTH]; //接收buf
 HANDLE m_ThreadArr[3]; //线程数组 0-发送,1-接收,2-连接
 HANDLE &m_Thread_send; //线程数组的引用 0-发送
 HANDLE &m_Thread_recv; //线程数组的引用 1-接收
 HANDLE &m_Thread_conn; //线程数组的引用 2-连接
 HWND m_hWnd; //主窗口的句柄
 bool m_isConnected; //是否连接状态
 bool m_isSend; //是否需要发送
 bool m_isQuit; //是否退出
};

#endif // !_XTCPCLIENT_H

 

XTcpClient类实现文件(Tcp的客户端)

//XTcpClient类实现文件,Tcp的客户端
#include "XTcpClient.h"

XTcpClient::XTcpClient():
 m_Thread_send(m_ThreadArr[0]),
 m_Thread_recv(m_ThreadArr[1]),
 m_Thread_conn(m_ThreadArr[2]){
}

XTcpClient::~XTcpClient(){
}

/***
 设置主窗口的句柄,方便类和线程控制窗口的控件 SetHwnd
***/
void XTcpClient::SetHwnd(HWND hWnd) {
 m_hWnd = hWnd;
}

/***
 将信息显示在主窗口 ShowMessage
***/
void XTcpClient::ShowMessage(TCHAR* msg) {
 SendMessage(GetDlgItem(m_hWnd, 1001), LB_INSERTSTRING, 0, (LPARAM)msg);
}

/***
 设置服务器IP和Port SetServerIpPort
***/
void XTcpClient::SetServerIpPort(char* _ip, int _port) { 
 memcpy(m_ServerIP, _ip, (strlen(_ip) + 1) * sizeof(char));
 m_ServerPort = _port;
}
/***
 XTcpClient的初始化:变量、套接字初始化 InitClient
***/
bool XTcpClient::InitClient() { 
 m_isConnected = false;
 m_isSend = false;
 m_isQuit = false;
 InitializeCriticalSection(&m_cs_send); //初始化临界区
 m_send_event = CreateEvent(NULL, FALSE, FALSE, NULL); //发送:自动事件对象,初始化为未触发状态
 m_recv_event = CreateEvent(NULL, TRUE, FALSE, NULL); //接收:手动事件对象,初始化为未触发状态
 m_Socket_Client = INVALID_SOCKET; 
 m_Thread_send = NULL; //发送线程句柄
 m_Thread_recv = NULL; //接收线程句柄
 m_Thread_conn = NULL; //连接线程句柄
 memset(m_SendBuf, 0, MAX_BUF_LENGTH * sizeof(char));
 memset(m_RecvBuf, 0, MAX_BUF_LENGTH * sizeof(char));

 //SOCKET在连接失败的时候,需要重新建才可以。所以放去 connet前面
 //WORD wVersionRequest;
 //WSADATA wsaData;
 //wVersionRequest = MAKEWORD(2, 2); //版本2.2
 //int err = WSAStartup(wVersionRequest, &wsaData);
 //if (err != 0) //WSAStartup错误
 // return false;
 //if (LOBYTE(wsaData.wVersion) != 2 || LOBYTE(wsaData.wVersion) != 2) {
 // WSACleanup(); //版本不对,清理退出
 // return false;
 //}
 //m_Socket_Client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 //if (m_Socket_Client == INVALID_SOCKET) //socket创建失败
 // return false;
 
 //unsigned long ul = 1;
 //err = ioctlsocket(m_Socket_Client, FIONBIO, &ul); //设置套接字非阻塞模式
 //if (err == SOCKET_ERROR)
 // return false;

 return true;
}

/***
 释放XTcpClient的资源 ReleaseClient
***/
void XTcpClient::ReleaseClient() {
 DeleteCriticalSection(&m_cs_send);
 CloseHandle(m_Thread_send);
 CloseHandle(m_Thread_recv);
 //CloseHandle(m_Thread_conn);//这个在conn的时候就释放了
 closesocket(m_Socket_Client);
 CloseHandle(m_send_event);
 CloseHandle(m_recv_event);
 WSACleanup();
}

/***
 创建连接线程 ConnectServer
***/
bool XTcpClient::ConnectServer() {
 printf("%s--%d--\n", m_ServerIP, m_ServerPort);
 printf("准备创建连接线程。\n");
 ShowMessage(_T("准备创建连接线程"));
 m_Thread_conn = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)ConnThread, (void*)this, 0, NULL);
 bool isok = (m_Thread_conn == NULL) ? false : true;
 CloseHandle(m_Thread_conn);
 m_Thread_conn = NULL;
 return isok;
}

/***
 连接线程函数 ConnThread
***/
DWORD WINAPI XTcpClient::ConnThread(void* pvParam) { 
 printf("进入连接线程。\n");
 XTcpClient *cli = (XTcpClient*)pvParam;
 cli->ShowMessage(_T("进入连接线程"));
 //SOCKET的初始化,也放在这里了:
 WORD wVersionRequest;
 WSADATA wsaData;
 wVersionRequest = MAKEWORD(2, 2); //版本2.2
 int err = WSAStartup(wVersionRequest, &wsaData);
 if (err != 0) //WSAStartup错误
 return false;
 if (LOBYTE(wsaData.wVersion) != 2 || LOBYTE(wsaData.wVersion) != 2) {
 WSACleanup(); //版本不对,清理退出
 return false;
 }
 cli->m_Socket_Client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 if (cli->m_Socket_Client == INVALID_SOCKET) //socket创建失败
 return false;

 //获取窗口控件的 服务器ip和port
 SOCKADDR_IN serverAddr;
 serverAddr.sin_family = AF_INET;
 serverAddr.sin_port = htons(cli->m_ServerPort);
 serverAddr.sin_addr.S_un.S_addr = inet_addr(cli->m_ServerIP);
 int i = 0;
 TCHAR strShow[256];
 while (i < 5) {
 printf("第%d次尝试连接...\n",i);
 wsprintf(strShow, _T("第%d次尝试连接..."), i);
 cli->ShowMessage(strShow);
 int res = connect(cli->m_Socket_Client, (SOCKADDR*)&serverAddr, sizeof(SOCKADDR));
 if (res == 0) {
 cli->m_isConnected = true;
 break;
 }
 else {
 cli->m_isConnected = false;
 ++i;
 Sleep(1000);
 }
 }
 if (cli->m_isConnected) {
 printf("连接ok。\n");
 cli->ShowMessage(_T("连接服务器成功!"));
 //允许收发数据和断开连接,禁止再连接,这个要放在 创建收发线程成功以后
 //创建 收发线程,是不是先要判断这2个线程已经启动了?在创建函数里面判断
 cli->CreateSendRecvThread();
 SetEvent(cli->m_recv_event); //触发接收事件
 }
 else {
 printf("连接error。\n");
 cli->ShowMessage(_T("连接服务器失败!"));
 //不允许收发数据,允许再连接
 EnterCriticalSection(&cli->m_cs_send); //关闭socket用临界区
 if (cli->m_Socket_Client != INVALID_SOCKET) {
 closesocket(cli->m_Socket_Client);
 WSACleanup();
 cli->m_Socket_Client = INVALID_SOCKET;
 }
 LeaveCriticalSection(&cli->m_cs_send);

 EnableWindow(GetDlgItem(cli->m_hWnd, 2002), FALSE);
 EnableWindow(GetDlgItem(cli->m_hWnd, 2003), FALSE);
 EnableWindow(GetDlgItem(cli->m_hWnd, 2001), TRUE);
 }
 printf("退出连接线程。\n");
 cli->ShowMessage(_T("退出连接线程!"));
 return 0;
}

/***
 创建收和发的线程 CreateSendRecvThread
***/
bool XTcpClient::CreateSendRecvThread() { 
 //希望:收发线程创建以后在断开连接的时候不销毁。退出程序时销毁。
 //所以,首先要判断是否已经创建该线程。当然,线程除非被人为退出,正常情况不会退出
 printf("创建收发线程...\n");
 ShowMessage(_T("判断是否需要创建收发线程。。"));
 if (m_Thread_send != NULL && WaitForSingleObject(m_Thread_send, 0) == WAIT_OBJECT_0) {
 //进入这里,表示线程曾经创建,但是已经退出了,需要重新创建
 ShowMessage(_T("Send线程已经关闭,需要重新创建!"));
 CloseHandle(m_Thread_send);
 m_Thread_send = NULL;
 }
 if (m_Thread_send == NULL)
 m_Thread_send = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)SendThread, (void*)this, 0, NULL);
 if (m_Thread_send == NULL) {
 ShowMessage(_T("Send线程创建失败!"));
 return false;
 }
 ShowMessage(_T("Send线程创建成功!"));
 printf("Send线程创建成功!\n");
 if (m_Thread_recv != NULL && WaitForSingleObject(m_Thread_recv, 0) == WAIT_OBJECT_0) {
 //进入这里,表示线程曾经创建,但是已经退出了,需要重新创建
 ShowMessage(_T("Recv线程已经关闭,需要重新创建!"));
 CloseHandle(m_Thread_recv);
 m_Thread_recv = NULL;
 }
 if (m_Thread_recv == NULL)
 m_Thread_recv = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)RecvThread, (void*)this, 0, NULL);
 if (m_Thread_recv == NULL) {
 ShowMessage(_T("Recv线程创建失败!"));
 return false;
 }
 ShowMessage(_T("Recv线程创建成功!"));
 printf("Recv线程创建成功!\n");
 EnableWindow(GetDlgItem(m_hWnd, 2002), TRUE);
 EnableWindow(GetDlgItem(m_hWnd, 2003), TRUE);
 return true;
}

/***
 发送线程 SendThread
***/
DWORD WINAPI XTcpClient::SendThread(void* pvParam) {
 //这里还需要加入断线的处理??
 printf("SendThread first in...\n");
 XTcpClient *cli = (XTcpClient*)pvParam;
 cli->ShowMessage(_T("第一次进入Send线程。"));
 TCHAR strShow[4096];
 while (1) {
 WaitForSingleObject(cli->m_send_event, INFINITE); //等待send事件被触发,每次send按钮点下就触发
 if (cli->m_isQuit)
 break;
 if (!cli->m_isConnected) //非连接状态,跳过循环
 continue;
 EnterCriticalSection(&cli->m_cs_send); //进入临界区,锁定发送buf
 if (cli->m_isSend && strlen(cli->m_SendBuf) > 0) {
 printf("Begin Send...\n");
 int res = send(cli->m_Socket_Client, cli->m_SendBuf, strlen(cli->m_SendBuf) + 1, 0);
 if (res == SOCKET_ERROR) {
 printf("Send error...\n");
 cli->ShowMessage(_T("Send Error!"));
 }
 else {
 printf("Send ok...\n");
 cli->ShowMessage(_T("Send->"));
 //转换 多字节到unicode
 MultiByteToWideChar(CP_ACP,0, cli->m_SendBuf, strlen(cli->m_SendBuf),strShow, strlen(cli->m_SendBuf));
 strShow[strlen(cli->m_SendBuf)] = '\0';
 cli->ShowMessage(strShow);
 }
 }
 cli->m_isSend = false;
 LeaveCriticalSection(&cli->m_cs_send); //退出临界区
 printf("发送线程进入等待...\n");
 cli->ShowMessage(_T("Send Thread 进入等待..."));
 }
 printf("Send Thread quit...\n");
 cli->ShowMessage(_T("Send Thread QUIT..."));
 return 0;
}
/***
 接收线程 RecvThread
***/
DWORD WINAPI XTcpClient::RecvThread(void* pvParam) {
 printf("Recv Thread First in...\n");
 XTcpClient *cli = (XTcpClient*)pvParam;
 cli->ShowMessage(_T("Recv线程第一次进入。。"));
 TCHAR strShow[4096];
 while (1) {
 WaitForSingleObject(cli->m_recv_event,INFINITE); //等待接收手动事件
 if (cli->m_isQuit) //退出线程
 break;
 if (!cli->m_isConnected) {
 ResetEvent(cli->m_recv_event); //设置手动接收事件为为触发状态
 continue;
 }
 cli->ShowMessage(_T("接收线程准备接收数据..."));
 memset(cli->m_RecvBuf, 0, sizeof(cli->m_RecvBuf)); //清空接收buf
 int res = recv(cli->m_Socket_Client, cli->m_RecvBuf, sizeof(cli->m_RecvBuf), 0);
 if (res == SOCKET_ERROR || res==0) {
 printf("接收失败...\n");
 cli->ShowMessage(_T("接收失败..."));
 cli->ShowMessage(_T("连接中断了...请重新连接服务器"));
 ResetEvent(cli->m_recv_event); //设置接收为非触发状态
 cli->m_isConnected = false;

 EnterCriticalSection(&cli->m_cs_send); //关闭socket用临界区???
 if (cli->m_Socket_Client != INVALID_SOCKET) {
 closesocket(cli->m_Socket_Client);
 WSACleanup();
 cli->m_Socket_Client = INVALID_SOCKET;
 }
 LeaveCriticalSection(&cli->m_cs_send);

 EnableWindow(GetDlgItem(cli->m_hWnd, 2001), TRUE);
 EnableWindow(GetDlgItem(cli->m_hWnd, 2002), FALSE);
 EnableWindow(GetDlgItem(cli->m_hWnd, 2003), FALSE);
 }
 //if (res == 0) { //连接中断了
 // printf("接收的时候,发现连接中断了...\n");
 // cli->ShowMessage(_T("接收的时候,发现连接中断了......"));
 // cli->m_isConnected = false;
 //}
 else {
 printf("显示:接收的信息...\n");
 printf("接收的信息:%s\n", cli->m_RecvBuf);
 cli->ShowMessage(_T("Recv->"));
 //转换 多字节到unicode
 MultiByteToWideChar(CP_ACP, 0, cli->m_RecvBuf, strlen(cli->m_RecvBuf), strShow, strlen(cli->m_RecvBuf));
 strShow[strlen(cli->m_RecvBuf)] = '\0';
 cli->ShowMessage(strShow);
 }
 }
 printf("接收线程退出...\n");
 cli->ShowMessage(_T("接收线程退出..."));
 return 0;
}

 

 

 

 

 

 

 

 

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

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

分享到:更多 ()

评论 抢沙发

评论前必须登录!