任务二 简单的聊天程序
一、要求
1.编写两对简单的一对一聊天程序,分别为面向连接方式和无连接方式(我选用无连接方式)
2.当开始通信后,一方先从键盘获得用户字符串,发送到另一方,然后等待对方数据,显示,再循环这个过程
3.而另一方则是先等待对方字符串,显示;然后从键盘获得用户字符串,发送,再循环这个过程。
2.当开始通信后,一方先从键盘获得用户字符串,发送到另一方,然后等待对方数据,显示,再循环这个过程
3.而另一方则是先等待对方字符串,显示;然后从键盘获得用户字符串,发送,再循环这个过程。
进阶要求
双方可以自由聊天,即随时都可以输入或显示对方的数据软件具有图形化界面
二、与任务相关的实验
05-套接字接口函数
07-隐式绑定
11-多路复用
12-阻塞与非阻塞
13-并发服务
07-隐式绑定
11-多路复用
12-阻塞与非阻塞
13-并发服务
三、完成过程
1、服务器端
这个任务我采用的无连接的方式完成。
通信的基本步骤:
1.服务器开启,申请套接字,确定本地端点,默认4660。
2.开启的时候,额外开启一个线程,用于接收来自客户端的消息,并显示在聊天记录中。
3.点击发送按钮,可以将发送消息框中的消息发送出去,并显示在聊天记录中。
以下为部分代码(
东拼乱凑的感觉,希望大神指导~~)
//定义的部分变量
SOCKET s;
sockaddr_in local;
sockaddr_in remote;
int len;
CString sendbuf;
char recvbuf[128];
WSAData wsa;
CString buffer; //存放一次的消息
static CString save; //存放聊天记录
//点击启动按钮后的执行代码
void CTanc_mfc_chat_serverDlg::OnRunbutton()
{
WSAStartup(0x101,&wsa);
s = socket(AF_INET,SOCK_DGRAM,0); //申请套接字
local.sin_family = AF_INET;
local.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //0.0.0.0
local.sin_port = htons(0x1234); //port 4660
bind(s,(sockaddr*)&local,sizeof(local));
//创建服务线程
CreateThread(NULL, 0 ,ThreadServer, NULL, 0, NULL);
AfxMessageBox("服务器已启动");
}
//线程部分的代码,用于接收消息 并显示在聊天记录中
DWORD WINAPI ThreadServer(LPVOID lpParameter)
{
while(TRUE)
{
len = sizeof(remote);
recvfrom(s,recvbuf,sizeof(recvbuf),0,(sockaddr*)&remote,&len);
buffer.Format(_T("客户说 :%s\r\n"),recvbuf);
save = save + buffer;
buffer.Empty();
buffer.FreeExtra();
theApp.m_pMainWnd->GetDlgItem(IDC_MESSAGE)->SetWindowText(save);
}
closesocket(s); //关闭套接字
WSACleanup();
}
//点击发送按钮执行的代码。将消息发送出去,并显示在聊天记录中
void CTanc_mfc_chat_serverDlg::OnSendbutton()
{
//显示在聊天记录中
UpdateData(TRUE);
buffer.Format(_T("我说 :%s\r\n"),m_sendMessage);
save = save + buffer;
buffer.Empty();
buffer.FreeExtra();
m_message = save;
UpdateData(FALSE);
//发送消息
sendbuf = m_sendMessage;
len = sizeof(remote);
sendto(s,sendbuf,strlen(sendbuf),0,(sockaddr*)&remote,len);
}
效果图:
二、客户端
客户端需要输入服务器IP地址和端口号,然后输入消息,进行发送。
//点击发送执行的代码
void CTanc_mfc_chat_clientDlg::OnSend()
{
//获取IP、端口号和发送的消息
UpdateData(TRUE);
BYTE nf1, nf2, nf3, nf4;
m_serverIPAddress.GetAddress(nf1, nf2, nf3, nf4);
sendbuf = m_sendMessage;
int port = m_port;
buffer.Format(_T("我说 :%s\r\n"),m_sendMessage);
save = save + buffer;
buffer.Empty();
buffer.FreeExtra();
WSAStartup(0x101,&wsa);
s = socket(AF_INET,SOCK_DGRAM,0);
local.sin_family = AF_INET;
local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
local.sin_port = htons(0x3412);
bind(s,(sockaddr*)&local,sizeof(local));
//确定IP和端口号
remote.sin_family = AF_INET;
remote.sin_addr.S_un.S_un_b.s_b1 = nf1;
remote.sin_addr.S_un.S_un_b.s_b2 = nf2;
remote.sin_addr.S_un.S_un_b.s_b3 = nf3;
remote.sin_addr.S_un.S_un_b.s_b4 = nf4;
remote.sin_port = htons(port);
len = sizeof(remote);
sendto(s,sendbuf,strlen(sendbuf),0,(sockaddr*)&remote,len);
m_message = save;
UpdateData(FALSE);
//创建服务线程
CreateThread(NULL, 0 ,ThreadClient, NULL, 0, NULL);
}
//用于显示接收消息的线程
DWORD WINAPI ThreadClient(LPVOID lpParameter)
{
while(TRUE)
{
len = sizeof(remote);
recvfrom(s,recvbuf,sizeof(recvbuf),0,(sockaddr*)&remote,&len);
buffer.Format(_T("服务器说 :%s\r\n"),recvbuf);
save = save + buffer;
buffer.Empty();
buffer.FreeExtra();
theApp.m_pMainWnd->GetDlgItem(IDC_MESSAGE)->SetWindowText(save);
}
closesocket(s);
WSACleanup();
}
效果图:
四、遇见的一些问题
1、CString类的buffer存放一次消息时,前次消息长于后次,前次多余的部分也会显示。如图:
//解决方法
buffer.Empty();
buffer.FreeExtra();
buffer = ""; //这种不行。
2、获取和写入界面数据的方法。
/*
用到了两种方法:
一个是获取ID的方法
二个是UpdateData()方法
*/
theApp.m_pMainWnd->GetDlgItem(IDC_MESSAGE)->SetWindowText(save);
UpdateData(TRUE);
UpdateData(FALSE);
//我表示没怎么区别搞懂,求具体的文档之类的~~~~
3、待续~~~
五、小结
基本完成了任务的要求,用线程实现了自由聊天,即随时都可以输入或显示对方的数据。对于C++还是不够了解,代码感觉好乱。