线程同步与异步套接字编程

1.利用事件对象来实现线程间的同步

新建一个win32 console application,取名Event,再建一个Event源文件,编辑:

#include <iostream.h> 
#include <windows.h> 
 
DWORD WINAPI Fun1Proc(LPVOID lpParameter); 
DWORD WINAPI Fun2Proc(LPVOID lpParameter); 
 
int tickets=100
HANDLE g_hEvent; 
 
void main() 

    HANDLE hThread1; 
    HANDLE hThread2; 
 
    hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL); 
    hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL); 
    CloseHandle(hThread1); 
    CloseHandle(hThread2); 
 
    //g_hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);//
创建一个匿名的有信号状态的事件对象 
    //g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);//
创建一个匿名的无信号状态的事件对象 
    g_hEvent=CreateEvent(NULL,FALSE,FALSE,"tickets");//
创建一个命名的无信号状态的事件对象 
    SetEvent(g_hEvent);//
将事件对象设置为有信号状态 
    if(g_hEvent) 
    { 
        if(ERROR_ALREADY_EXISTS==GetLastError()) 
        { 
            cout<<"only one instance can run!"<<endl; 
            return
        } 
    } 
 
    Sleep(4000); 
    CloseHandle(g_hEvent); 

 
DWORD WINAPI Fun1Proc( 
  LPVOID lpParameter   // thread data 


    while(TRUE) 
    { 
        WaitForSingleObject(g_hEvent,INFINITE); 
//        ResetEvent(g_hEvent);//
将事件对象设为非信号状态  
        if(tickets>0
        { 
            Sleep(1); 
            SetEvent(g_hEvent); 
            cout<<"thread1 sell ticket : "<<tickets--<<endl; 
        } 
        else 
        { 
            SetEvent(g_hEvent);//
将事件对象设为有信号状态  
            break
        } 
    } 
     
    return 0

 
DWORD WINAPI Fun2Proc( 
  LPVOID lpParameter   // thread data 


     
    while(TRUE) 
    { 
        WaitForSingleObject(g_hEvent,INFINITE); 
//        ResetEvent(g_hEvent);//
将事件对象设为非信号状态 
        if(tickets>0
        { 
            Sleep(1); 
            SetEvent(g_hEvent); 
            cout<<"thread2 sell ticket : "<<tickets--<<endl; 
        } 
        else 
        { 
            SetEvent(g_hEvent);//
将事件对象设为有信号状态 
            break
        } 
    } 
    return 0
}

2.利用CriticalSection实现线程同步

#include <iostream.h> 
#include <windows.h> 
 
DWORD WINAPI Fun1Proc(LPVOID lpParameter); 
DWORD WINAPI Fun2Proc(LPVOID lpParameter); 
 
int tickets=100
 
CRITICAL_SECTION g_cs; //
定义一个全局的临界区对象 
void main() 

    HANDLE hThread1; 
    HANDLE hThread2; 
 
    hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL); 
    hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL); 
    CloseHandle(hThread1); 
    CloseHandle(hThread2); 
     
    InitializeCriticalSection(&g_cs);//
初始化一个临界区对象 
    Sleep(4000); 
 
    DeleteCriticalSection(&g_cs);//
释放这个临界区对象 

 
DWORD WINAPI Fun1Proc( 
  LPVOID lpParameter   // thread data 


    while(TRUE) 
    { 
        EnterCriticalSection(&g_cs);//
获得临界区的所有权,进入临界区 
        if(tickets>0
        { 
            Sleep(1); 
            cout<<"thread1 sell ticket : "<<tickets--<<endl; 
        } 
        else 
        { 
            break
        } 
        LeaveCriticalSection(&g_cs);//
离开临界区,并释放所有权 
    } 
     
    return 0

 
DWORD WINAPI Fun2Proc( 
  LPVOID lpParameter   // thread data 


     
    while(TRUE) 
    { 
        EnterCriticalSection(&g_cs);//
获得临界区的所有权,进入临界区 
        if(tickets>0
        { 
            Sleep(1); 
            cout<<"thread2 sell ticket : "<<tickets--<<endl; 
        } 
        else 
        { 
            break
        } 
        LeaveCriticalSection(&g_cs);//
离开临界区,并释放所有权 
    } 
    return 0
}

3.线程死锁

#include <iostream.h> 
#include <windows.h> 
 
DWORD WINAPI Fun1Proc(LPVOID lpParameter); 
DWORD WINAPI Fun2Proc(LPVOID lpParameter); 
 
int tickets=100
 
CRITICAL_SECTION g_csA; //
定义一个全局的临界区对象 
CRITICAL_SECTION g_csB; 
void main() 

    HANDLE hThread1; 
    HANDLE hThread2; 
 
    hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL); 
    hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL); 
    CloseHandle(hThread1); 
    CloseHandle(hThread2); 
     
    InitializeCriticalSection(&g_csA);//
初始化一个临界区对象 
    InitializeCriticalSection(&g_csB); 
    Sleep(4000); 
 
    DeleteCriticalSection(&g_csA);//
释放这个临界区对象 
    DeleteCriticalSection(&g_csB); 

 
DWORD WINAPI Fun1Proc( 
  LPVOID lpParameter   // thread data 


    while(TRUE) 
    { 
        EnterCriticalSection(&g_csA);//
获得临界区的所有权,进入临界区 
        Sleep(1); 
        EnterCriticalSection(&g_csB); 
        if(tickets>0
        { 
            Sleep(1); 
            cout<<"thread1 sell ticket : "<<tickets--<<endl; 
        } 
        else 
            break
        LeaveCriticalSection(&g_csB);//
离开临界区,并释放所有权 
        LeaveCriticalSection(&g_csA); 
    } 
        return 0

 
DWORD WINAPI Fun2Proc( 
  LPVOID lpParameter   // thread data 


     
    while(TRUE) 
    { 
        EnterCriticalSection(&g_csB);//
获得临界区的所有权,进入临界区 
        Sleep(1); 
        EnterCriticalSection(&g_csA); 
        if(tickets>0
        { 
            Sleep(1); 
            cout<<"thread2 sell ticket : "<<tickets--<<endl; 
        } 
        else 
            break
        LeaveCriticalSection(&g_csA);//
离开临界区,并释放所有权 
        LeaveCriticalSection(&g_csB); 
    } 
    return 0
}

4.利用异步套接字编写网络聊天室程序

新建一个基于单文档的MFC的应用程序,取名叫Chat2,编辑资源,如下图:

在预编译头文件中添加:

#include <winsock2.h> //使用winsock函数要使用它 
#pragma comment(lib,"Ws2_32.lib")
 

编辑函数InitInstance

BOOL CChat2App::InitInstance() 

    WORD wVersionRequested; 
    WSADATA wsaData; 
    int err; 
     
    wVersionRequested = MAKEWORD( 22 ); 
     
    err = WSAStartup( wVersionRequested, &wsaData ); 
    if ( err != 0 )  
    {         
        return FALSE; 
    } 
     
    if ( LOBYTE( wsaData.wVersion ) != 2 || 
        HIBYTE( wsaData.wVersion ) != 2 ) { 
        WSACleanup( ); 
        return FALSE;  
    }
 
 
    AfxEnableControlContainer(); 
    .......... 
    .......... 
}

添加虚函数:

Chat2.h中编辑:

class CChat2App : public CWinApp 

public
    CChat2App(); 
    ~CChat2App();//
增加一个析构函数,去调用WSACleanup 
    .......... 
    .......... 

Chat2.cpp中编辑:

CChat2App::~CChat2App()

{

    WSACleanup();

}

并在CChat2Dlg.h中添加:

public
    CChat2Dlg(CWnd* pParent = NULL);    // standard constructor 
    ~CChat2Dlg();//
析构函数 
 private
    SOCKET m_socket;
 

CChat2Dlg.cpp中添加:

CChat2Dlg::~CChat2Dlg() 

    if(m_socket) 
    { 
        closesocket(m_socket); 
    } 
}

再添加成员函数BOOL CChat2Dlg::InitSocket,编辑:

BOOL CChat2Dlg::InitSocket() 

    m_socket=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,0); 
    if(INVALID_SOCKET==m_socket) 
    { 
        MessageBox("
创建套接字失败!"); 
        return FALSE; 
    } 
    SOCKADDR_IN addrSock; 
    addrSock.sin_addr.S_un.S_addr=htol(INADDR_ANY); 
    addrSock.sin_family=AF_INET; 
    addrScok.sin_port=htons(6000); 
    if(SOCKET_ERROR==bind(m_socket,(SOCKET*)&addrSock,sizeof(SOCKADDR))) 
    { 
        MessageBox("
绑定失败"); 
        return FALSE; 
    } 
    if(SOCKET_ERROR==WSAAsyncSelect(m_socket,m_hWnd,WM_SOCK,FD_READ))//
请求一个基于消息的网络读取事件通知 
    { 
        MessageBox("
注册网络读取事件失败!"); 
        return FALSE; 
    } 
    return TRUE; 
}

并在BOOL CChat2Dlg::OnInitDialog()调用一下:

BOOL CChat2Dlg::OnInitDialog() 

    .............. 
    .............. 
    // TODO: Add extra initialization here 
    InitSocket(); 
    return TRUE;  // return TRUE  unless you set the focus to a control 
}

接着编写WM_SOCK消息:

Chat2Dlg.h中添加:

#define UM_SOCK WM_USER+1 //消息定义 

afx_msg void OnSock(WPARAM,LPARAM);//
消息函数声明

Chat2Dlg.cpp中编辑:

添加消息映射:

BEGIN_MESSAGE_MAP(CChat2Dlg, CDialog) 
    //{{AFX_MSG_MAP(CChat2Dlg) 
    ON_WM_SYSCOMMAND() 
    ON_WM_PAINT() 
    ON_WM_QUERYDRAGICON() 
    //}}AFX_MSG_MAP 
    ON_MESSAGE(UM_SOCK,OnSock) //
消息映射 
END_MESSAGE_MAP() 

消息函数实现:

void CChat2Dlg::OnSock(WPARAM wParam,LPARAM lParama) 

    switch(LOWORD(lParama)) 
    { 
    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; 
        if(SOCKET_ERROR==WSARecvFrom(m_socket,&wsabuf,1,&dwRead,&dwFlag, 
            (SOCKADDR*)&addrFrom,&len,NULL,NULL))//
接收数据,并判断 
        { 
            MessageBox("
接收数据失败"); 
            return ; 
        } 
        str.Format("%s
说:%s",inet_ntoa(addrFrom.sin_addr),wsabuf.buf); 
        str+="\r\n"
        GetDlgItemText(IDC_EDIT_RECV,strTemp); 
        str+=strTemp; 
        SetDlgItemText(IDC_EDIT_RECV,str); 
        break
    } 
}

双击发送按钮,接下来编写发送端:

void CChat2Dlg::OnBtnSend()  

    // TODO: Add your control notification handler code here 
    DWORD dwIP; 
    CString strSend;//
用于存放发送的字节数 
    WSABUF wsabuf; 
    DWORD dwSend; 
    int len; 
    ((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP); 
     
    SOCKADDR_IN addrTo; 
    addrTo.sin_addr.S_un.S_addr=htonl(dwIP); 
    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;//
有一个'\0'作为结尾 
 
    SetDlgItemText(IDC_EDIT_SEND,""); 
    if(SOCKET_ERROR==WSASendTo(m_socket,&wsabuf,1,&dwSend,0
        (SOCKADDR*)&addrTo,sizeof(SOCKADDR),NULL,NULL))//
发送数据,并判断 
    { 
        MessageBox("
发送数据失败"); 
        return
    } 
}

5.利用主机名发送数据

void CChat2Dlg::OnSock(WPARAM wParam,LPARAM lParama) 

    switch(LOWORD(lParama)) 
    { 
    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;//
定义一个HOSTENT结构体指针 
        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
    } 
}
 

void CChat2Dlg::OnBtnSend()  

    // TODO: Add your control notification handler code here 
    DWORD dwIP; 
    CString strSend;//
用于存放发送的字节数 
    WSABUF wsabuf; 
    DWORD dwSend;//
用于指向存放the number of bytes sent by this call 
    int len; 
    CString strHostName; 
    SOCKADDR_IN addrTo; 
    HOSTENT* pHost;//
定义一个HOSTENT结构体 
    if(GetDlgItemText(IDC_EDIT_HOSTNAME,strHostName),strHostName=="NULL")//
获取主机名,并判断其是否为空 
    { 
        ((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);//
IP地址控件中获取IP地址 
        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]); 
    } 
 
    ((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP); 
     
    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;//
有一个'\0'作为结尾 
 
    SetDlgItemText(IDC_EDIT_SEND,""); 
    if(SOCKET_ERROR==WSASendTo(m_socket,&wsabuf,1,&dwSend,0
        (SOCKADDR*)&addrTo,sizeof(SOCKADDR),NULL,NULL))//
发送数据,并判断 
    { 
        MessageBox("
发送数据失败"); 
        return
    } 
}

运行,OK!

原文地址:https://www.cnblogs.com/luowei010101/p/2030829.html