MFC(线程同步与异步套接字,孙鑫C++第十六讲笔记整理)

1.事件对象:来实现线程的同步。与互斥对象一样均属于内核对象。

 当人工重置有信号时,所有线程均得到信号,所以不能设为人工重置。代码就不贴了,通过创建匿名的事件对象,也可以让一个程序只能运行一个实例。

 

 

2.关键代码段实现线程的同步:类似公用电话亭,只有当电话亭里面没人了,其它人才可以再进去打电话。用了4个函数,这种方法比较简单!但缺点是如果使用了多少关键代码码,容易赞成线程的死锁

 

 

3.线程死锁,用关键代码示例,用了两个临界区对象,实战中要注意避免这种错误!

 

 

4.使用异步套接字编写网络聊天室

 1)加载套接字库,进行版本协商,包含头文件,链接库文件,这次请示的是2.2版本!

 2)在类CChatDlg中增加一个成员变量m_socket,在析构函数中释放这个变量

 3)利用WSASocket()创建套接字(数据报类型的UDP型的)

 4)然后调用WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ)为网络事件定义消息!此时如果发生FD_READ消息,系统会发送UM_SOCK消息给应用程序!程序并不会阻塞在这儿了!

 以上是在BOOL CChatDlg::OnInitDialog()完成

 5)然后完成消息响应!

 头文件中:#define UM_SOCK WM_USER+1

 afx_msg void OnSock(WPARAM,LPARAM);

  源文件中:

   ON_MESSAGE(UM_SOCK,OnSock)

   实现消息响应函数:void CChatDlg::OnSock(WPARAM wParam,LPARAM lParam)

{

 switch(LOWORD(lParam))

 {

 case FD_READ:

 WSABUF wsabuf;

 wsabuf.buf=new char[200];

 wsabuf.len=200;

 DWORD dwRead;

 DWORD dwFlag=0;

 SOCKADDR_IN addrFrom;

 int len=sizeof(SOCKADDR);

 CString str;

 CString strTemp;

 HOSTENT *pHost;

 if(SOCKET_ERROR==WSARecvFrom(m_socket,&wsabuf,1,&dwRead,&dwFlag,

     (SOCKADDR*)&addrFrom,&len,NULL,NULL))

 {

  MessageBox("接收数据失败!");

  return;

 }

 pHost=gethostbyaddr((char*)&addrFrom.sin_addr.S_un.S_addr,4,AF_INET);

 //str.Format("%s :%s",inet_ntoa(addrFrom.sin_addr),wsabuf.buf);

 str.Format("%s :%s",pHost->h_name,wsabuf.buf);

 str+="\r\n";

 GetDlgItemText(IDC_EDIT_RECV,strTemp);

 str+=strTemp;

 SetDlgItemText(IDC_EDIT_RECV,str);

 break;

 }

}

OK

 6)完成数据发送的功能!

     void CChatDlg::OnBtnSend()

{

 // TOD Add your control notification handler code here

 DWORD dwIP;

 CString strSend;

 WSABUF wsabuf;

 DWORD dwSend;

 int len;

 CString strHostName;

 SOCKADDR_IN addrTo;

 HOSTENT* pHost;

 if(GetDlgItemText(IDC_EDIT_HOSTNAME,strHostName),strHostName=="")

 {

 ((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);

 addrTo.sin_addr.S_un.S_addr=htonl(dwIP);

 }

 else

 {

 pHost=gethostbyname(strHostName);

 addrTo.sin_addr.S_un.S_addr=*((DWORD*)pHost->h_addr_list[0]);

 }

 

 addrTo.sin_family=AF_INET;

 addrTo.sin_port=htons(6000); GetDlgItemText(IDC_EDIT_SEND,strSend);

 len=strSend.GetLength();

 wsabuf.buf=strSend.GetBuffer(len);

 wsabuf.len=len+1; SetDlgItemText(IDC_EDIT_SEND,""); if(SOCKET_ERROR==WSASendTo(m_socket,&wsabuf,1,&dwSend,0,

  (SOCKADDR*)&addrTo,sizeof(SOCKADDR),NULL,NULL))

 {

 MessageBox("发送数据失败!");

 return;

 }

 

}     7)完成将主机名转换为IP地址的功能,以前将IP地址转换为主机名的功能,单线程的聊天室创建完毕!性能并且非常出色!

 

