网络程序设计
实验报告
实验名称: UDP通信实验
实验类型:____验证型实验_____ __
指导教师:_____________________
专业班级:_____________________
姓 名:_______________________
学 号:____________________
电子邮件:___________
实验地点:____________
实验日期 2013 年 3 月 29 日
实验成绩:__________________________
一、实验目的
l 进一步理解Winsock API的调用方法
l 了解UDP协议的工作原理
l 掌握UDP服务端程序和客户端程序的编写流程
l 熟悉程序的调试方法。
二、实验设计
根据实验要求如下:
1. 认真理解数据报套接字编程模型,仔细阅读并调试运行UDPserve.cpp程序和UTPClient.cpp程序源代码,分析在服务端和客户端分别使用了哪些Winsock API函数,写入实验报告;
2. 修改UDPServer和UDPClient程序,设计一个简单的UDP通信程序,并达到以下要求:
1双方能相互发送数据,并显示接收到的数据。
2当收到对方的数据为“bye”时,能退出程序。
3. 编程验证实验思考题中问题。
选做,服务器同多个客户端通信
1.首先需要了解一些关于UDP的相关函数
l 数据报套接字编程使用的函数
1) 创建套接字函数socket()
SOCKET socket(int af,int type,int protocol);
由于采用数据报套接字进行数据传输,因此type参数必须设置为SOCK_DGRAM,protocol参数必须设置为IPPROTO_UDP
2) 绑定本地地址到所创建的套接字函数bind()
int bind(SOCKET s,const struct sockaddr* name,int namelen);
在实际编程时可以省略该函数,系统会自动绑定
3) 接收数据函数recvfrom()
int recvfrom(SOCKET s,char* buf,int len,int flags,
struct sockaddr* from,int* fromlen);
4) 发送数据函数sendto()
int sendto(SOCKET s,const char* buf,int len,int flags,
const struct sockaddr* to,int* tolen);
5) 关闭套接字函数closesocket()
int closesocket(SOCKET s);
这些是编写程序时候需要用到的,之后是关于设置通信的流程图
左边的流程图是服务端的流程图, 右边的是客户端的流程图
2.然后根据流程图编写程序函数
三、实验过程
1. 实验结果下图为程序运行的时候客户端口的界面
实验结果下图为程序运行的时候服务端口的界面
四、讨论与分析
1能否在接收数据之间不进行bind()调用?如果能,请说明可能的情况。
2能否使用connect()连接对方?为什么?
③能否在不调用sendto()函数之前调用recvfom()函数
1 答:可以。如果首先调用sendto函数,则可以不调用bind函数显示地绑定到本地地址,系统会自动地为程序绑定。
2 答:可以使用connect接连对方。如果希望为一个数据报套接字指定唯一的通信方时,可以使用connect来实现这一功能。需要注意的是,在数据报套接字上使用connect并不是建立连接,不存在“握手”的过程。仅仅是为这个套接字指定一个通信方,一旦指定了对方的地址,就可以通过send/recv来发送/接收数据了。而且可以在这个数据报套接字上多次调用connect函数来指定不同的通信方。
③ 答:实验证明可以在调用sendto函数之前调用recvform函数。当s已经被显示地绑定了本地地址后,调用recvfrom函数将默认在阻塞模式下进行,即将一直等待信息,直到成功接受到数据。
五、实验者自评
实验的设计过程是根据实验指导书的流程来做的,设计过程要考虑诸多的因素,需要全方位考虑函数的编写,因为这次要涉及两个程序的编写,先了解实验的要求,然后熟悉相应的函数知识,设计函数流程图,之后根据流程图进行编码的编写,其中运用了一些套接字的相关知识,实验过后,我对基本的windsock API函数的使用方法和实现原理有了更深入的了解,对课本上的理论知识有了更好的巩固。在实验过程中,熟悉了网络编程方法,对自己的编程动手能力有了较好的锻炼和提高,实验的最后检测,也提高了我实践的能力,很感谢这次的实验让我了解了通信的相互关系,终于知道了所谓的QQ的通信原理是怎么回事,也对这方面产生了浓厚的兴趣,但是自己编程能力还是有一点点差的啦,争取以后好好学习关于通信这方面的知识,好好熟读课本,将知识内化为自己的一部分,运用到实践中去!
六、附录:关键代码
Client端口
// 创建套节字
SOCKET s = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(s == INVALID_SOCKET)
{
printf("Failed socket() %d \n", ::WSAGetLastError());
return 0;
}
// 也可以在这里调用bind函数绑定一个本地地址
// 否则系统将会自动安排
// 填写远程地址信息
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(4567);
// 注意,这里要填写服务器程序所在机器的IP地址
// 如果你的计算机没有联网,直接使用127.0.0.1即可
addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
// 发送数据
printf(" client start\n");
char szText[] = " TCP Server Demo! \r\n";
::sendto(s, szText, strlen(szText), 0, (sockaddr*)&addr, sizeof(addr));
// 接收数据
char buff[1024];
int nLen = sizeof(addr);
while(TRUE)
{
int nRecv = ::recvfrom(s, buff, 1024, 0, (sockaddr*)&addr, &nLen);
if(nRecv > 0)
{
buff[nRecv] = '\0';
printf(" 接收到数据(%s):%s", ::inet_ntoa(addr.sin_addr), buff);
char sendmsg[1000];
printf("\ninput message: ");
scanf("%s",sendmsg);
sendmsg[strlen(sendmsg)] = '\0';//从套接字发送信息
::sendto(s, sendmsg, strlen(sendmsg), 0, (sockaddr*)&addr, sizeof(addr));
/*if(strcmp(sendmsg,"bye"==0))
{
break;
}*/
}
}
::closesocket(s);
return 0;
第二篇:UDP和TCP网络实验报告
西安理工大学高科学院
TCP\UDP程序开发
实验报告
姓名:
专业:信息系统与信息管理
班级:
学号:
日期:
1:实验题目:基于UDP\TCP的聊天程序开发
2:实验目的:
开发TCP\UDP协议应用程序,掌握网络应用程序的工作原理。通过本实验,深入理解TCP和UDP协议的异同点,了解网络协议的工作过程,学会网络通讯编程的基本方法,能够编制网络应用程序。
3.实验报告内容:
(实验程序与注解如下)
namespace ChatClient
{
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
public class Class1
{
//UDPClient对象
private static UdpClient m_Client;
//本地和远程端口
private static int LocalPort = 8080;
private static int RemotePort = 8080;
//本地主机名称
private static string m_szHostName;
//广播组地址
private static IPAddress m_GroupAddress;
//远程广播组端
private static IPEndPoint m_RemoteEP;
//跟踪用户是否退出程序
private static bool m_Done = false;
//显示使用方法
public static void Usage()
{
Console.WriteLine("UDP Multicast Chat Utility");
Console.WriteLine("\nUsage:");
Console.WriteLine("chat.exe");
}
public static void Initialize()
{
//初始化UDPClient对象
m_Client = new UdpClient(LocalPort);
//创建多目标广播组对象
m_GroupAddress = IPAddress.Broadcast;
//加入组
m_Client.JoinMulticastGroup(m_GroupAddress);
//创建远程广播组端
m_RemoteEP = new IPEndPoint( m_GroupAddress, RemotePort);
}
public static void Terminate()
{
//退出广播组
m_Client.DropMulticastGroup(m_GroupAddress);
}
public static void Listener()
{
//确保主线程开始接受用户输入
Thread.Sleep(2000);
//ASCII编码
Encoding ASCII = Encoding.ASCII;
//循环中不断接受数据
while(!m_Done)
{
IPEndPoint endpoint = null;
//接受数据
Byte[] data = m_Client.Receive(ref endpoint);
//得到数据的ASCII字符串形式
String strData = ASCII.GetString(data);
//如果包含:@
if( strData.IndexOf(":@") > 0 )
{
//先检查这个结束消息是否来自主线程还是来自其他聊天客户
Char [] separators = {':'};
String [] vars = strData.Split(separators);
//如果是本地机器
if( vars[0] == m_szHostName )
{
//结束线程
Console.WriteLine("shutting down Listener thread...");
//在这种情况下主线程已经把m_Done设为true,
//这里为了保险起见,再次设置m_Done为true
m_Done = true;
}
else
{
//显示消息,某个聊天客户已经离开
Console.WriteLine("{0} has left the conversation", vars[0]);
}
}
else
{
//如果这是一条聊天消息,则检查是否来自
//本地机器,如果不是,则显示这条消息
if(strData.IndexOf(":") > 0)
{
Char [] separators = {':'};
String [] vars = strData.Split(separators);
if( vars[0] != m_szHostName )
{
Console.WriteLine(strData);
}
}
}
}
Console.WriteLine("Listener thread finished...");
return;
}
public static int Main( String [] args )
{
if( args.Length > 0 )
{
//显示使用方法信息
Usage();
return 1;
}
//得到主机名称和地址
//m_szHostName = Dns.GetHostName(); IT超人
m_szHostName = "127.0.0.1";
//初始化
Console.WriteLine("Initializing...");
Initialize();
//开始监听线程
Console.WriteLine("Starting Listener thread...");
Thread t = new Thread(new ThreadStart(Listener));
t.Start();
//发送数据使用的缓冲区
Byte [] buffer = null;
//编码格式
Encoding ASCII = Encoding.ASCII;
//记录用户是否按下“@”键
bool m_ShuttingDown = false;
//循环语句中读取用户的输入并发送消息
while(!m_ShuttingDown)
{
String s = Console.ReadLine();
//没有输入继续循环
if( s.Length == 0 )
continue;
//如果第一个字母为@,则推出循环
if(String.Compare(s,0,"@",0,1) == 0)
{
//停止监听线程中的循环
m_Done = true;
//向其他客户发送结束消息
s = m_szHostName + ":@";
m_ShuttingDown = true;
}
else
{
s = m_szHostName + ":" + s;
}
//分配发送缓冲区空间
buffer = new Byte[s.Length + 1];
//把字符串内容以ASCII编码格式放入发送缓冲区中
int len = ASCII.GetBytes( s.ToCharArray(), 0, s.Length, buffer, 0);
//发送消息
int ecode = m_Client.Send(buffer, len, m_RemoteEP);
//发送失败
if(ecode <= 0)
{
Console.WriteLine("Error in send : " + ecode);
}
}
//结束监听线程
t.Abort();
t.Join();
namespace ChatClient
{
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
public class Class1
{
//UDPClient对象
private static UdpClient m_Client;
//本地和远程端口
private static int LocalPort = 8080;
private static int RemotePort = 8080;
//本地主机名称
private static string m_szHostName;
//广播组地址
private static IPAddress m_GroupAddress;
//远程广播组端
private static IPEndPoint m_RemoteEP;
//跟踪用户是否退出程序
private static bool m_Done = false;
//显示使用方法
public static void Usage()
{
Console.WriteLine("UDP Multicast Chat Utility");
Console.WriteLine("\nUsage:");
Console.WriteLine("chat.exe");
}
public static void Initialize()
{
//初始化UDPClient对象
m_Client = new UdpClient(LocalPort);
//创建多目标广播组对象
m_GroupAddress = IPAddress.Broadcast;
//加入组
m_Client.JoinMulticastGroup(m_GroupAddress);
//创建远程广播组端
m_RemoteEP = new IPEndPoint( m_GroupAddress, RemotePort);
}
public static void Terminate()
{
//退出广播组
m_Client.DropMulticastGroup(m_GroupAddress);
}
public static void Listener()
{
//确保主线程接受用户输入
Thread.Sleep(2000);
//ASCII编码
Encoding ASCII = Encoding.ASCII;
//循环中不断接受数据
while(!m_Done)
{
IPEndPoint endpoint = null;
//接受数据
Byte[] data = m_Client.Receive(ref endpoint);
//得到数据的ASCII字符串
String strData = ASCII.GetString(data);
//如果包含:@
if( strData.IndexOf(":@") > 0 )
{
//先检查这个结束消息是否来自主线程还是来自其他聊天客户
Char [] separators = {':'};
String [] vars = strData.Split(separators);
//如果是本地机器
if( vars[0] == m_szHostName )
{
//结束线程
Console.WriteLine("shutting down Listener thread...");
//在这种情况下主线程已经把m_Done设为true,
//这里为了保险起见,再次设置m_Done为true
m_Done = true;
}
else
{
//显示消息,某个客户已下线
Console.WriteLine("{0} has left the conversation", vars[0]);
}
}
else
{
//如果这是一条聊天消息,则检查是否来自
//本地机器,如果不是,则显示这条消息
if(strData.IndexOf(":") > 0)
{
Char [] separators = {':'};
String [] vars = strData.Split(separators);
if( vars[0] != m_szHostName )
{
Console.WriteLine(strData);
}
}
}
}
Console.WriteLine("Listener thread finished...");
return;
}
public static int Main( String [] args )
{
if( args.Length > 0 )
{
//显示使用方法信息
Usage();
return 1;
}
//得到主机名称和地址
//m_szHostName = Dns.GetHostName(); 123
m_szHostName = "127.0.0.1";
//初始化
Console.WriteLine("Initializing...");
Initialize();
//开始监听
Console.WriteLine("Starting Listener thread...");
Thread t = new Thread(new ThreadStart(Listener));
t.Start();
//发送数据使用的缓冲区
Byte [] buffer = null;
//编码格式
Encoding ASCII = Encoding.ASCII;
//记录用户是否按下“@”键
bool m_ShuttingDown = false;
//循环语句中读取用户的输入并发送消息
while(!m_ShuttingDown)
{
String s = Console.ReadLine();
//没有输入继续循环
if( s.Length == 0 )
continue;
//如果第一个字母为@,则推出循环
if(String.Compare(s,0,"@",0,1) == 0)
{
//停止监听线程中的循环
m_Done = true;
//向其他客户发送结束消息
s = m_szHostName + ":@";
m_ShuttingDown = true;
}
else
{
s = m_szHostName + ":" + s;
}
//分配发送缓冲区空间
buffer = new Byte[s.Length + 1];
int len = ASCII.GetBytes( s.ToCharArray(), 0, s.Length, buffer, 0);
//发送
int ecode = m_Client.Send(buffer, len, m_RemoteEP);
//发送失败
if(ecode <= 0)
{
Console.WriteLine("Error in send : " + ecode);
}
}
//结束监听
t.Abort();
t.Join();
//中断连接
Console.WriteLine("Closing connection...");
Terminate();
return 0;
}
}
}
程序结束
4系统测试:
通过单机和联机状态下的实验,本程序基本达到了实验目的的要求。
完成了网络中的简单通讯,实现了对UDO\TCP熟练运用的目的。