文档库 最新最全的文档下载
当前位置:文档库 › 网络编程实验报告

网络编程实验报告

网络编程实验报告
网络编程实验报告

实验一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.SetTopIndex(m_ListWords.GetCount() - 1);

}

WSAAsyncSelect(m_client,m_hWnd,10000,FD_READ|FD_CLOSE); //①

//界面完善

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.SetTopIndex(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.SetTopIndex(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_CLOSE);

m_ListWords.AddString(sTemp);

m_ListWords.SetTopIndex(m_ListWords.GetCount()-1);

}

CloseSock()的代码如下:

void CChatClientDlg::CloseSock()

{

m_ListWords.AddString("服务器断开了");

m_ListWords.SetTopIndex(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.SetTopIndex(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.SetTopIndex(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.SetTopIndex(m_ListWords.GetCount() - 1);

}

“断开”按钮事件过程代码如下:

void CChatServerDlg::OnDisconnect()

{

closesocket(m_client);

m_ListWords.AddString("与客户端断开");

m_ListWords.SetTopIndex(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_CLOSE);

m_ListWords.AddString("接收了一个客户端的连接请求");

m_ListWords.SetTopIndex(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.SetTopIndex(m_ListWords.GetCount() - 1);

//继续侦测客户端的动向

WSAAsyncSelect(m_client,m_hWnd,30000,FD_READ|FD_CLOSE);

}

CloseSock()函数代码如下:

void CChatServerDlg::CloseSock()

{

//关闭与客户端的通信信道

m_ListWords.AddString("客户端断开连接");

m_ListWords.SetTopIndex(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()或WSASendTo())。

8. 数据的接收(recv()或WSARecv(),recvfrom()或WSARecvfrom())。

9. 关闭套接字(closesocket())。

10. 关闭Winsock(WSACleanup())。

2通信过程

相关文档