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


    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!

  • 相关阅读:
    排列数组所有情况
    查到的结果的某个字段在一串字符串之中
    element组件化跳转和路由式跳转
    vue路由and组件操作
    事件 绑定,取消冒泡,拖拽 ,点击,事件委托习题
    窗口属性 和DOM 元素尺寸位置 及习题加强
    DOM树的增删改查 和 Date定时任务
    JS DOM 初做了解,习题笔记
    struts配置及检验
    第一个JSP登录跳转
  • 原文地址:https://www.cnblogs.com/luowei010101/p/2030829.html
Copyright © 2020-2023  润新知