MFC 基于SOCKET 实现服务端客户端一对多

11 篇文章 1 订阅
1 篇文章 0 订阅
这是一个关于C++编程的示例,详细介绍了如何创建一个能够处理多个客户端连接的服务器,并且客户端可以与服务器进行数据交换。代码实现了基于socket的异步非阻塞通信,同时处理了UNICODE和多字符集的字符转换问题,确保在不同字符集环境下数据传输的正确性。
摘要由CSDN通过智能技术生成

注意:本篇文章源代码贴得很多,所以有点长,如果觉得阅读不方便,可以直接复制源代码到你的程序里去,也可以直接下载源码。后面整合了socket方法写了一个类,源码下载里没有。

源码下载:https://download.csdn.net/download/ya4599/15653688

1.程序说明

开发环境是VS2008 ,基于socket实现一个服务端和多个客户端通信。

服务端:

指定端口,关闭和开启服务器监听;

将已连接客户端显示在客户端CListBox列表;

监听FD_ACCEPT|FD_READ|FD_CLOSE事件;

可选择和特定的已连接客户端通信;

通信数据和系统信息滚动显示。

客户端:

指定服务器IP、端口,关闭和连接服务器;

监听FD_CONNECT|FD_READ|FD_CLOSE事件,

顺便做了一个测试程序,测试程序作用是循环打开指定个数的客户端和关闭客户端。

服务端界面截图如下:

 

客户端界面截图如下:

 

测试程序截图如下:

2.服务端主要代码:

 MServerDlg.h 

// MServerDlg.h : 头文件


#pragma once
#include "afxwin.h"
#include <string>
using namespace std;

public:
	CListBox mClientList;//显示 客户端主机名——IP:PORT
	SOCKET socket_server;//服务器套接字
	SOCKET sClient;		//当前连接的客户端套接字
	sockaddr_in sAddrin_server;
	sockaddr_in sAddrin_client;
	BOOL isServerStart;//服务器是否启动
	CMap<SOCKET, UINT,CString, LPCTSTR> clientMap;//客户端集合  key SCOKET value 主机名__IP:PORT
	int iClientCount;
	// 显示信息
	CString mMsg;
	LRESULT OnSocket(WPARAM wParam, LPARAM lParam);
	// 有客户端连接
	void OnAccept(SOCKET s);
	void OnRead(SOCKET s);
	void OnCloseConnect(SOCKET s);
	void SendMsgToClient(SOCKET s,CString msg_unicode);
	UINT uPort;      //服务器端口
	CString strHostIp; //本机IP
	CString strHostName;//本机名
	BOOL firstTimeRead;//某个套接字第一次收到信息
	afx_msg void OnBnClickedButtonSendmsgtoclient();//发送信息到客户端
	afx_msg void OnBnClickedButtonClose();//断开某个连接
	// UNICEDE 字符集下CString (w_char*)转char*
	string Wchar_tToChar_UnicodeToAnsi(CString unicodeStr);
	// UNICODE字符集下char*转CString(w_char)
	CString CharToWchar_t_AnsiToUnicode(char* ansiChar);
	// 关闭客户端操作
	int CloseClient(SOCKET s, int itemIndex);
	//显示信息并让Edit滚动到内容末尾
	void SetEditShowLastedLine(CString sMsg,int iSize);
	void SetEditShowLastedLine(CString sMsg);

	CEdit mMsgEditCtrl;//信息显示CEdit句柄
	afx_msg void OnLbnSelchangeListClient();//ListBox选择项改变
	// 获取本机IP
	CString GetMyHostIP(void);
	afx_msg void OnBnClickedButtonStart();//启动、关闭服务器
	afx_msg void OnBnClickedButtonClean();//清空信息
};

MServerDlg.cpp

// MServerDlg.cpp : 实现文件
//头文件包含
#include "stdafx.h"
#include "MServer.h"
#include "MServerDlg.h"
#include <windows.h> //一定要包含该头文件
#include  < locale.h > 
#pragma comment(lib, "WS2_32.lib")  //windwows下的socket编程函数库
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define WM_SOCKET  WM_USER+1  //自定义消息
#define WSAGETSELECTEVENT(lParam) LOWORD(lParam)
#define WSAGETSELECTERRO(lParam) HIWORD(lParam)
#define MAX_CLIENT 121


BEGIN_MESSAGE_MAP(CMClientDlg, CDialog)
	
	ON_MESSAGE(WM_SOCKET,CMClientDlg::OnSocket)//自定义消息,绑定WSAAsyncSelect
	
END_MESSAGE_MAP()


//启动、关闭服务器
void CMServerDlg::OnBnClickedButtonStart()
{
	// TODO: 在此添加控件通知处理程序代码
	GetDlgItemTextW(IDC_EDIT_IP,strHostIp);//获取服务器IP
	uPort=GetDlgItemInt(IDC_EDIT_PORT);//获取服务器端口
	if(!isServerStart)//服务器未启动,进行启动操作
	{
		WSADATA wsaData;
		WORD wVersion=MAKEWORD(2,2);
		if(WSAStartup(wVersion,&wsaData)!=0)
		{
			SetEditShowLastedLine(_T("系统:初始化winsock失败!\r\n"));
			return ;
		}
		sAddrin_server.sin_family=AF_INET;
		sAddrin_server.sin_port=htons(uPort);
		string sIp=Wchar_tToChar_UnicodeToAnsi(strHostIp);//IP  CString UNICODE 转ANSI string
		sAddrin_server.sin_addr.S_un.S_addr=inet_addr(sIp.c_str());//string转char*
		socket_server=::socket(AF_INET,SOCK_STREAM,0);
		::bind(socket_server,(sockaddr*)&sAddrin_server,sizeof(sAddrin_server));//端口绑定
		SetEditShowLastedLine(_T("系统:服务器监听启动!\r\n"));
		::listen(socket_server,5);//启动监听
		::WSAAsyncSelect(socket_server,this->m_hWnd,WM_SOCKET,FD_ACCEPT);//非阻塞模式,监听连接事件
		SetDlgItemTextW(IDC_BUTTON_START,_T("关闭服务器"));
		isServerStart=TRUE;
	}else//服务器已经启动,进行关闭操作
	{
		closesocket(socket_server);//关闭服务器套接字
		::WSACleanup();
		//关闭所有客户端
		POSITION pos = clientMap.GetStartPosition();
		while(pos!=NULL)
		{
			UINT uKey;
			CString sVal;
			clientMap.GetNextAssoc(pos,uKey,sVal);
			closesocket(uKey);
		}
		isServerStart=FALSE;
		clientMap.RemoveAll();//清空CMap
		mClientList.ResetContent();//清空CListBox
		iClientCount=0;
		SetDlgItemInt(IDC_EDIT_CLIENTCOUNT,iClientCount);//客户端连接数量显示
		SetEditShowLastedLine(_T("系统:服务器监听关闭!\r\n"));
		SetDlgItemTextW(IDC_BUTTON_START,_T("启动服务器"));//服务器启动、关闭按钮
	}
}

