注意:本篇文章源代码贴得很多,所以有点长,如果觉得阅读不方便,可以直接复制源代码到你的程序里去,也可以直接下载源码。后面整合了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无法通过窗口名称获取到客户端句柄,然后关闭。
为了配合测试程序关闭客户端,需要给客户端注册一个类名方便找到客户端。
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;
}