下面是一些具体的代码:

#include<windows.h>
#include<iostream.h>


DWORD WINAPI ThreadProc1(
						 LPVOID lpParameter 
						 );
DWORD WINAPI ThreadProc2(
						 LPVOID lpParameter  
						 );



int tickes=100;
HANDLE hEvent;
int main()
{
	HANDLE hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);
	HANDLE hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);
	
	hEvent=CreateEvent(NULL,FALSE,TRUE,NULL);//自动,有信号
	
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	Sleep(4000);
	
	return 0;
	
}

DWORD WINAPI ThreadProc1(
						 LPVOID lpParameter   // thread data
						 )
{
	
	while(TRUE)
	{
		SetEvent(hEvent);
		WaitForSingleObject(hEvent,INFINITE);
		if(tickes>0)
		{
			cout<<"thread1 sell ticke: "<<tickes--<<endl;
		}
		else
		{
			break;
		}
		//ReleaseMutex(hEvent);
		ResetEvent(hEvent);
	}
	return 0;
	
}


DWORD WINAPI ThreadProc2(
						 LPVOID lpParameter   // thread data
						 )
{
	while(TRUE)
	{
		SetEvent(hEvent);
		WaitForSingleObject(hEvent,INFINITE);
		if(tickes>0)
		{
			cout<<"thread2 sell ticke: "<<tickes--<<endl;
		}
		else
		{
			break;
		}
		//ReleaseMutex(hEvent);
		ResetEvent(hEvent);
	}
	return 0;
}


 

//CreateEvent设置自定的,并且初始有信号
/*#include<windows.h>
#include<iostream.h>


DWORD WINAPI ThreadProc1(
						 LPVOID lpParameter 
						 );
DWORD WINAPI ThreadProc2(
						 LPVOID lpParameter  
						 );



int tickes=100;
HANDLE hEvent;
int main()
{
	HANDLE hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);
	HANDLE hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);
	
	hEvent=CreateEvent(NULL,FALSE,TRUE,NULL);//自动,有信号
	
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	Sleep(4000);
	
	return 0;
	
}

DWORD WINAPI ThreadProc1(
						 LPVOID lpParameter   // thread data
						 )
{
	
	while(TRUE)
	{
		WaitForSingleObject(hEvent,INFINITE);
		if(tickes>0)
		{
			cout<<"thread1 sell ticke: "<<tickes--<<endl;
		}
		else
		{
			break;
		}
	
	}
	return 0;
	
}


DWORD WINAPI ThreadProc2(
						 LPVOID lpParameter   // thread data
						 )
{
	while(TRUE)
	{
		WaitForSingleObject(hEvent,INFINITE);
		if(tickes>0)
		{
			cout<<"thread2 sell ticke: "<<tickes--<<endl;
		}
		else
		{
			break;
		}
	
	}
	return 0;
}*/
//结论是,设置自动的,并且开始有信号(无信号可以调用SetEvent来使无信号到有信号的一个转变)
//只有一个线程获得了信号,并且运行完后(时间片到了),则下一个线程是无法运行的,因为此时只有一个
//线程有信号,并且运行完后,马上使得事件对象无信号


 

//CreateEvent手动,一开始就无信号
/*#include<windows.h>
#include<iostream.h>


DWORD WINAPI ThreadProc1(
						 LPVOID lpParameter 
						 );
DWORD WINAPI ThreadProc2(
						 LPVOID lpParameter  
						 );



int tickes=100;
HANDLE hEvent;
int main()
{
	HANDLE hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);
	HANDLE hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);
	
	hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);//手动,无信号
	SetEvent(hEvent);
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	Sleep(4000);
	
	return 0;
	
}

DWORD WINAPI ThreadProc1(
						 LPVOID lpParameter   // thread data
						 )
{
	
	while(TRUE)
	{
		WaitForSingleObject(hEvent,INFINITE);
		if(tickes>0)
		{
			cout<<"thread1 sell ticke: "<<tickes--<<endl;
		}
		else
		{
			break;
		}
		
	}
	return 0;
	
}


DWORD WINAPI ThreadProc2(
						 LPVOID lpParameter   // thread data
						 )
{
	while(TRUE)
	{
		WaitForSingleObject(hEvent,INFINITE);
		if(tickes>0)
		{
			cout<<"thread2 sell ticke: "<<tickes--<<endl;
		}
		else
		{
			break;
		}
		
	}
	return 0;
}*/
//使用手动设置,则任何线程都获得了信号,都可以运行,可以使用ResetEvent使得信号变得无效


 