//监听套接字感兴趣的事件,WM_USER+1 消息函数
LRESULT CMServerDlg::OnSocket(WPARAM wParam, LPARAM lParam)
{
	if(WSAGETSELECTERRO(lParam))
	{
		//删除clientMap对应套接字
		OnCloseConnect(wParam);
		CString strErr;
		clientMap.Lookup(wParam,strErr);
		strErr.Format(_T("系统:%s异常断开,错误码=%d\r\n"),strErr,WSAGETSELECTERRO(lParam));
		SetEditShowLastedLine(strErr);
		return -1;

	}
	TRACE(_T("wParam=%d\n"),wParam);
	switch(WSAGETSELECTEVENT(lParam))
	{
	case FD_ACCEPT:
		OnAccept(wParam);
		break;
	case FD_READ:
		OnRead(wParam);
		break;
	case FD_CLOSE:
		OnCloseConnect(wParam);
		break;
	case FD_WRITE:
		break;
	default:
		break;
	
	}
	return 0;
}



// 有客户端连接  当接收连接请求时触发事件
void CMServerDlg::OnAccept(SOCKET s)//参数为服务器本地套接字
{
	firstTimeRead=TRUE;//第一次接受数据,客户端连接成功后会立马发送客户端主机名给服务器
	int len=sizeof(sAddrin_client);
	sClient=accept(s,(sockaddr*)&sAddrin_client,&len);//获取客户端套接字,ip,端口   
	::WSAAsyncSelect(sClient,this->m_hWnd,WM_SOCKET,FD_READ|FD_WRITE|FD_CLOSE);//重新设置该客户端监听的消息
	CString str;
	char* p_cIp=::inet_ntoa(sAddrin_client.sin_addr);
	str.Format(_T("%s,%s:%d"),
		strHostName,//服务器主机名
		CharToWchar_t_AnsiToUnicode(p_cIp),//客户端ip
		sAddrin_client.sin_port);//客户端端口 
	
	//将服务器主机名,客户端ip,端口  套接字值发送给客户端,方便客户端获取主机名和自己的端口
	//为了不显示在信息里,所以没用SendMsgToClient(SOCKET s,CString msg_unicode)
	int slen=(str.GetLength() + 1) * sizeof(TCHAR);
	::send(sClient,(const char *) (LPCTSTR) str, slen,0);
}

/************************
作用:发送数据给客户端
SOCKET s  对应客户端套接字
CString msg_unicode 用户输入的信息
******************************/
void CMServerDlg::SendMsgToClient(SOCKET s,CString msg_unicode)
{
	int len=(msg_unicode.GetLength() + 1) * sizeof(TCHAR);
	if (::send(s,(const char *) (LPCTSTR) msg_unicode, len,0)!= SOCKET_ERROR)
	{
		CString str;
		str.Format(_T("发送:%s\r\n"),msg_unicode);
		SetEditShowLastedLine(str,len);
	}
	else
	{
		SetEditShowLastedLine(_T("系统:消息发送失败!\r\n"));
	}
}


// 当套接字中有数据需要读取时触发事件
void CMServerDlg::OnRead(SOCKET s)//参数为对应客户端套接字
{
	//通过套接字从clientMap获取客户端主机名和IP端口
	//然后使对应客户端在clientList并使其能看见
	CString sClient;
	if(clientMap.Lookup(s,sClient)==TRUE)
	{
		int index=mClientList.FindString(0,sClient);
		mClientList.SetTopIndex(index);//可见
	}
	void * buff=0;
	int size =0;
	while(TRUE)
	{
		char rcvBuff[128];
		int realLen=::recv(s,rcvBuff,123,0);
		if(realLen<=0)
		{
			break;
		}else
		{
			int new_size=size+realLen;
			buff=realloc(buff,new_size);
			memcpy((void*)(((char*)buff)+size),rcvBuff,realLen);
			size=new_size;
		}
	}
	if(size==0)//没接收到内容
	{
		return;
	}
	CString mstr;
	if(firstTimeRead)//第一次接收的肯定是对应客户端主机名
	{
		firstTimeRead=FALSE;
		char* p_cIp=::inet_ntoa(sAddrin_client.sin_addr);
		mstr.Format(_T("%s__%s:%d"),
					((LPCTSTR)buff),CharToWchar_t_AnsiToUnicode(p_cIp),sAddrin_client.sin_port);;
		mClientList.AddString(mstr);
		mClientList.SetItemData(iClientCount,s);//将SOCKET设置为客户端列表项的itemdata,
		mClientList.SetCurSel(iClientCount++);//高亮IDC_EDIT_CLIENTCOUNT
		SetDlgItemInt(IDC_EDIT_CLIENTCOUNT,iClientCount);
		//listbox滚动
		clientMap.SetAt(s,mstr);
		mClientList.SetTopIndex(iClientCount>13?iClientCount-13:iClientCount);
		SetEditShowLastedLine(_T("系统:")+mstr+_T("已连接\r\n"));
		
	}else//正常的客户端发过来的信息
	{
		clientMap.Lookup(s,mstr);
		mClientList.SetCurSel(mClientList.FindString(0,mstr));
		mstr.Format(_T("接收:%s\r\n"),(LPCTSTR)buff);
		SetEditShowLastedLine(mstr,size);
	}
	free(buff);
}


//  关闭客户端连接 当套接字关闭时触发事件
void CMServerDlg::OnCloseConnect(SOCKET s)//参数为对应客户端套接字
{
	for(int i=0;i<iClientCount;i++)
	{
		USHORT itemData=(USHORT)mClientList.GetItemData(i);
		if(itemData==s)
		{
			CloseClient(s,i);
			break;
		}
	}
}

//发送信息到客户端,
//默认的客户端是最后连接的客户端sClient
void CMServerDlg::OnBnClickedButtonSendmsgtoclient()
{
	CString str_send=_T(""); 
	GetDlgItemTextW(IDC_EDIT_MSG,str_send);
	if( str_send==_T(""))
	{
		return;
	}
	TRACE(_T("sClient=%d\n"),sClient);
	SendMsgToClient(sClient,str_send);
}


/*******************
// 关闭客户端操作
SOCKET s, 客户端套接字
int itemIndex 客户端在clientList下标
******************/
int CMServerDlg::CloseClient(SOCKET s, int itemIndex)
{
	CString sItemText=_T("");
	mClientList.GetText(itemIndex,sItemText);
	SetEditShowLastedLine(_T("系统:")+sItemText+_T("已断开\r\n"));
	clientMap.RemoveKey(s);
	mClientList.DeleteString(itemIndex);
	iClientCount--;
	SetDlgItemInt(IDC_EDIT_CLIENTCOUNT,iClientCount);
	return closesocket(s);
}


// UNICEDE 字符集下CString (w_char*)转char*
string CMServerDlg::Wchar_tToChar_UnicodeToAnsi(CString unicodeStr)
{
	wchar_t * mWchar=unicodeStr.GetBuffer(unicodeStr.GetLength());
	int iLen=WideCharToMultiByte(CP_ACP,0,mWchar,-1,NULL,0,NULL,NULL);
	if(iLen==0)
	{
		unicodeStr.ReleaseBuffer();
		return NULL;
	}
	char *pResultChar=new char[iLen+1];
	WideCharToMultiByte( CP_ACP, 0, mWchar, -1, pResultChar, iLen, NULL, NULL ); 
	pResultChar[iLen]='\0';
	string str=pResultChar;
	delete[]pResultChar;
	return str;
}

