网络编程实验报告
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
网络编程实验报告
实验一TCP Socket API程序设计
一、预备知识
1.网络编程基本概念
网络上的计算机间的通讯,实质上是网络中不同主机上的程序之间的通讯。
在互联网中使用IP地址来标识不同的主机,在网络协议中使用端口号来标识主机上不同进程,即使用(IP地址,端口号)二元组。
套接字(Socket)用于描述IP地址和端口,是一个通信链的句柄,通信时一个网络程序将要传输的一段信息写入它所在主机的Socket中,该Socket通过与网络接口卡相连的传输介质将这段信息发送到另一台主机的Socket中,以供其他程序使用。
图1-1 TCP通信流程
2.TCP通信流程
TCP程序是面向连接的,程序运行后,服务器一直处于监听状态,客户端与
服务器通信之前必须首先发起连接请求,由服务器接收请求并在双方之间建立连接后才可以互相通信。
二、实验目的
1.了解Winsock API编程原理;
2.掌握TCP Socket程序的编写;
3.了解C/S模式的特点;
4.学会解决实验中遇到的问题。
三、实验任务
使用Winsock API相关类实现TCP Socket通信程序,并能成功运行。
四、实验环境及工具
1. Windows2000/XP/7
2. Visual C++开发平台
3. Visual Studio2010
五、实验内容和步骤
参照《Visual C++网络编程教程》书中81页,TCP Socket API 程序设计。
连接:
void CChatClientDlg::OnConnect()
{
WSADATA wsd; //WSADATA结构
WSAStartup(MAKEWORD(2,2),&wsd); //加载协议,使用Winsock 2.2版
m_client = socket(AF_INET,SOCK_STREAM,0); //创建流式套接字
//服务器地址
sockaddr_in serveraddr;
UpdateData();
if(ServerIP.IsBlank())
{
AfxMessageBox("请指定服务器IP!");
return;
}
if(sPort.IsEmpty())
{
AfxMessageBox("请指定端口!");
return;
}
//获取服务器进程的IP和端口
BYTE nFild[4];
CString sIP;
ServerIP.GetAddress(nFild[0],nFild[1],nFild[2],nFild[3]);
sIP.Format("%d.%d.%d.%d",nFild[0],nFild[1],nFild[2],nFild[3]);
//设置服务器地址结构的内容
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.S_un.S_addr = inet_addr(sIP);
serveraddr.sin_port = htons(atoi(sPort));
//发起连接须指明要访问的服务器进程地址,这个地址存储在serveraddr中if(connect(m_client,(sockaddr*)&serveraddr,sizeof(serveraddr)) ! = 0)
{
MessageBox("连接失败");
return;
}
else
{
m_ListWords.AddString("连接服务器成功!");
m_ListWords.SetT opIndex(m_ListWords.GetCount() - 1);
}
WSAAsyncSelect(m_client,m_hWnd,10000,FD_READ|FD_CLO SE); //①
//界面完善
ServerIP.EnableWindow(false);
ServerPort.EnableWindow(false);
m_ButtonConnect.EnableWindow(false);
m_ButtonDisconnect.EnableWindow();
m_EditWords.EnableWindow();
m_ButtonSend.EnableWindow();
m_ButtonExit.EnableWindow(false);
m_ButtonClear.EnableWindow();
}
“断开”按钮的事件过程代码如下:
void CChatClientDlg::OnDisconnect()
{
//断开与服务器的连接
closesocket(m_client);
m_ListWords.AddString("从服务器断开");
m_ListWords.SetT opIndex(m_ListWords.GetCount() - 1);
ServerIP.EnableWindow();
ServerPort.EnableWindow();
m_ButtonConnect.EnableWindow();
m_ButtonDisconnect.EnableWindow(false);
m_EditWords.EnableWindow(false);
m_ButtonSend.EnableWindow(false);
m_ButtonExit.EnableWindow();
}
“发送”按钮事件过程代码如下:
void CChatClientDlg::OnSend()
{
//向服务器发送信息
UpdateData();
if(m_sWords.IsEmpty())
{
AfxMessageBox("发送的消息不能为空!");
return;
}
//开始发送数据
int i = send(m_client,m_sWords.GetBuffer(0),m_sWords.GetLength(),0);
m_ListWords.AddString("发送:" + m_sWords);
m_ListWords.SetT opIndex(m_ListWords.GetCount() - 1);
}
BOOL CChatClientDlg::PreTranslateMessage(MSG* pMsg)
{
if(pMsg->message == 10000) //识别应用程序中定义的消息号{
switch(pMsg->lParam) //判断网络事件类型
{
case FD_READ:
this->ReceiveData(); //若为FD_READ则接收数据
break;
case FD_CLOSE:
this->CloseSock(); //如果服务端断开,客户端也断开
break;
}
}
else
return CDialog::PreTranslateMessage(pMsg);
}
下面来实现ReceiveData()和CloseSock()方法。
首先在头文件ChatClientDlg.h中的类CChatClientDlg中定义这两个方法:void ReceiveData();
void CloseSock();
然后分别编写这两个方法的代码。
ReceiveData()的代码如下:
void CChatClientDlg::ReceiveData()
{
char buffer[1024];
//接收服务器传来的数据
int num = recv(m_client,buffer,1024,0); //函数recv()接收数据
buffer[num] = '\\0';
//将接收的数据添加到列表框中
CString sTemp;
sTemp.Format("收到:%s",buffer);
//接收完数据后继续侦测
WSAAsyncSelect(m_client,m_hWnd,10000,FD_READ|FD_CLO SE);
m_ListWords.AddString(sTemp);
m_ListWords.SetT opIndex(m_ListWords.GetCount()-1);
}
CloseSock()的代码如下:
void CChatClientDlg::CloseSock()
{
m_ListWords.AddString("服务器断开了");
m_ListWords.SetT opIndex(m_ListWords.GetCount() - 1);
closesocket(m_client);
ServerIP.EnableWindow();
ServerPort.EnableWindow();
m_ButtonConnect.EnableWindow();
m_ButtonDisconnect.EnableWindow(false);
m_EditWords.EnableWindow(false);
m_ButtonSend.EnableWindow(false);
m_ButtonExit.EnableWindow();
}
“清空”按钮的事件过程:
m_ListWords.ResetContent();
“关于”按钮的事件过程:
CAboutDlg dlgAbout;
dlgAbout.DoModal();
服务器端:
开始监听代码:
void CChatServerDlg::OnListen()
{
WSADATA wsd; //WSADATA结构
WSAStartup(MAKEWORD(2,2),&wsd); //加载协议栈,使用Winsock 2.2版
m_server = socket(AF_INET,SOCK_STREAM,0); //创建流式套接字
//将网络中的事件关联到窗口的消息函数中,定义消息号为20000,侦测客户端的连接请求
WSAAsyncSelect(m_server,m_hWnd,20000,FD_ACCEPT);
m_client = 0;
BYTE nFild[4];
CString sIP;
UpdateData();
if(ServerIP.IsBlank())
{
AfxMessageBox("请设置IP地址!");
return;
}
if(sPort.IsEmpty())
{
AfxMessageBox("请设置监听端口!");
return;
}
ServerIP.GetAddress(nFild[0],nFild[1],nFild[2],nFild[3]);
sIP.Format("%d.%d.%d.%d",nFild[0],nFild[1],nFild[2],nFild[3]);
//服务器地址
sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.S_un.S_addr = inet_addr(sIP); serveraddr.sin_port = htons(atoi(sPort));
//绑定地址
if (bind(m_server,(sockaddr*)&serveraddr,sizeof(serveraddr))) {
MessageBox("绑定地址失败.");
return;
}
//监听开始,服务器等待连接请求的到来
listen(m_server,5);
m_ListWords.AddString("监听开始:");
m_ListWords.AddString("地址" + sIP + " 端口" + sPort);
m_ListWords.AddString("等待客户端连接……");
//界面完善
m_ListWords.SetT opIndex(m_ListWords.GetCount()-1); ServerIP.EnableWindow(false);
ServerPort.EnableWindow(false);
m_ButtonListen.EnableWindow(false);
m_ButtonStopListen.EnableWindow();
m_ButtonClear.EnableWindow();
m_ButtonExit.EnableWindow(false);
}
“停止监听”按钮事件过程代码如下:
void CChatServerDlg::OnStopListen()
{
//停止监听
closesocket(m_server);
m_ListWords.AddString("停止监听");
m_ListWords.SetT opIndex(m_ListWords.GetCount() - 1); ServerIP.EnableWindow();
ServerPort.EnableWindow();
m_ButtonListen.EnableWindow();
m_ButtonStopListen.EnableWindow(false);
m_ButtonExit.EnableWindow();
}
“发送”按钮事件过程代码如下:
void CChatServerDlg::OnSend()
{
//向服务器发送信息
UpdateData();
if(m_sWords.IsEmpty())
{
AfxMessageBox("发送的消息不能为空!");
return;
}
//开始发送数据
int i = send(m_client,m_sWords.GetBuffer(0),m_sWords.GetLength(),0);
m_ListWords.AddString("发送:" + m_sWords);
m_ListWords.SetT opIndex(m_ListWords.GetCount() - 1);
}
“断开”按钮事件过程代码如下:
void CChatServerDlg::OnDisconnect()
{
closesocket(m_client);
m_ListWords.AddString("与客户端断开");
m_ListWords.SetT opIndex(m_ListWords.GetCount() - 1);
m_ButtonDisconnect.EnableWindow(false);
m_EditWords.EnableWindow(false);
m_ButtonSend.EnableWindow(false);
}
在头文件ChatServerDlg.h的类CChatServerDlg中声明:void HandleData(); //用于接收客户端的连接请求
void ReceiveData();
void CloseSock();
首先为主对话框对象添加PreTranslateMessage()方法。
BOOL CChatServerDlg::PreTranslateMessage(MSG* pMsg) {
if (pMsg->message == 20000) //识别消息号
{
this->HandleData(); //接收客户端的连接请求}
else if(pMsg->message == 30000) //识别消息号
{
switch(pMsg->lParam)
{
case FD_READ:
this->ReceiveData(); //接收数据
break;
case FD_CLOSE:
this->CloseSock(); //断开连接
break;
}
}
else
return CDialog::PreTranslateMessage(pMsg);
}
调用HandleData()函数,其代码如下:
void CChatServerDlg::HandleData()
{
sockaddr_in serveraddr;
int len = sizeof(serveraddr);
m_client = accept(m_server,(struct sockaddr*)&serveraddr,&len);
//在接收请求并为客户端创建对应的套接字后,就开始在该套接字上侦测//FD_READ和FD_CLOSE事件,指定消息号为30000 WSAAsyncSelect(m_client,m_hWnd,30000,FD_READ|FD_CLO SE);
m_ListWords.AddString("接收了一个客户端的连接请求");
m_ListWords.SetT opIndex(m_ListWords.GetCount() - 1);
//界面完善
m_ButtonDisconnect.EnableWindow();
m_EditWords.EnableWindow();
m_ButtonSend.EnableWindow();
}
ReceiveData()函数代码如下:
void CChatServerDlg::ReceiveData()
{
//接收客户端的数据
char buffer[1024];
int num = recv(m_client,buffer,1024,0);
buffer[num] = 0;
CString sTemp;
sTemp.Format("收到:%s",buffer);
m_ListWords.AddString(sTemp); //显示信息
m_ListWords.SetT opIndex(m_ListWords.GetCount() - 1);
//继续侦测客户端的动向
WSAAsyncSelect(m_client,m_hWnd,30000,FD_READ|FD_CLO SE);
}
CloseSock()函数代码如下:
void CChatServerDlg::CloseSock()
{
//关闭与客户端的通信信道
m_ListWords.AddString("客户端断开连接");
m_ListWords.SetT opIndex(m_ListWords.GetCount() - 1);
closesocket(m_client); //关闭与客户端通信的Socket
WSAAsyncSelect(m_server,m_hWnd,20000,FD_ACCEPT);//准备接收新的客户端连接
//界面完善
m_ButtonDisconnect.EnableWindow(false);
m_EditWords.EnableWindow(false);
m_ButtonSend.EnableWindow(false);
}
服务器的初始化代码如下:
//界面初始化
m_ButtonStopListen.EnableWindow(false);
m_ButtonDisconnect.EnableWindow(false);
m_ButtonClear.EnableWindow(false);
m_EditWords.EnableWindow(false);
m_ButtonSend.EnableWindow(false);
运行结果:
六、思考题
1.用Winsock API编程时,主要进行哪些通行的操作步骤?
2. 阐述C/S模式的通信过程。
答:
1.通行的操作
1. Winsock的打开(WSAStartup())。
2. 建立套接字(socket()或WSASocket())。
3. 地址绑定(bind())。
4. 服务器监听连接(listen())。
5. 客户端提出连接申请(connect()或WSAConnect ())。
6. 服务器接收客户端的连接请求(accept()或WSAAccept())。
7. 数据的发送(send()或WSASend(),sendto()或WSASendT o())。
8. 数据的接收(recv()或WSARecv(),recvfrom()或WSARecvfrom())。
9. 关闭套接字(closesocket())。
10. 关闭Winsock(WSACleanup())。
2通信过程。