//关键代码段,临界区域
//如果只是Enter了但是没有Leave则下一个线程获取不了信号,下一个线程得不到执行
/*
#include<windows.h>
#include<iostream.h>


DWORD WINAPI ThreadProc1(
						 LPVOID lpParameter 
						 );
DWORD WINAPI ThreadProc2(
						 LPVOID lpParameter  
						 );



int tickes=100;
CRITICAL_SECTION section;
int main()
{
	HANDLE hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);
	HANDLE hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);
	
	
	
	CloseHandle(hThread1);
	CloseHandle(hThread2);

	InitializeCriticalSection(§ion);
	Sleep(4000);
	
	DeleteCriticalSection(§ion);
	return 0;
	
}

DWORD WINAPI ThreadProc1(
						 LPVOID lpParameter   // thread data
						 )
{
	
	while(TRUE)
	{
		EnterCriticalSection(§ion);
		if(tickes>0)
		{
			cout<<"thread1 sell ticke: "<<tickes--<<endl;
		}
		else
		{
			break;
		}
		LeaveCriticalSection(§ion);
		
	}
	return 0;
	
}


DWORD WINAPI ThreadProc2(
						 LPVOID lpParameter   // thread data
						 )
{
	while(TRUE)
	{
		EnterCriticalSection(§ion);
		if(tickes>0)
		{
			cout<<"thread2 sell ticke: "<<tickes--<<endl;
		}
		else
		{
			break;
		}
		LeaveCriticalSection(§ion);
	}
	return 0;
}*/


 

//死锁的体现

/*#include<windows.h>
#include<iostream.h>


DWORD WINAPI ThreadProc1(
						 LPVOID lpParameter 
						 );
DWORD WINAPI ThreadProc2(
						 LPVOID lpParameter  
						 );



int tickes=100;
CRITICAL_SECTION sectionA;
CRITICAL_SECTION sectionB;
int main()
{
	HANDLE hThread1=CreateThread(NULL,0,ThreadProc1,NULL,0,NULL);
	HANDLE hThread2=CreateThread(NULL,0,ThreadProc2,NULL,0,NULL);
	
	
	
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	
	InitializeCriticalSection(§ionA);
	InitializeCriticalSection(§ionB);
	Sleep(4000);
	
	DeleteCriticalSection(§ionB);
	DeleteCriticalSection(§ionA);
	return 0;
	
}

DWORD WINAPI ThreadProc1(
						 LPVOID lpParameter   // thread data
						 )
{
	
	while(TRUE)
	{
		EnterCriticalSection(§ionA);
		Sleep(1);
		EnterCriticalSection(§ionB);
		if(tickes>0)
		{
			cout<<"thread1 sell ticke: "<<tickes--<<endl;
		}
		else
		{
			break;
		}
		LeaveCriticalSection(§ionB);
		LeaveCriticalSection(§ionA);
		
	}
	return 0;
	
}


DWORD WINAPI ThreadProc2(
						 LPVOID lpParameter   // thread data
						 )
{
	while(TRUE)
	{
		EnterCriticalSection(§ionB);
		Sleep(1);
		EnterCriticalSection(§ionA);

		if(tickes>0)
		{
			cout<<"thread2 sell ticke: "<<tickes--<<endl;
		}
		else
		{
			break;
		}
		LeaveCriticalSection(§ionA);
		LeaveCriticalSection(§ionB);
	}
	return 0;
}*/


 

 