// UNICODE字符集下char*转CString(w_char)
CString CMServerDlg::CharToWchar_t_AnsiToUnicode(char* ansiChar)
{
	int iLen = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, ansiChar, -1, NULL, 0 );  
    if (iLen == 0)  
    {  
        return _T("");  
    }  
    wchar_t* pResult = new wchar_t[iLen+1];  
    MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, ansiChar, -1, pResult, iLen );  
	pResult[iLen]='\0';
    CString  strResult(pResult);
	delete []pResult;
	return strResult;
}

 

3客户端主要代码

MClientDlg.h

// MClientDlg.h : 头文件
//

#pragma once
#include "afxwin.h"
#include <string>

using namespace std;




public:
	SOCKET socket_client;
	sockaddr_in sAddrin_server;
	sockaddr_in sAddrin_client;
	BOOL IsConnected;
	BOOL firstTimeRead;
	// 显示信息
	CString mMsg;
	LRESULT OnSocket(WPARAM wParam, LPARAM lParam);
	void OnRead(SOCKET s);
	void OnCloseConnect(SOCKET s);
	void SendMsgToServer(SOCKET s,CString msg_unicode);
	CString strHostName;
	CString strHostIp;
	UINT uHostPort;

	// 服务器主机名
	CString strServerHostName;
	// 服务器IP
	CString strIp;
	// 服务器端口
	UINT uPort;
	afx_msg void OnBnClickedButtonConnect();
	// UNICEDE 字符集下CString (w_char*)转char*
	string Wchar_tToChar_UnicodeToAnsi(CString unicodeStr);
	// UNICODE字符集下char*转CString(w_char)
	CString CharToWchar_t_AnsiToUnicode(char* ansiChar);
	void SetEditShowLastedLine(CString sMsg,int size);
	void SetEditShowLastedLine(CString sMsg);
	CEdit mMsgEditCtrl;
	afx_msg void OnBnClickedButtonSend();
	CString GetMyHostIP(void);

	afx_msg void OnBnClickedButtonClean();
};

MClientDlg.cpp

// MClientDlg.cpp : 实现文件
//头文件

#include "stdafx.h"
#include "MClient.h"
#include "MClientDlg.h"
#include <windows.h> //一定要包含该头文件
#include  < locale.h > 
#pragma comment(lib, "WS2_32.lib")  //windwows下的socket编程函数库
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define WM_SOCKET  WM_USER+1
#define WSAGETSELECTEVENT(lParam) LOWORD(lParam)
#define WSAGETSELECTERRO(lParam) HIWORD(lParam)

BEGIN_MESSAGE_MAP(CMClientDlg, CDialog)
	
	ON_MESSAGE(WM_SOCKET,CMClientDlg::OnSocket)//自定义消息,绑定WSAAsyncSelect
	
END_MESSAGE_MAP()


// CMClientDlg 消息处理程序

BOOL CMClientDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	setlocale(LC_ALL, "zh-CN");//设定中文
	
	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}


LRESULT CMClientDlg::OnSocket(WPARAM wParam, LPARAM lParam)
{
	if(WSAGETSELECTERRO(lParam))
	{
		//删除clientMap对应套接字
		OnCloseConnect(wParam);
		CString strErr;
		strErr.Format(_T("系统:%s异常断开,错误码=%d\r\n"),strErr,WSAGETSELECTERRO(lParam));
		SetEditShowLastedLine(strErr);
		return -1;

	}
	switch(WSAGETSELECTEVENT(lParam))
	{
	
		case FD_READ:
			OnRead(wParam);
			break;
		case FD_CLOSE:
			OnCloseConnect(wParam);
			break;
		default:
		break;
	
	}
	return 0;
}



void CMClientDlg::SendMsgToServer(SOCKET s,CString msg_unicode)
{
	int len=(msg_unicode.GetLength() + 1) * sizeof(TCHAR);
	if (::send(s,(const char *) (LPCTSTR) msg_unicode, len,0)!= SOCKET_ERROR)
	{
		CString str;
		str.Format(_T("发送:%s\r\n"),msg_unicode,len);
		SetEditShowLastedLine(str,len);
	}
	else
	{
		SetEditShowLastedLine(_T("系统:消息发送失败!\r\n"));
	}
}

//连接、断开服务器
void CMClientDlg::OnBnClickedButtonConnect()
{
	// TODO: 在此添加控件通知处理程序代码
	if(IsConnected)
	{
		if(!closesocket(socket_client))
		{
			::WSACleanup();
			IsConnected=FALSE;
			firstTimeRead=TRUE;
			socket_client=INVALID_SOCKET;
			SetDlgItemTextW(IDC_BUTTON_CONNECT,_T("连接服务器"));
			GetDlgItem(IDC_EDIT_SENDMSG)->EnableWindow(TRUE);
			GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(TRUE);
			SetEditShowLastedLine(_T("系统:服务器已经断开!\r\n"));
		}else
		{
			CString tip;
			tip.Format(_T("系统:服务器断开失败,错误码%d\r\n"),WSAGetLastError());
			SetEditShowLastedLine(tip);
		}
	}else
	{
		GetDlgItemTextW(IDC_EDIT_IP,strIp);
		uPort=GetDlgItemInt(IDC_EDIT_PORT,0,1);
		if(strIp==_T("")||uPort<1000)//端口号不小于1000
		{
			SetEditShowLastedLine(_T("系统:请设置服务器信息。\r\n服务器地址或端口号不正确!\r\n"));
			return;
		}
		WSADATA wsad;
		WORD wVersion=MAKEWORD(2,2);
		if(WSAStartup(wVersion,&wsad)!=0)
		{
			SetEditShowLastedLine(_T("系统:初始化winsock失败!\r\n"));
		}
		socket_client=::socket(AF_INET,SOCK_STREAM,0);
		//将套接字设置为异步模式
		sAddrin_server.sin_family=AF_INET;
		sAddrin_server.sin_port=htons(uPort);
		string sIp=Wchar_tToChar_UnicodeToAnsi(strIp);
		sAddrin_server.sin_addr.S_un.S_addr=inet_addr(sIp.c_str());
		if(0==::connect(socket_client,(sockaddr*)&sAddrin_server,sizeof(sAddrin_server)))
		{
			//将套接字设置为异步模式,如果此步在connect之前,则connect不需要判断,因为会一直返回1
			int k =::WSAAsyncSelect(socket_client,this->m_hWnd,WM_SOCKET,FD_READ|FD_WRITE|FD_CLOSE);
			IsConnected=TRUE;
			CString tip;
			tip.Format(_T("系统:连接服务器%s:%d成功!\r\n"),strIp,uPort);
			SetEditShowLastedLine(tip);
			SetDlgItemTextW(IDC_BUTTON_CONNECT,_T("断开服务器"));
			GetDlgItem(IDC_EDIT_SENDMSG)->EnableWindow(TRUE);
			GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(TRUE);
		}else
		{
			CString tip;
			tip.Format(_T("系统:连接服务器%s:%d失败!错误码=%d\r\n"),strIp,uPort,WSAGetLastError());
			SetEditShowLastedLine(tip);
		}
	}
	

}


