1、MFC中加载套接字库:
1> 在App类的InitInstance函数中使用函数AfxSocketInit函数进行加载:
if(!AfxSocketInit()) //"Failed to Initialize Sockets",MB_OK| MB_ICONSTOP)
{
AfxMessageBox(_T("加载套接字库失败!", MB_OK | MB_ICONSTOP);
return false;
}
2> 在预编译头文件stdafx.h中包含相应的头文件Afxsock.h
#include <Afxsock.h>
注:stdafx.h是一个预编译头文件,在该文件中包含了MFC应用程序运行所需的一些必要的头文件。对于所有MFC程序来说,它们第一个要包含的头文件就是这个预编译头文件。
此函数加载的是1.1版本的套接字库;同时它可以确保应用程序终止之前,调用WSACleanup函数以终止对套接字库的使用,并且利用此函数加载套接字库时,不需要为工程链接ws2_32.lib库函数
如果需要套接字库2.0的版本,则不应调用此函数,而应直接调用WSAStartup函数来初始化程序所使用的套接字库
2、获取IP Address Contrl中用户输入的IP:
//获取对方IP
DWORD dwIP;
((CIPAddressCtrl* )GetDlgItem(IDC_IPADDRESS))->GetAddress(dwIP);
3、实现线程同步:利用互斥对象mutex
1> 互斥对象属于内核对象
2> 互斥对象包含一个使用数量、一个线程ID和一个计数器
3> 可利用互斥对象实现同步,互斥对象可看成是一把钥匙;
4> 对于互斥对象来说,谁拥有谁释放!(线程ID标识拥有者)
5> 互斥对象具有与线程相关的特点
6> 线程必须主动请求共享对象的使用权才有可能获得其所有权(WaitForSingleObject)
7> CreateMutex函数:创建或打开
8> WaitForSingleObject函数:请求使用权
9> ReleaseMutex函数:释放使用权
10> 可以根据WaitForSingleObject函数的返回值知道当前线程是如何得到互斥对象的所有权的;因此,在程序中应该根据 WaitForSingleObject函数的返回值进行一些相应处理。
4、实现保证应用程序只有一个实例在运行:通过命名的互斥对象
hMutex=CreateMutex(NULL,TRUE,_T("tickets"));//此处必须是命名的
if(hMutex)
{
if(ERROR_ALREADY_EXISTS==GetLastError())
{
cout<<"only instance can run!"<<endl;
return;
}
}
WaitForSingleObject(hMutex,INFINITE);
ReleaseMutex(hMutex);
ReleaseMutex(hMutex);
注:将以上创建互斥对象的代码放在线程创建代码之上
5、Sleep函数:让某个线程暂停,让出时间片;
原型:void Sleep(DWORD dwMilliseconds);
6、代码
#include <windows.h>
#include <iostream>
using std::cout;
using std::endl;
DWORD WINAPI Fun1Proc(
__in LPVOID lpParameter
); //thread1 的起始地址(函数),此函数的原型在MSDN中查找
DWORD WINAPI Fun2Proc(
__in LPVOID lpParameter
);//thread1 的起始地址(函数)
int index=0;
int tickets=100;
HANDLE hMutex;
int main()
{
//匿名互斥对象的创建
//hMutex=CreateMutex(0, true,NULL); //创建时拥有互斥对象
//ReleaseMutex(hMutex); //释放该互斥对象的所有权,递减互斥对象的计数器
//hMutex=CreateMutex(0, false,NULL);//创建时不拥有互斥对象,即互斥对象是有信号状态(已通知状态)
//hMutex=CreateMutex(0, true, NULL);
//WaitForSingleObject(hMutex, INFINITE);
//ReleaseMutex(hMutex);
//ReleaseMutex(hMutex);
/*while(index++<1000)
std::cout<<"main thread is running/n"<<std::endl;*/
hMutex=CreateMutex(0, true, _T("tickets"));//通过命名的互斥对象来实现保证应用程序只有一个实欲行
if(hMutex) //如果是一个有效的句柄
{
if(ERROR_ALREADY_EXISTS==GetLastError())//该命名的互斥对象已被创建
{
cout<<"only one instance can run!"<<endl;
return 0;
}
}
ReleaseMutex(hMutex);
HANDLE hThread1=CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL); //创建线程1
HANDLE hThread2=CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL); //创建线程2
CloseHandle(hThread1);//关闭线程1的句柄,并未中止新建线程,且需要关闭(1)不需要,因此关闭(2)关闭句柄,递减该线程内核对象的使用计数
CloseHandle(hThread2);//同上;在程序中,当不再需要线程句柄是,应将其关闭,让这个线程内核对象的引用计数减1
注:当创建的这个线程执行完毕之后,系统也会递减该线程内核对象的使用计数。当使用计数减为0时,系统会释放该线程内核对象
Sleep(40000);//让主线程暂停运行,让出时间片
return 0;
}
DWORD WINAPI Fun1Proc(
__in LPVOID lpParameter
)
{
/*while(index++<1000)
std::cout<<"thread1 is running"<<std::endl;*/
/*while(1)
{
WaitForSingleObject(hMutex, INFINITE);
if(tickets>0)
{
Sleep(2);
cout<<"thread1 sell ticket: "<<tickets--<<endl;
}
else
break;
ReleaseMutex(hMutex);
}*/
WaitForSingleObject(hMutex, INFINITE);
cout<<"thread1 is running..."<<endl;
return 0;
}
DWORD WINAPI Fun2Proc(
__in LPVOID lpParameter
)
{
/*while(index++<1000)
std::cout<<"thread1 is running"<<std::endl;*/
/*while(1)
{
WaitForSingleObject(hMutex, INFINITE);
if(tickets>0)
{
Sleep(2);
cout<<"thread2 sell ticket: "<<tickets--<<endl;
}
else
break;
ReleaseMutex(hMutex);
}*/
WaitForSingleObject(hMutex, INFINITE);
cout<<"thread2 is running..."<<endl;
return 0;
}
7、网络聊天程序的实现步骤(用基于对话框的实例)
1> 加载套接字库:MFC中的AfxSocketInit函数
(1) App类的InitInstance中:
if(!AfxSocketInit())
{
AfxMessageBox(_T("加载套接字库失败!"));
return false;
}
(2)在stdafx.h中包含同文件#include <Afxsock.h>
2> 创建并初始化套接字:
给主Dlg类添加:
(1) SOCKET m_socket; //private data member
(2) BOOL InitSocket(); //public member function
(3) 在主Dlg类的OnInitDialog函数中执行初始化,即调用InitSocket函数
//初始化套接字函数的实现
BOOL CChat3Dlg::InitSocket()
{
m_socket=socket(AF_INET, SOCK_DGRAM, 0);
if(INVALID_SOCKET==m_socket)
{
MessageBox(_T("套接字创建失败!"));
return false;
}
SOCKADDR_IN addrSock;
addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSock.sin_family=AF_INET;
addrSock.sin_port=htons(5003);
int retval=bind(m_socket, (SOCKADDR* )&addrSock, sizeof(SOCKADDR));
if(SOCKET_ERROR==retval)
{
closesocket(m_socket);
MessageBox(_T("绑定失败!"));
return FALSE;
}
return TRUE;
}
3> 实现接收端功能:将接收数据的操作放置在一个单独的线程中完成
(1)创建接收线程:
HANDLE hThread=CreateThread(NULL, 0, RecvProc, (LPVOID)pRecvParam, 0, NULL);
CloseHandle(hThread);//关闭该接收线程句柄,释放其引用计数,此点易漏
其中RecvProc是线程入口函数,其原型固定(MSDN中);pRecvParam是传递的参数,其类型为:
struct RECVPARAM
{
SOCKET sock; //传递套接字
HWND hwnd; //传递窗口句柄
};
RECVPARAM* pRecvParam=new RECVPARAM;
pRecvParam->sock=m_socket;
pRecvParam->hwnd=this->m_hWnd;
(2)线程入口函数的定义:
DWORD WINAPI CChat3Dlg::RecvProc( LPVOID lpParameter)
{
SOCKET sock=((RECVPARAM* )lpParameter)->sock;
HWND hwnd=((RECVPARAM* )lpParameter)->hwnd;
SOCKADDR_IN addrFrom;
int len=sizeof(SOCKADDR);
char recvBuf[200]; //接收发送过来的数据
char tempBuf[300]; //将接收到的数据格式化
int retval;
while(1)
{
retval=recvfrom(sock, recvBuf, 200, 0, (SOCKADDR*)&addrFrom, &len);
if(SOCKET_ERROR==retval)
break;
sprintf(tempBuf, "%s 说: %s", inet_ntoa(addrFrom.sin_addr), recvBuf);
::PostMessage(hwnd, UM_RECVDATA, 0, (LPARAM)tempBuf); //发送自定义消息,通过tempBuf传递数据
}
return 0;
}
注:OO思想:将此线程入口函数定义为主Dlg的member function, 并且是static的
4> 自定义消息及其响应函数(显示数据)(详细步骤见chapter09)
其消息响应函数为:
LRESULT CChat3Dlg::OnRecvData(WPARAM wParam, LPARAM lParam)
{
//取出接收到的数据
CString str((char*)lParam);
CString strTemp;
//保存已有数据
GetDlgItemText(IDC_EDIT_RECV, strTemp);
str+="/t/n";
str+=strTemp;
//显示所有的数据
SetDlgItemText(IDC_EDIT_RECV, str);
return 0;
}
5> 实现发送端功能
为发送按钮添加消息响应函数:
void CChat3Dlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
DWORD dwIP;
((CIPAddressCtrl* )GetDlgItem(IDC_IP))->GetAddress(dwIP);
SOCKADDR_IN addrTo;
addrTo.sin_addr.S_un.S_addr=htonl(dwIP);
addrTo.sin_family=AF_INET;
addrTo.sin_port=htons(5003);
CString strSend;
//取出发送端用户在发送框中的输入信息
GetDlgItemText(IDC_EDIT_SEND, strSend);
sendto(m_socket, strSend.GetBuffer(strSend.GetLength()), strSend.GetLength()+1, 0, (SOCKADDR* )&addrTo, sizeof(SOCKADDR));
SetDlgItemText(IDC_EDIT_SEND, "");
}
注:发送时一定药多发送一个字节:strSend.GetLength()+1
注:将接收框设为支持多行,并将发送按钮置为默认按钮和不可见(直接按ENTER键发送数据)
VC++深入详解·chapter15·笔记
最新推荐文章于 2025-04-10 20:13:08 发布