好了。上一节讲套接字的初始化和接收端线程的编写。这一节我们来写发送端。
发送端的主要功能是将发送编辑器里面的数据发送过去。我们用一个函数来实现。
void SendData(HWND hwnd)
函数首先还是创建一个网络地址。初始化后。得到姓名编辑框的名字和IP编辑框的IP;
然后将数据和 姓名发送给接收端线程。
函数实现如下:
void SendData(HWND hwnd)
{
TCHAR cResult[50];
TCHAR sendBuf[256];//作为单次传输不的超过256. 也就是1K.若要大于它就要分割数据。采writefile和readfile来实现。
TCHAR cResultU[50];
TCHAR cResultI[50];
// 这里我们先得到IP编辑框里面的内容。
GetDlgItemText(hwnd,IDC_EDT_ADDR,cResult,sizeof(cResult));
//初始化发送端的网络地址结构。还是跟前面一样。
//但是我们前面的地址由于是服务端可以接收多人的数据。所以我们用INSOCK_ANY来初始化。
//但是这里我们要先得到对方IP.再赋给网络地址结构。
SOCKADDR_IN addrTo;
//在这里我开始没有用(char*)来转换得不到IP,后来我用MessageBox在它的前面输出后居然可以了。所以我在下面加上(char*)
//就成功了。所以我们经常可以采用MessageBox来看结果。
addrTo.sin_addr.S_un.S_addr = inet_addr((char*)cResult);//记得强制转化为char*
addrTo.sin_family = AF_INET;
addrTo.sin_port = htons(port);
//下面是判断用户是否输入了名字和IP.
GetDlgItemText(hwnd,IDC_EDT_USE,cResultU,sizeof(cResultU));
GetDlgItemText(hwnd,IDC_EDT_ADDR,cResultI,sizeof(cResultI));
if(0 == strcmp(cResultU,""))
{
MessageBox(hwnd,TEXT("请登陆用户名和IP!"),TEXT("错误!"),MB_OK);
return ;
}
if(0 == strcmp(cResultI,""))
{
MessageBox(hwnd,TEXT("请登陆用户名和IP!"),TEXT("错误!"),MB_OK);
return ;
}
//得到发送编辑框里面的数据。
GetDlgItemText(hwnd,IDC_EDT_SEND,sendBuf,sizeof(sendBuf));
if(strcmp(sendBuf,"") == 0)
{
MessageBox(hwnd,TEXT("不能发送看空信息!"),TEXT("错误"),MB_OK);
return ;
}
int retsend;
//这里UDP的我们采用sendto来发送数据。TCP的采用send。
参数跟recvfrom一样。可以对照来学习。
retsend = sendto(m_socket,sendBuf,sizeof(sendBuf),0,
(SOCKADDR*)&addrTo,sizeof(addrTo));
if(retsend <= 0)
{
MessageBox(hwnd,TEXT("发送失败!"),TEXT("失败"),MB_OK);
}
SetDlgItemText(hwnd,IDC_EDT_SEND,"");//最后清空发送编辑框。
}
完工!发送端的函数就这么简单。最基本的聊天程序原理都完了。后面的就是自己怎么去加一些自己喜欢的东西啦。
像我写的。用户名和IP必须在另一个对话框中来填写。是不允许在主对话框中直接的写。所以这就涉及到创建子对话框的内容了。在这里我只是简单的说说。因为主要还是讲网络编程的啦。
首先创建对话框我们有两个函数可以实现。
1.对话框模板资源创建一个无模式的对话框
HWND CreateDialog(HINSTANCE hlnstance,LPCTSTR lpTemplate,HWND hWndParent,DLGPROC IpDialogFunc);
2. 根据对话框模板资源创建一个模态的对话框:
int DialogBox(HINSTANCE hlnstance,LPCTSTRIpTemplate,HWND hWndParent,DLGPROC IpDialogFunc);
关于模式和无模式的观念。这里就不讲了。大家可以参考其他书籍。其实你看两个函数创建的参数都一样。
我这里采用的是模式对话框。
// 调用应用程序实例句柄。这个函数前面已经用过。
HINSTANCE appInstance = (HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE);
//创建子对话框,
DialogBox(appInstance,MAKEINTRESOURCE(IDD_ABOUT),hwnd,(DLGPROC)DialogProc);
第一个是实例的句柄。第二个是资源,我们采用MAKEINTRESOURCE来加载。第三个你要创建子窗口的父窗口的句柄。
当然如果你没有父窗口的话。直接NULL就可以了。最后一个参数是回调函数。也就是程序处理函数。大多是处理消息的。
其实他跟你开始建立的窗口处理函数是一样的。也没什么好讲的。
BOOL CALLBACK DialogProc(HWND hwndDlg,UINT UMsg,WPARAM wParam,LPARAM IParam)
{
TCHAR cResultU[50];
TCHAR cResultI[50];
switch(UMsg)
{
case WM_INITDIALOG:
{
BOOL IsChecked = SendMessage(GetDlgItem(hwndDlg,IDC_CHK),BM_GETCHECK,0,0);
if(IsChecked)
{
SetWindowText(GetDlgItem(hwndDlg,IDC_EDT1),TEXT("流浪的心"));
SetWindowText(GetDlgItem(hwndDlg,IDC_EDT2),TEXT("127.0.0.1"));
EnableWindow(GetDlgItem(hwndDlg,IDC_EDT1),FALSE);
EnableWindow(GetDlgItem(hwndDlg,IDC_EDT2),FALSE);
}
}
break;
case WM_COMMAND:
{
//鼠标点击消息。
int id = wParam;
switch(id)
{
case IDC_CHK:
{
BOOL IsChecked = SendMessage(GetDlgItem(hwndDlg,IDC_CHK),BM_GETCHECK,0,0);
if(IsChecked)
{
SetWindowText(GetDlgItem(hwndDlg,IDC_EDT1),TEXT("侯永华"));
SetWindowText(GetDlgItem(hwndDlg,IDC_EDT2),TEXT("127.0.0.1"));
EnableWindow(GetDlgItem(hwndDlg,IDC_EDT1),FALSE);
EnableWindow(GetDlgItem(hwndDlg,IDC_EDT2),FALSE);
}
else
{
EnableWindow(GetDlgItem(hwndDlg,IDC_EDT1),TRUE);
EnableWindow(GetDlgItem(hwndDlg,IDC_EDT2),TRUE);
SetWindowText(GetDlgItem(hwndDlg,IDC_EDT1),TEXT(""));
SetWindowText(GetDlgItem(hwndDlg,IDC_EDT2),TEXT(""));
}
}
break;
case IDC_BTNOK:
{
GetDlgItemText(hwndDlg,IDC_EDT1,cResultU,sizeof(cResultU));
GetDlgItemText(hwndDlg,IDC_EDT2,cResultI,sizeof(cResultU));
if(0 == strcmp(cResultU,""))
{
MessageBox(hwndDlg,TEXT("用户名不能为空!"),TEXT("错误!"),MB_OK);
return FALSE;
}
if(0 == strcmp(cResultI,""))
{
MessageBox(hwndDlg,TEXT("用户名不能为空!"),TEXT("错误!"),MB_OK);
return FALSE;
}
//再这里不知道为什么我用GetParent(hwndDlg)得不到父句柄 ?
//我现在只能用声明一个全局的父对话框句柄来实现。
SetWindowText(GetDlgItem(Hwnd,IDC_EDT_USE),cResultU);
SetWindowText(GetDlgItem(Hwnd,IDC_EDT_ADDR),cResultI);
EndDialog(hwndDlg,0);
}
break;
case IDC_BTNCAN:
EndDialog(hwndDlg,0);
break;
}
}
break;
case WM_DESTROY:
EndDialog(hwndDlg,0);
break;
default:
break;
}
return 0;//记得返回为零
}
好了。窗口处理 函数也讲完了。比较注意的是两个窗口间的传输数据的问题。还有个窗口控件句柄的问题要理清除。
不要掉混了 。
还有菜单的处理。这里就讲两个函数。一个是标记是否复选:CheckMenuItem,和禁用EnableMenuItem函数。
以前我以为可以用EnableWindow来实现。发现不可以。呵呵。是这连个函数。关于具体的用法大家可以看看MSDN。
很好用的函数哦,你以后会经常用到的。
OK!写完了 。由于是初学有不对的地方希望能告诉我。我写这个主要是对自己所学的做一个总结。看看自己到底学会了什么。有一种温习的作用。而且可以加深自己对知识的理解。明天写多功能记事本程序实现总结。j