//显示信息并让最后一行可见
void CMClientDlg::SetEditShowLastedLine(CString sMsg,int size)
{
	_SYSTEMTIME st;
	GetLocalTime(&st); 
	CString strTime;
	strTime.Format(_T("%02d:%02d:%02d:%03d  字节=%d\r\n"),st.wHour,st.wMinute,st.wSecond,st.wMilliseconds,size);
	sMsg=strTime+sMsg;
	mMsg+=sMsg;
	UpdateData(FALSE);
	mMsgEditCtrl.LineScroll(mMsgEditCtrl.GetLineCount());
}


void CMClientDlg::SetEditShowLastedLine(CString sMsg)
{
	_SYSTEMTIME st;
	GetLocalTime(&st); 
	CString strTime;
	strTime.Format(_T("%02d:%02d:%02d:%03d\r\n"),st.wHour,st.wMinute,st.wSecond,st.wMilliseconds);
	sMsg=strTime+sMsg;
	mMsg+=sMsg;
	UpdateData(FALSE);
	mMsgEditCtrl.LineScroll(mMsgEditCtrl.GetLineCount());
}


// 当套接字中有数据需要读取时触发事件
void CMClientDlg::OnRead(SOCKET s)//参数为对应客户端套接字
{
	void * buff=0;
	int size =0;
	while(TRUE)
	{
		char rcvBuff[128];
		int realLen=::recv(s,rcvBuff,123,0);
		if(realLen<=0)
		{
			break;
		}else
		{
			int new_size=size+realLen;
			buff=realloc(buff,new_size);
			memcpy((void*)(((char*)buff)+size),rcvBuff,realLen);
			size=new_size;
		}
	}
	if(size==0)//没接收到内容
	{
		return;
	}
	CString mstr;
	//第一次接收的肯定是IP:PORT,connect成功后服务器会将
	//服务器主机名,IP:端口发送回来, 例:LIUYAZHOU-2,192.168.1.147:58818
	if(firstTimeRead)
	{
		mstr=((LPCTSTR)buff);
		int index=mstr.Find(',',0);
		strServerHostName=mstr.Left(index);//获取服务器主机名
		index=mstr.Find(':',0);
		mstr=mstr.Right(mstr.GetLength()-1-index);//获取客户端所用端口
		uHostPort=_ttoi(mstr);
		mstr.Format(_T("%s__%s:%d"),strHostName,strHostIp,uHostPort);
		SetDlgItemTextW(IDC_STATIC_SERVERNAME,strServerHostName);
		SetWindowTextW(mstr);//显示到对话框标题
		//将客户端主机名发送给服务器(第一次发送数据给服务器)
		int slen=(strHostName.GetLength() + 1) * sizeof(TCHAR);
		::send(socket_client,(const char *) (LPCTSTR) strHostName, slen,0);
	}else//正常接收
	{
		mstr.Format(_T("接收:%s\r\n"),(LPCTSTR)buff);
		SetEditShowLastedLine(mstr,size);
	}
	free(buff);
	firstTimeRead=FALSE;
}




//  关闭客户端连接 当套接字关闭时触发事件
void CMClientDlg::OnCloseConnect(SOCKET s)//参数为对应客户端套接字
{
	if(!closesocket(s))
	{
		::WSACleanup();
		IsConnected=FALSE;
		firstTimeRead=TRUE;
		socket_client=INVALID_SOCKET;
		SetDlgItemTextW(IDC_BUTTON_CONNECT,_T("连接服务器"));
		GetDlgItem(IDC_EDIT_SENDMSG)->EnableWindow(TRUE);
		GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(TRUE);
		SetEditShowLastedLine(_T("系统:服务器主动断开连接!\r\n"));
	}else
	{
		CString tip;
		tip.Format(_T("系统:服务器断开失败,错误码%d\r\n"),WSAGetLastError());
		SetEditShowLastedLine(tip);
	}
		
}

//发送信息给服务器
void CMClientDlg::OnBnClickedButtonSend()
{
	// TODO: 在此添加控件通知处理程序代码
	CString sSendMsg;
	GetDlgItemTextW(IDC_EDIT_SENDMSG,sSendMsg);
	SendMsgToServer(socket_client,sSendMsg);	
}



// 获取本机IP 主机名
CString CMClientDlg::GetMyHostIP(void)
{
	char cName[128];
	gethostname(cName,128);
	HOSTENT* host=gethostbyname(cName);
	char *name=host->h_name;
	strHostName=CharToWchar_t_AnsiToUnicode(name);
	for(int i=0;;i++)
	{
		char *p =host->h_addr_list[i];
		if(NULL==p)
		{
			break;
		}
		struct in_addr addr; 
		memcpy(&addr, host->h_addr_list[i], sizeof(struct in_addr)); 
		strHostIp=CharToWchar_t_AnsiToUnicode(inet_ntoa(addr));
	}
	return strHostName;
}


// UNICEDE 字符集下CString (w_char*)转string string 转char* 用string::c_str();
string CMClientDlg::Wchar_tToChar_UnicodeToAnsi(CString unicodeStr)
{
	wchar_t * mWchar=unicodeStr.GetBuffer(unicodeStr.GetLength());
	int iLen=WideCharToMultiByte(CP_ACP,0,mWchar,-1,NULL,0,NULL,NULL);
	if(iLen==0)
	{
		unicodeStr.ReleaseBuffer();
		return NULL;
	}
	char *pResultChar=new char[iLen+1];
	WideCharToMultiByte( CP_ACP, 0, mWchar, -1, pResultChar, iLen, NULL, NULL ); 
	pResultChar[iLen]='\0';
	string str=pResultChar;
	delete []pResultChar;
	return str;
}

// UNICODE字符集下char*转CString(w_char)
CString CMClientDlg::CharToWchar_t_AnsiToUnicode(char* ansiChar)
{
	int iLen = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, ansiChar, -1, NULL, 0 );  
    if (iLen == 0)  
    {  
        return _T("");  
    }  
    wchar_t* pResult = new wchar_t[iLen+1];  
    MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, ansiChar, -1, pResult, iLen );
	pResult[iLen]='\0';
    CString  strResult(pResult);
	delete []pResult;
	return strResult;
}

因为客户端程序将客户端窗口名称改变了,格式为:主机名__IP:PORT  ,所以每个客户端的窗口名称都不一样,测试程序中FindWindow无法通过窗口名称获取到客户端句柄,然后关闭。

为了配合测试程序关闭客户端,需要给客户端注册一个类名方便找到客户端。

方法在这里:https://blog.csdn.net/erick08/article/details/8712783?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242

1:在MClient.rc文件中对话框属性里增加一行 CLASS  "CMClientDlg" ,当然类名自己取,记住就行。