下面是用异步套接字写的聊天程序,跟前面的多线程对比一下:

1新建一个基于单文档的MFC程序

2拉控件成这样:

 

3在CXXAPP文件中的InitInstance中添加WSAStartupXX库添加函数,查看MSDN的WSAStartup

4在CXXDlg中添加成员变量SOCKET m_socket和成员函数 InitSocket;

BOOL CNewChatDlg::InitSocket()
{
	m_socket=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,0);

	if(!m_socket)
	{
		AfxMessageBox("创建套接字失败");
		return FALSE;
	}

	SOCKADDR_IN sockaddr;
	sockaddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
	sockaddr.sin_family=AF_INET;
	sockaddr.sin_port=htons(6000);

	int returnbind;
	returnbind=bind(m_socket,(SOCKADDR*)&sockaddr,sizeof(SOCKADDR));
	if(SOCKET_ERROR==returnbind)
	{
		AfxMessageBox("绑定失败");
		return FALSE;
	}

	int se;
	se=WSAAsyncSelect(m_socket,m_hWnd,WM_RECV,FD_READ);
	if(SOCKET_ERROR==se)
	{
		AfxMessageBox("创建网络读取时间失败");
		return FALSE;
	}
	return TRUE;
}


并在CXXDlg中的OnInitDialog中调用InitSocket();

5自定义消息响应事件

a #define WM_RECV WM_USER+1

b afx_msg void OnRecv(WPARAM wParam,LPARAM lParam);

c ON_MESSAGE(WM_RECV,OnRecv)

void CNewChatDlg::OnRecv(WPARAM wParam,LPARAM lParam)
{
	switch(LOWORD(lParam))
	{
	case FD_READ:
		WSABUF wsabuf;
		wsabuf.buf=new char[200];
		wsabuf.len=200;

		DWORD dwread;
		DWORD dwflag=0;

		SOCKADDR_IN sockaddr;
		int len=sizeof(SOCKADDR);



		if(SOCKET_ERROR ==WSARecvFrom(m_socket,&wsabuf,1,&dwread,&dwflag,
			(SOCKADDR*)&sockaddr,&len,NULL,NULL))
		{
			AfxMessageBox("接收数据失败");
			return ;
		}

	


		char temp[200];
		sprintf(temp,"%s 说 %s ",inet_ntoa(sockaddr.sin_addr),wsabuf.buf);
		CString jieshou;
		GetDlgItemText(ID_JIESHOU,jieshou);
		if(jieshou!="")
		{
		jieshou+="\r\n";
		}
		jieshou+=temp;
		SetDlgItemText(ID_JIESHOU,jieshou);

		break;
	}
}


6添加发送按钮事件:

void CNewChatDlg::OnSend() 
{
	// TODO: Add your control notification handler code here
	
	DWORD dwIp;
	CString strsend;
	((CIPAddressCtrl*)GetDlgItem(ID_IPADDRESS))->GetAddress(dwIp);

	SOCKADDR_IN sockaddr;
	sockaddr.sin_addr.S_un.S_addr=htonl(dwIp);
	sockaddr.sin_family=AF_INET;
	sockaddr.sin_port=htons(6000);

	

	GetDlgItemText(ID_FASONG,strsend);
	

	

	WSABUF wsabuf;
	wsabuf.buf=strsend.GetBuffer(strsend.GetLength());
	wsabuf.len=strsend.GetLength()+1;
	SetDlgItemText(ID_FASONG,"");
	
	DWORD dwsent;
	int len=sizeof(SOCKADDR);

	if(SOCKET_ERROR ==WSASendTo(m_socket,&wsabuf,1,&dwsent,0,
		(SOCKADDR*)&sockaddr,len,NULL,NULL))
	{
		AfxMessageBox("发送数据失败");
		return ;
	}

		
}

CNewChatDlg::~CNewChatDlg()
{
	closesocket(m_socket);
}


 

CNewChatDlg::~CNewChatDlg()
{
	closesocket(m_socket);
}


 

要熟悉异步套接字和多线程的编程,这个很重要,一定要熟练。

 

 

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值