VC++深入详解·chapter15·笔记

来源:互联网 发布:淘宝买的机票怎么取 编辑:程序博客网 时间:2024/05/18 13:10

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键发送数据)