//第一步:在MClient.rc文件中对话框属性里增加一行 CLASS "CMClientDlg" 
IDD_MCLIENT_DIALOG DIALOGEX 0, 0, 204, 186
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "MClient"
FONT 12, "MS Shell Dlg", 400, 0, 0x1
CLASS "CMClientDlg"   //就是这一行!!!!!!!!!!!!!!!!!!!!
BEGIN
    LTEXT           "",IDC_STATIC_SERVERNAME,57,9,99,8
    EDITTEXT        IDC_EDIT_IP,56,23,129,12,ES_AUTOHSCROLL
    EDITTEXT        IDC_EDIT_PORT,56,39,29,12,ES_AUTOHSCROLL
    PUSHBUTTON      "连接服务器",IDC_BUTTON_CONNECT,92,38,41,14
    LTEXT           "服务器IP",IDC_STATIC,7,25,38,8
    LTEXT           "服务器端口",IDC_STATIC,6,40,39,8
    EDITTEXT        IDC_EDIT_MSG,7,55,181,93,ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | WS_VSCROLL
    EDITTEXT        IDC_EDIT_SENDMSG,7,153,152,33,ES_MULTILINE | ES_WANTRETURN | WS_DISABLED | WS_VSCROLL
    PUSHBUTTON      "发送",IDC_BUTTON_SEND,164,164,32,14,WS_DISABLED
    LTEXT           "服务器主机名",IDC_STATIC,7,9,55,8
    PUSHBUTTON      "清空",IDC_BUTTON_CLEAN,162,38,28,14
END

2,在窗口完成初始化之前,对窗口类进行注册。在MClient.App    InitInstance()里添加如下代码,放在 SetRegistryKey(_T("应用程序向导生成的本地应用程序"));上面。

WNDCLASS wc;

	// Get the info for this class.
	// #32770 is the default class name for dialogs boxes.
	::GetClassInfo(AfxGetInstanceHandle(), L"#32770", &wc);

	// Change the name of the class.
	wc.lpszClassName = L"CMClientDlg"; //这里请再次注意,一定要保证和rc资源文件里保存的类名相同!
	// Register this class so that MFC can use it.
	AfxRegisterClass(&wc);

 3.测试程序主要代码


void COpenExeDlg::OnBnClickedButtonOpen()
{
	// TODO: 在此添加控件通知处理程序代码
	CFileDialog fileDlg(TRUE, _T("exe"), _T("*.exe"), OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("(*.exe)|*.exe"), NULL);  
	if (fileDlg.DoModal() == IDOK)    //弹出对话框  
	{  
		mFilePath.Format(_T("%s"),fileDlg.GetPathName());//得到完整的文件名和目录名拓展名 
		mFileName=fileDlg.GetFileName();
		int Which = mFilePath.Find(mFileName);
		mPath=mFilePath.Left(Which);
		UpdateData(FALSE);
	}  
}

void COpenExeDlg::OnEnChangeEditCount()
{
	uCount=GetDlgItemInt(IDC_EDIT_COUNT);
}

void COpenExeDlg::OnBnClickedButtonOpenexe()
{
	UINT num=uCount;
	while(num--)
	{
		WinExec(mFilePath, SW_SHOW);
	}
	//ShellExecuteW(NULL,_T("open"),mFilePath,NULL,NULL, SW_SHOWNORMAL);
}

void COpenExeDlg::OnBnClickedButtonCloseexe()
{
	// TODO: 在此添加控件通知处理程序代码
	HWND hWnd; 
	// 先得到想要关闭的窗口的句柄 
	// 比如用FindWindow 
	hWnd=FindWindowW(L"CMClientDlg",NULL);
	while(hWnd)
	{
		::PostMessage(hWnd,WM_CLOSE,0,0); 
		hWnd=FindWindowW(L"CMClientDlg",NULL);
	}
}

源码下载:https://download.csdn.net/download/ya4599/15653688

 

*********************************************************分割线*************************************************************

自己整合了一下,将socket相关功能集合到一个类,包括服务器和客户端的读写功能

还有区分了字符集问题,多字符集和UNICODE都能用,不会乱码,类如下

MySocket.h

#pragma once
#include <string>
#include <windows.h> //一定要包含该头文件
#pragma comment(lib, "WS2_32.lib")  //windwows下的socket编程函数库
using namespace std;

#define WSAGETSELECTEVENT(lParam) LOWORD(lParam)//获取socket事件
#define WSAGETSELECTERRO(lParam) HIWORD(lParam)//获取socket错误码

#define SERVER_SOCKET_EVENT   FD_READ|FD_CLOSE|FD_WRITE
#define CLIENT_SOCKET_EVENT   FD_READ|FD_CLOSE|FD_WRITE|FD_CONNECT

typedef struct ClientInfo
{
	CString sClientIp;//客户端IP
	CString sClientHostName;//客户端主机名
	UINT uClientPort;//客户端使用的端口
}CLIENT_INFO,*LPCLIENT_INFO;

class MySocket
{
public:
	MySocket(void);
	virtual ~MySocket(void);
private:
	sockaddr_in sAddrin_server;
	sockaddr_in sAddrin_client;
	//WSAAsyncSelect监听socket消息绑定的窗口句柄
	HWND hBindWnd;
public:
	SOCKET sServer;//服务器套接字
	SOCKET sClient;//当前客户端套接字
	//本机IP
	CString sHostIp;
	//本机主机名
	CString sHostName;
public://字符集转换函数
#ifdef _UNICODE
	//UNICODE CString转string
	string Wchar_tToChar_UnicodeToAnsi(CString unicodeStr);
	//char 转CString
	CString CharToWchar_t_AnsiToUnicode(char* ansiChar);
#endif


public:
	/*********************************************
	*公共功能函数 
	**********************************************/
	//赋值hBindWnd
	HWND SetBandWindowHwnd(HWND hwnd);
	// 获取本机IP 
	CString GetMyHostIP(void);
	// 获取本机主机名
	CString GetMyHostName(void);
	//接收数据
	LPVOID OnRead(SOCKET s,int&rcvLen);
	// 接收到数据,返回CString
	CString OnReadString(SOCKET s,int &rcvLen);
	//接受数据,返回string
	std::string OnReadChar(SOCKET s,int&rcvLen);//参数为对应客户端套接字
	//关闭套接字
	int CloseMySocket(SOCKET s);
	//发送数据CString
	int SendMsg(SOCKET s,CString msg_unicode);
	//发送数据char* 
	int SendMsg(SOCKET s,char* pBuff);
	//发送主机名
	int SendHostname(SOCKET s);
public:
	/*********************************************
	*服务器相关函数和变量
	**********************************************/
	//已连接的客户端数量
	int iClientCount;
	//客户端集合  key 客户端SCOKET   value ClientInfo
	CMap<SOCKET, UINT,CLIENT_INFO, CLIENT_INFO> clientMap;
	//指定clientMap哈希表大小
	void InitClientMapHashTable(UINT nHashValue);
	// 初始化socket,开启服务器,启动服务器监听
	int InitServerSocket(HWND hWnd, CString strHostIp,u_short uPort,u_int wMsg,long iEvent);
	// 关闭服务器
	int CloseServerSocket(void);
	// 有客户端连接
	int OnAccept(SOCKET s,u_int wMsg,long iEvent);
	//有客户端断开连接
	int ServerCloseClientConnect(SOCKET s);
	//发送连接客户端端口给客户端
	int Send_clientPort_ToClient(SOCKET s,sockaddr_in addrClient);

public:
	/*********************************************
	*客户端相关函数和变量 包括:
	****************************************/
	//客户端初始化
	int initClientSocket(HWND hWnd);
	//客户端连接服务器
	int ConnectServer(CString strIp,UINT uPort,u_int wMsg);
	//客户端连接服务器成功
	int OnConnect(SOCKET s,u_int wMsg,long iEvent);
	//关闭客户端
	int CloseClient(void);
};

