1 基于对话框的的TCP服务器建立步骤
1.1 建立基于对话框的MFC应用程序时,选支持Winsock
好处:我们不用再写加载/卸载 套接字库,以及头文件和lib文件了
1.2 设计界面------开启服务的按钮
1.3 关联 开启服务的成员函数
1.4 修改OnButtonOpenServer函数
注意点:
1 成员变量的使用
2 在Accept前面使用多线程--------因为是阻塞函数;但需求又必须执行Accept函数
知识点:
1 建立多线程
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to security attributes NULL
DWORD dwStackSize, // initial thread stack size 0
LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread function 新的线程的入口函数
LPVOID lpParameter, // argument for new thread 入口函数的参数---因为是地址,所以可以传进来很多参数
DWORD dwCreationFlags, // creation flags 0,则新的线程会马上进入 入口函数
LPDWORD lpThreadId // pointer to receive thread ID NULL,用来存新线程的ID号
);
2 入口函数的原型必须和下面一样
DWORD WINAPI ThreadProc(
LPVOID lpParameter // thread data
);
3 如何退出子线程
CloseHandle(hThread);//其实不是关闭,而是让子线程的计数器减1。如果不加,相当于子线程永远不会退出,而发生内存泄露。
注意:它只是让计数器减一
4 怎样让图标成为 虚的
GetDlgItem(IDC_BUTTON_OPEN_SERVER)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_CLOSE_SERVER)->EnableWindow(TRUE);
注意: GetDlgItem是成员函数;参数是图标的ID号;返回值是图标对应的对象的地址;
EnableWindow也是(CWnd)成员函数,它的作用可以让图标变 虚
5 静态成员函数、静态成员变量
5.1 静态成员函数---外部函数 有相似的地方
相似:静态成员函数属于1个类,而不属于1个对象
所以,静态成员函数的调用有2种方式:
方式一:跟普通成员函数一样
方式二:只要在函数名前加 类的作用域就可以
注意:静态成员函数 只能访问 静态成员变量
注意:
在.h中增加static
5.2 静态成员变量----全局变量 有相似的地方
相似:静态成员变量属于1个类,而不属于1个对象
注意:
在.h中增加static
在.cpp中如此使用:
SOCKET CMfctcpserverDlg::socket_listen=NULL;
SOCKET CMfctcpserverDlg::socket_connect=NULL;
而且要放在函数的外面。
6 自定义消息
6.1 概念
BOOL PostMessage(
HWND hWnd, // handle of destination window 接收窗口的句柄-------任何窗口都有1个句柄
UINT Msg, // message to post 消息名称-----代表1大类消息
WPARAM wParam, // first message parameter 消息内容----如buf_recv数组中的数组名
LPARAM lParam // second message parameter
);
知识点:
1 如何得到 主对话框的窗口句柄?
Dlg类里,天生就有,是基类的成员变量-----m_hWnd
可以通过CreateThread函数,传给入口函数ThreadProc
方法:利用如何函数的参数;注意要强制类型转换
代码如下:
void CMfctcpserverDlg::OnButtonOpenServer() { // TODO: Add your control notification handler code here socket_listen=socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in addr_b; //addr_b.sin_addr.S_un.S_addr=inet_addr("127.0.0.1"); addr_b.sin_addr.S_un.S_addr=htonl(INADDR_ANY); addr_b.sin_port=htons(6588); addr_b.sin_family=AF_INET; struct sockaddr * addr=(struct sockaddr *)&addr_b; bind(socket_listen,addr,sizeof(SOCKADDR)); int flag=listen(socket_listen,4); HANDLE hThread=CreateThread(NULL,0,ThreadProc,(LPVOID)m_hWnd,0,NULL); //此处的m_hWnd是基类的成员变量,就是主对话框的 窗口句柄 CloseHandle(hThread);//其实不是关闭,而是让子线程的计数器减1。如果不加,相当于子线程永远不会退出,而发生内存泄露。 GetDlgItem(IDC_BUTTON_OPEN_SERVER)->EnableWindow(FALSE); GetDlgItem(IDC_BUTTON_CLOSE_SERVER)->EnableWindow(TRUE); } DWORD WINAPI CMfctcpserverDlg ::ThreadProc(LPVOID lpParameter) { //SOCKET socket_listen=(SOCKET)lpParameter; //收到的lpParameter,其实是m_hWnd变量的复制品 HWND hWnd1=(HWND)lpParameter; //通过强制类型转换,恢复了复制品的本来面目,即HWND类型 sockaddr_in addr_bb; sockaddr * addr_client=(sockaddr *)&addr_bb; int len=sizeof(sockaddr); socket_connect=accept(socket_listen,addr_client,&len); char buf_recv[1000]={'\0'}; int count=recv(socket_connect,buf_recv,sizeof(buf_recv)-1,0); //AfxMessageBox(buf_recv); //m_edit_show.Format("%s",buf_recv); //UpdateData(FALSE); ::PostMessage(m_hWnd1,DATA_USER,(WPARAM)buf_recv,NULL); // WPARAM a; return 0; }
6.2 改进后的代码
//声明静态变量 static SOCKET socket_listen; static SOCKET socket_connect; static DWORD WINAPI ThreadProc( LPVOID lpParameter); //在头文件中添加消息 afx_msg void OnRecvData(WPARAM wParam, LPARAM lParam);//add //在cpp中添加函数声明 ON_MESSAGE(WM_RECV_DATA, OnRecvData) //cpp change //实现函数体OnRecvData void CMfctcpservertest1Dlg::OnRecvData(WPARAM wParam, LPARAM lParam) { //AfxMessageBox("hello,zhangsan"); char * recv_str = (char *)wParam; int d = strlen(recv_str); m_edit_show.Format("%d",d); UpdateData(FALSE); return; } //修改函数 DWORD WINAPI CMfctcpservertest1Dlg::ThreadProc( LPVOID lpParameter) { HWND hWnd1 = (HWND)lpParameter; sockaddr_in addr_bb; sockaddr * addr_client=(sockaddr *)&addr_bb; int len=sizeof(sockaddr); //SOCKET socket_listen = (SOCKET)lpParameter; socket_connect=accept(socket_listen,addr_client,&len); static char buf_recv[1000]={'\0'}; int count =recv(socket_connect,buf_recv,sizeof(buf_recv)-1,0); // m_edit_show.Format("%s",buf_recv); // AfxMessageBox(buf_recv); ::PostMessage(hWnd1, WM_RECV_DATA, (WPARAM)buf_recv, NULL); // WPARAM a; return 0; } void CMfctcpservertest1Dlg::OnButtonOpenserver() { // TODO: Add your control notification handler code here socket_listen=socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in addr_b; addr_b.sin_addr.S_un.S_addr=htonl(INADDR_ANY); addr_b.sin_port=6588; addr_b.sin_family=AF_INET; struct sockaddr * addr=(struct sockaddr *)&addr_b; bind(socket_listen,addr,sizeof(SOCKADDR)); int flag = listen(socket_listen,4); HANDLE hThread = CreateThread(NULL, 0, ThreadProc, (LPVOID)m_hWnd, 0, NULL); CloseHandle(hThread); //让子线程数目减1,如果不加,可能内存泄漏 GetDlgItem(IDC_BUTTON_OPENSERVER)->EnableWindow(FALSE); GetDlgItem(IDC_BUTTON_CLOSESERVER)->EnableWindow(TRUE); } void CMfctcpservertest1Dlg::OnButtonCloseserver() { // TODO: Add your control notification handler code here closesocket(socket_connect); closesocket(socket_listen); GetDlgItem(IDC_BUTTON_OPENSERVER)->EnableWindow(TRUE); GetDlgItem(IDC_BUTTON_CLOSESERVER)->EnableWindow(FALSE); }