MySocket.cpp 

 

#include "StdAfx.h"
#include "MySocket.h"
#include  < locale.h > 


MySocket::MySocket(void)
{
	sClient=0;
	sServer=0;
	memset(&sAddrin_server,0, sizeof(sAddrin_server));
	memset(&sAddrin_client,0, sizeof(sAddrin_client));
	GetMyHostIP();
	GetMyHostName();
	iClientCount=0;//已连接的客户端数量
	setlocale(LC_ALL, "chs");//设定中文

}

MySocket::~MySocket(void)
{
}


#ifdef _UNICODE
/********************************************
*作用:UNICODE字符集下char*转CString(w_char)
*参数:char * ansiChar  二进制字符串指针
*返回值:strResult UNICODE  CString字符串   
********************************************/
CString MySocket::CharToWchar_t_AnsiToUnicode(char* ansiChar)
{
	int iLen = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, ansiChar, -1, NULL, 0 );  
    if (iLen == 0)  
    {  
        return _T("");  
    }  
    wchar_t* pResult = new wchar_t[iLen+1];  
    MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, ansiChar, -1, pResult, iLen );  
	pResult[iLen]='\0';
    CString  strResult(pResult);
	delete []pResult;
	return strResult;
}

/********************************************
*作用:UNICEDE 字符集下CString (w_char*)转char*
*参数:char * ansiChar  二进制字符串指针
*返回值:strResult UNICODE  string字符串   string转char* 用 sIp.c_str();
********************************************/
string MySocket::Wchar_tToChar_UnicodeToAnsi(CString unicodeStr)
{
	wchar_t * mWchar=unicodeStr.GetBuffer(unicodeStr.GetLength());
	int iLen=WideCharToMultiByte(CP_ACP,0,mWchar,-1,NULL,0,NULL,NULL);
	if(iLen==0)
	{
		unicodeStr.ReleaseBuffer();
		return NULL;
	}
	char *pResultChar=new char[iLen+1];
	WideCharToMultiByte( CP_ACP, 0, mWchar, -1, pResultChar, iLen, NULL, NULL ); 
	pResultChar[iLen]='\0';
	string strResult=pResultChar;
	delete[]pResultChar;
	return strResult;
}
#endif

/********************************************
*作用:	指定clientMap哈希表大小
*返回值: strHostName 主机名CString
********************************************/
void MySocket::InitClientMapHashTable(UINT nHashValue)
{
	clientMap.InitHashTable(nHashValue);
}


/********************************************
*作用:	获取本机主机名
*返回值: strHostName 主机名CString
********************************************/
CString MySocket::GetMyHostName(void)
{
	char cName[128];
	gethostname(cName,128);
	HOSTENT* host=gethostbyname(cName);
	char *name=host->h_name;
#ifdef _UNICODE
	sHostName=CharToWchar_t_AnsiToUnicode(name);//name转CString
#else
	CString m_strHostName(name);
	sHostName=m_strHostName;
#endif

	return sHostName;
}

/********************************************
*作用:	获取本机IP 本机IP
*返回值 :strHostIp 主机名CString
********************************************/
CString MySocket::GetMyHostIP(void)
{
	char cName[128];
	gethostname(cName,128);
	HOSTENT* host=gethostbyname(cName);
	char *p =host->h_addr_list[0];
	struct in_addr addr; 
	memcpy(&addr, host->h_addr_list[0], sizeof(struct in_addr)); 
#ifdef _UNICODE
	sHostIp=CharToWchar_t_AnsiToUnicode(inet_ntoa(addr));//IP转CString
#else
	CString m_strHostIp(inet_ntoa(addr));
	sHostIp=m_strHostIp;
#endif
	return sHostIp;
}

/********************************************
*作用:	赋值hBindWnd【WSAAsyncSelect监听socket消息绑定的窗口句柄】
*返回值: 窗口句柄
********************************************/
HWND MySocket::SetBandWindowHwnd(HWND hWnd)
{
	hBindWnd=hWnd;
	return hBindWnd;
}

/***************************
*作用:初始化socket,开启服务器,启动服务器监听
*参数:HWND hWnd				WSAAsyncSelect监听socket消息绑定的窗口句柄
*参数:CString strHostIp		服务器本机IP可通过GetHostIP()获取  
*参数:u_short uPort			服务器监听端口
*参数:u_int wMsg				自定义的绑定WSAAsyncSelect的消息宏
*参数:long iEvent				感兴趣的socket消息,取值有以下:(没有全部列出,只列出常用的)
						FD_READ:当套接字中有数据需要读取时触发事件
						FD_WRITE:当向套接字写入数据时触发事件
						FD_OOB:当接收到外带数据时触发事件
						FD_ACCEPT:当接收连接请求时触发事件
						FD_CONNECT:当连接完成时触发事件
						FD_CLOSE:当套接字关闭时触发事件
*返回值:int	成功	0  
			失败	SOCKET_ERROR 调用WSAGetLastError()获取错误代码
******************************/
int MySocket::InitServerSocket(HWND hWnd, CString strHostIp,u_short uPort,u_int wMsg,long iEvent)
{
	GetMyHostIP();
	GetMyHostName();
	SetBandWindowHwnd(hWnd);
	WSADATA wsaData;
	WORD wVersion=MAKEWORD(2,2);
	int error=WSAStartup(wVersion,&wsaData);
	if(error!=0)
	{
		TRACE(_T("@InitServerSocket	WSAStartup失败!错误码=%d\r\n"),error);
		return error;
	}
	sAddrin_server.sin_family=AF_INET;
	sAddrin_server.sin_port=htons(uPort);
#ifdef _UNICODE
	string sIp=Wchar_tToChar_UnicodeToAnsi(strHostIp);//IP  CString UNICODE 转ANSI string
	sAddrin_server.sin_addr.S_un.S_addr=inet_addr(sIp.c_str());//string转char*并赋值 INVALID_SOCKET
#else
	sAddrin_server.sin_addr.S_un.S_addr=inet_addr(strHostIp.GetBuffer());//string转char*并赋值 INVALID_SOCKET
	strHostIp.ReleaseBuffer();
#endif
	sServer=::socket(AF_INET,SOCK_STREAM,0);
	error=::bind(sServer,(sockaddr*)&sAddrin_server,sizeof(sAddrin_server));
	if(error==SOCKET_ERROR)//端口绑定
	{
		TRACE(_T("@InitServerSocket	bind失败!\r\n"));
		return error;
	}
	error=::listen(sServer,5);
	if(error==SOCKET_ERROR)//启动监听
	{
		TRACE(_T("@InitServerSocket	listen失败!\r\n"));
		return error;
	}
	error=::WSAAsyncSelect(sServer,hWnd,wMsg,iEvent);//非阻塞模式,监听连接事件
	if(error==SOCKET_ERROR)//启动监听
	{
		TRACE(_T("@InitServerSocket	WSAAsyncSelect失败!\r\n"));
		return error ;
	}
	return 0;
}
/********************************************
*作用:关闭服务器
*返回值:int	成功	0  
			失败	SOCKET_ERROR 
******************************/
int MySocket::CloseMySocket(SOCKET s)
{
	if(closesocket(s))//关闭服务器套接字
	{
		TRACE(_T("@CloseMySocket	关闭SOCKET s失败!\r\n"));
		return SOCKET_ERROR;
	}
	if(::WSACleanup())
	{
		TRACE(_T("@CloseMySocket	WSACleanup失败!\r\n"));
		return SOCKET_ERROR;
	}
	return 0;
}

/********************************************
*作用:关闭服务器
*返回值:int	成功	0  
			失败	调用WSAGetLastError()获取错误代码
******************************/
int MySocket::CloseServerSocket(void)
{
	int ret=CloseMySocket(sServer);
	if(ret!=0)
	{
		TRACE(_T("@CloseServerSocket	关闭服务器失败!\r\n"));
		return ret;
	}

	//关闭所有客户端
	POSITION pos = clientMap.GetStartPosition();
	while(pos!=NULL)
	{
		UINT uKey;
		ClientInfo mInfo;
		clientMap.GetNextAssoc(pos,uKey,mInfo);
		closesocket(uKey);
	}
	clientMap.RemoveAll();//清空CMap
	iClientCount=0;//已连接的客户端数量
	memset(&sAddrin_server,0, sizeof(sAddrin_server));
	memset(&sAddrin_client,0, sizeof(sAddrin_client));
	sServer=0;//服务器套接字
	sClient=0;//当前客户端套接字
	hBindWnd=NULL;
	sHostIp=_T("");//本机IP
	sHostName=_T("");//本机主机名
	return 0;
}



/************************
*作用:有客户端连接时触发事件
*参数:SOCKET s  参数为服务器本地套接字
*参数:long iEvent 需要监听的事件 服务器一般为 WM_SOCKET,FD_READ|FD_WRITE|FD_CLOSE
*返回值:int	成功	sClient
			失败	INVALID_SOCKET(0)   调用WSAGetLastError()获取错误代码
******************************/
int MySocket::OnAccept(SOCKET s,u_int wMsg,long iEvent)
{
	int len=sizeof(sAddrin_client);
	sClient=accept(s,(sockaddr*)&sAddrin_client,&len);//获取客户端套接字,ip,端口
	TRACE(_T("@OnAccept	sClient=%d sServer=%d\r\n"),sClient,sServer);
	if(INVALID_SOCKET==sClient)
	{
		
		TRACE(_T("@OnAccept	有客户端连接accept事件失败!\r\n"));
		return INVALID_SOCKET;
	}
	if(hBindWnd==NULL)
	{
		
		TRACE(_T("@OnAccept	hBindWnd为空,请先调用SetBandWindowHwnd!\r\n"));
		return INVALID_SOCKET;
	}
	::WSAAsyncSelect(sClient,hBindWnd,wMsg,iEvent);//重新设置该客户端监听的消息
	char* p_cIp=::inet_ntoa(sAddrin_client.sin_addr);
	ClientInfo mClientInfo={_T(""),_T(""),0};
	#ifdef _UNICODE
	mClientInfo.sClientIp=CharToWchar_t_AnsiToUnicode(p_cIp);//客户端ip
#else
	mClientInfo.sClientIp.Format(_T("%s"),p_cIp);
#endif
	mClientInfo.uClientPort=sAddrin_client.sin_port;//客户端端口
	clientMap.SetAt(sClient,mClientInfo);
	iClientCount++;
	return sClient;
}

/************************
*作用:发送数据给客户端
*参数:SOCKET s  对应客户端套接字
*参数:CString msg_unicode 用户输入的信息
*返回值:int	成功	0  
			失败	调用WSAGetLastError()获取错误代码
******************************/
int MySocket::SendMsg(SOCKET s,CString msg_unicode)
{
	int len=(msg_unicode.GetLength() + 1) * sizeof(TCHAR);
	const char *c_msg=NULL;
	#ifdef _UNICODE
		string s_msg=Wchar_tToChar_UnicodeToAnsi(msg_unicode);
		c_msg=s_msg.c_str();
	#else
		c_msg=msg_unicode.GetBuffer();
		msg_unicode.ReleaseBuffer(); 
	#endif
	int RealSendLength=::send(s,(const char *)c_msg, len,0);
	if (RealSendLength!= SOCKET_ERROR)
	{
		return RealSendLength;
	}
	else
	{
		return SOCKET_ERROR;
	}
}


int MySocket::SendMsg(SOCKET s,char* pBuff)
{
	int len=strlen(pBuff)+1;
	int RealSendLength=::send(s,(const char *) pBuff, len,0);
	if (RealSendLength!= SOCKET_ERROR)
	{
		return RealSendLength;
	}
	else
	{
		return SOCKET_ERROR;
	}
}

/********************************************
*作用:当套接字中有数据需要读取时触发事件
*参数:SOCKET s  参数为对应客户端套接字
*int &rcvLen 接收数据的长度
*返回值:LPVOID	 buff 接受的数据
******************************/
LPVOID MySocket::OnRead(SOCKET s,int&rcvLen)//参数为对应客户端套接字
{
	sClient=s;//保证当前Client套接字为最后一个发送信息过来的客户端
	TRACE(_T("@OnRead	sClient=%d sServer=%d\r\n"),sClient,sServer);
	void * buff=0;
	int size =0;
	while(TRUE)
	{
		char rcvBuff[128];
		int realLen=::recv(s,rcvBuff,123,0);
		if(realLen<=0)
		{
			break;
		}else
		{
			int new_size=size+realLen;
			buff=realloc(buff,new_size);
			memcpy((void*)(((char*)buff)+size),rcvBuff,realLen);
			size=new_size;
		}
	}  
	rcvLen=size;
	if(size==0)//没接收到内容
	{
		return	0;
	}
	return buff;

}

/********************************************
*作用:当套接字中有数据需要读取时触发事件
*参数:SOCKET s  参数为对应客户端套接字
*int &rcvLen 接收数据的长度
*返回值:CString	 sRcvMsg 接受的数据
			失败	_T("");
******************************/
CString MySocket::OnReadString(SOCKET s,int&rcvLen)//参数为对应客户端套接字
{
	//通过套接字从clientMap获取客户端主机名和IP端口
	//然后使对应客户端在clientList并使其能看见
	void* pBuff=OnRead(s,rcvLen);
	CString sRcvMsg;
	#ifdef _UNICODE
		sRcvMsg.Format(_T("%s\r\n"),CharToWchar_t_AnsiToUnicode((char*)pBuff));
	#else
		sRcvMsg.Format(_T("%s\r\n"),((char*)pBuff));
	#endif
	//sRcvMsg.Format(_T("接收:%s\r\n"),(LPCTSTR)pBuff);
	free(pBuff);
	return sRcvMsg;
}


/********************************************
*作用:当套接字中有数据需要读取时触发事件
*参数:SOCKET s  参数为对应客户端套接字
*int &rcvLen 接收数据的长度
*返回值:std::string	 string 转char * 用 string::c_str();
******************************/
std::string MySocket::OnReadChar(SOCKET s,int &rcvLen)//参数为对应客户端套接字
{
	void *pBuff=OnRead(s,rcvLen);
	std::string rcvstr=(char*)pBuff;
	free(pBuff);
	return rcvstr;
}

/********************************************
*作用:服务器端关闭指定客户端连接 当套接字关闭时触发事件
*参数:SOCKET s  需要关闭的客户端套接字
*返回值:int	成功	0  
			失败	SOCKET_ERROR 可调用WSAGetLastError()获取错误代码
******************************/
int MySocket::ServerCloseClientConnect(SOCKET s)//参数为对应客户端套接字
{
	if(closesocket(s))
	{
		TRACE(_T("@ServerCloseClientConnect	服务器关闭指定客户端连接失败!\r\n"));
		return SOCKET_ERROR;
	}
	clientMap.RemoveKey(s);	
	iClientCount--;
	return 0;
}

/********************************************
*作用:发送主机名给客户端
*参数:SOCKET s  对应客户端套接字
*返回值:int 成功发送字节数	 
			失败	SOCKET_ERROR 可调用WSAGetLastError()获取错误代码
******************************/
int MySocket::SendHostname(SOCKET s)
{
	if((sHostName.Compare(_T(""))==0))
	{
		TRACE(_T("@SendHostname	未取得主机名,请先获取主机名。"));
		return SOCKET_ERROR;
	}
	int RealSendLength=SendMsg(s,sHostName);
	if(RealSendLength!= SOCKET_ERROR)
	{
		return RealSendLength;
	}else
	{
		TRACE(_T("@SendHostname	发送主机名失败\r\n"));
		return SOCKET_ERROR;
	}

}


/********************************************
*作用:发送连接客户端端口给客户端
*参数:SOCKET s  对应客户端套接字
*参数:sockaddr_in addrClient   客户端的IP,端口信息,在OnAccept里客户端连接时候获取并赋值给sAddrin_client
*返回值:int 成功发送字节数	 
			失败	SOCKET_ERROR 可调用WSAGetLastError()获取错误代码
******************************/
int MySocket::Send_clientPort_ToClient(SOCKET s,sockaddr_in addrClient)
{
	if(addrClient.sin_port==0)
	{
		TRACE(_T("@Send_clientPort_ToClient	addrClient为空,无客户端连接\r\n"));
		return SOCKET_ERROR;
	}
	CString str;
	str.Format(_T("%d"),addrClient.sin_port);
	//将客户端端口发送给客户端
	int RealSendLength=SendMsg(s,str);
	if(RealSendLength!= SOCKET_ERROR)
	{
		return RealSendLength;
	}else
	{
		TRACE(_T("@Send_clientPort_ToClient	发送连接客户端端口给客户端失败\r\n"));
		return SOCKET_ERROR;
	}

}


/********************************************
*作用:客户端初始化
*参数:HWND hWnd socket绑定自定义消息的窗口
*返回值:int 0 成功  
			失败	 调用WSAGetLastError()获取错误代码
******************************/
int MySocket::initClientSocket(HWND hWnd )
{
	GetMyHostIP();
	GetMyHostName();
	SetBandWindowHwnd(hWnd);
	WSADATA wsad;
	WORD wVersion=MAKEWORD(2,2);
	if(WSAStartup(wVersion,&wsad)!=0)
	{
		TRACE(_T("@initClientSocket	WSAStartup初始化winsock失败!\r\n"));
		return WSAGetLastError();
	}
	sClient=::socket(AF_INET,SOCK_STREAM,0);
	TRACE(_T("@initClientSocket	sClient=%d sServer=%d\r\n"),sClient,sServer);
	if(INVALID_SOCKET==sClient)
	{
		TRACE(_T("@initClientSocket	socket失败!\r\n"));
		return WSAGetLastError();
	}
	return 0;
}

/************************
*作用:客户端连服务器
*参数:CString strIp  服务器IP
*参数:UINT uPort  服务器端口
*参数:u_int wMsg  窗口绑定的套接字自定义消息
*返回值:int	成功	m_client_s  
			失败	INVALID_SOCKET(0)   调用WSAGetLastError()获取错误代码
******************************/
int MySocket::ConnectServer(CString strIp,UINT uPort,u_int wMsg)
{
	if(hBindWnd==NULL)
	{
		
		TRACE(_T("@ConnectServer	hBindWnd为空,请先调用SetBandWindowHwnd!\r\n"));
		return -1;
	}
	sAddrin_server.sin_family=AF_INET;
	sAddrin_server.sin_port=htons(uPort);
	#ifdef _UNICODE
	string sIp=Wchar_tToChar_UnicodeToAnsi(strIp);//IP  CString UNICODE 转ANSI string
	sAddrin_server.sin_addr.S_un.S_addr=inet_addr(sIp.c_str());//string转char*并赋值 INVALID_SOCKET
#else
	sAddrin_server.sin_addr.S_un.S_addr=inet_addr(strIp.GetBuffer());//string转char*并赋值 INVALID_SOCKET
	strIp.ReleaseBuffer();
#endif
	TRACE(_T("@ConnectServer	sClient=%d sServer=%d\r\n"),sClient,sServer);
	if(0!=::connect(sClient,(sockaddr*)&sAddrin_server,sizeof(sAddrin_server)))
	{
		int error=WSAGetLastError();
		TRACE(_T("@ConnectServer	连接服务器失败 error=%d!\r\n"),error);
		return error;
	}
	TRACE(_T("@ConnectServer	连接服务器%s:%d成功!\r\n"),strIp,uPort);
	::WSAAsyncSelect(sClient,hBindWnd,wMsg,CLIENT_SOCKET_EVENT);
	return 0;

}

/************************
*作用:客户端连服务器成功时触发事件
*参数:SOCKET s  参数客户端本地套接字
*参数:u_int wMsg  窗口绑定的套接字自定义消息
*参数:long iEvent 需要监听的事件 服务器一般为 WM_SOCKET,FD_READ|FD_WRITE|FD_CLOSE
*返回值:int	成功	m_client_s  
			失败	INVALID_SOCKET(0)   调用WSAGetLastError()获取错误代码
******************************/
int MySocket::OnConnect(SOCKET s,u_int wMsg,long iEvent)
{
	if(hBindWnd==NULL)
	{
		TRACE(_T("@OnConnect	hBindWnd为空,请先调用SetBandWindowHwnd!\r\n"));
		return INVALID_SOCKET;
	}
	::WSAAsyncSelect(s,hBindWnd,wMsg,iEvent);//重新设置该客户端监听的消息
	return s;
}




/********************************************
*作用:关闭客户端
*返回值:int	成功	0  
			失败	调用WSAGetLastError()获取错误代码
******************************/
int MySocket::CloseClient(void)
{
	TRACE(_T("@CloseClient	sClient=%d sServer=%d\r\n"),sClient,sServer);
	int ret=CloseMySocket(sClient);
	if(ret!=0)
	{
		TRACE(_T("@CloseClient	关闭客户端失败!\r\n"));
		return ret;
	}
	memset(&sAddrin_server,0, sizeof(sAddrin_server));
	memset(&sAddrin_client,0, sizeof(sAddrin_client));
	sServer=0;//服务器套接字
	sClient=0;//当前客户端套接字
	hBindWnd=NULL;
	sHostIp=_T("");//本机IP
	sHostName=_T("");//本机主机名
	return 0;
}

 

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值