UDP编程.docx
《UDP编程.docx》由会员分享,可在线阅读,更多相关《UDP编程.docx(8页珍藏版)》请在冰豆网上搜索。
UDP编程
讲义6:
UDP编程
1.基本概念
(1)TCP,可靠,传输大小无限制,但是需要连接建立时间,差错控制开销大。
UDP,不可靠,差错控制开销较小,传输大小限制在64K以下,不需要建立连接。
(2)UDP编程涉及的两个类包
中提供DatagramSocket和DatagramPacket支持数据报通信,其中:
DatagramSocket用于在程序之间建立传送数据报的通信连接;
DatagramPacket则用来表示一个数据报。
(2-1)DatagramSocket的构造方法:
DatagramSocket();
DatagramSocket(intprot);
DatagramSocket(intport,InetAddressladdr)
其中,port指明socket所使用的端口号,如果未指明端口号,则把socket连接到本地主机上一个可用的端口。
laddr指明一个可用的本地地址。
给出端口号时要保证不发生端口冲突,否则会生成SocketException类例外。
注意:
上述的两个构造方法都声明抛弃非运行时例外SocketException,程序中必须进行处理,或者捕获、或者声明抛弃。
用数据报方式编写client/server程序时,客户方和服务方首先都要建立一个DatagramSocket对象,用来接收或发送数据报,然后使用DatagramPacket类对象作为传输数据的载体。
(2-2)DatagramPacket的构造方法:
DatagramPacket(bytebuf[],intlength);
DatagramPacket(bytebuf[],intlength,InetAddressaddr,intport);
DatagramPacket(byte[]buf,intoffset,intlength);
DatagramPacket(byte[]buf,intoffset,intlength,InetAddressaddress,intport);
其中,buf中存放数据报数据,length为数据报中数据的长度,addr和port指明目的地址,offset指明数据报的位移量。
在接收数据前,应该采用上面的第一种方法生成一个DatagramPacket对象,给出接收数据的缓冲区及其长度。
然后调用DatagramSocket的方法receive()等待数据报的到来,receive()将一直等待,直到收到一个数据报为止。
DatagramPacketpacket=newDatagramPacket(buf,256);
Socket.receive(packet);
发送数据前,先生成一个新的DatagramPacket对象,这时要使用上面的第二种构造方法,在给出存放发送数据的缓冲区的同时,还要给出完整的目的地址,包括IP地址和端口号。
发送数据是通过DatagramSocket方法send()实现的,send()根据数据报的目的地址来寻径,以传递数据报。
DatagramPacketpacket=newDatagramPacket(buf,length,address,port);
Socket.send(packet);
在构造数据报时,要给出InetAddress类参数。
类InetAddress在包中定义,用来表示一个Internet地址,可以通过它提供的类方法getByName()从一个表示主机名的字符串获取该主机的IP地址,然后再获取相应的地址信息。
2.InetAddress类,作用:
代表一个IP地址。
这个类没有构造器但是有几个工厂方法,通过传递不同的参数例如IP,Hostname等得到一个InetAddress实例,下例可以得到机器的IP地址。
import.*;
publicclassTestNet
{
publicstaticvoidmain(String[]args)throwsException
{
InetAddressia=InetAddress.getByName("compaq");
StringipAdr=ia.getHostAddress();
System.out.println(ipAdr);
}
}
3.示例
ex1:
(1)Server监听UDP8000端口
import java.io.*;
import .*;
publicclass UDPServer{
public static void main(String[] args)throws IOException{
DatagramSocket server = new DatagramSocket(8000);System.out.println("Serverstarted...");
byte[] recvBuf = new byte[100];
DatagramPacket recvPacket
= new DatagramPacket(recvBuf , recvBuf.length);
server.receive(recvPacket);
String recvStr = new String(recvPacket.getData() , 0 , recvPacket.getLength());
System.out.println("Hello!
" + recvStr);
int port = recvPacket.getPort();
InetAddress addr = recvPacket.getAddress();
String sendStr = "Hello !
I'm Server";
byte[] sendBuf;
sendBuf = sendStr.getBytes();
DatagramPacket sendPacket
= new DatagramPacket(sendBuf , sendBuf.length , addr , port );
server.send(sendPacket);
server.close();
}
}
(2)Client
import java.io.*;
import .*;
class UDPClient{
public static void main(String[] args)throws IOException{
DatagramSocket client = new DatagramSocket();
String sendStr = "Hello!
I'm Client";
byte[] sendBuf;
sendBuf = sendStr.getBytes();
InetAddress addr = InetAddress.getByName("127.0.0.1");
int port = 8000;
DatagramPacket sendPacket
= new DatagramPacket(sendBuf ,sendBuf.length , addr , port);
client.send(sendPacket);
byte[] recvBuf = new byte[100];
DatagramPacket recvPacket
= new DatagramPacket(recvBuf , recvBuf.length); client.receive(recvPacket);
String recvStr = new String(recvPacket.getData() , 0 ,recvPacket.getLength());
System.out.println("Received:
" + recvStr);
client.close();
}
}
Ex2:
UDP多线程
(1)客户方程序Client.java
importjava.io.*;
import.*;
importjava.util.*;
publicclassClient{
publicstaticvoidmain(String[]args)throwsIOException
{
DatagramSocketsocket=newDatagramSocklet();
//创建数据报套接字
Byte[]buf=newbyte[256];//创建缓冲区
InetAddressaddress=InetAddress.getByName(“localhost”);
//得到Server的IP信息
DatagramPacketpacket=newDatagramPacket(buf,buf.length,address,8000);
//创建DatagramPacket对象
socket.send(packet);//发送
packet=newDatagramPacket(buf,buf.length);
//创建新的DatagramPacket对象,用来接收数据报
socket.receive(packet);//接收
Stringreceived=newString(packet.getData());
//根据接收到的字节数组生成相应的字符串
System.out.println("QuoteoftheMoment:
"+received);
socket.close();//关闭套接口
}
}
(2)服务器方程序:
Server.java
publicclassServer{
publicstaticvoidmain(Stringargs[])throwsjava.io.IOException
{
newServerThread().start();
//启动一个QuoteServerThread线程
}
}
(3)程序ServerThread.java
importjava.io.*;
import.*;
importjava.util.*;
//服务器线程
publicclassQuoteServerThreadextendsThread
{
protectedDatagramSocketsocket=null;
//记录和本对象相关联的DatagramSocket对象
protectedBufferedReaderin=null;
//用来读文件的一个Reader
protectedbooleancontinu=true;
//标志变量,是否继续操作
publicServerThread()throwsIOException{
//无参数的构造函数
this("ServerThread");
//以ServerThread为默认值调用带参数的构造函数
}
publicQuoteServerThread(Stringname)throwsIOException{
super(name);//调用父类的构造函数
socket=newDatagramSocket(8000);
//在端口8000创建数据报套接字
try{
in=newBufferedReader(newFileReader("one.txt"));//打开文件,构造相应BufferReader对象
}
catch(FileNotFoundExceptione){//异常处理
System.err.println("Couldnotopenquotefile.Servingtimeinstead."); //打印出错信息
}
}
publicvoidrun()//线程主体
{
while(continu){
try{
byte[]buf=newbyte[256];//创建缓冲区
DatagramPacketpacket=newDatagramPacket(buf,buf.length);
//由缓冲区构造DatagramPacket对象
socket.receive(packet);//接收数据报
StringdString=null;
if(in==null)dString=newDate().toString();
//如果初始化的时候打开文件失败了,/则使用日期作为要传送的字符串
elsedString=getNext();
//否则调用成员函数从文件中读出字符串
buf=dString.getByte();
//把String转换成字节数组,以便传送
InetAddressaddress=packet.getAddress();
//从Client端传来的Packet中得到Client地址
intport=packet.getPort();//和端口号
packet=newDatagramPacket(buf,buf.length,address,port);
//根据客户端信息构建DatagramPacket
socket.send(packet);//发送数据报
}
catch(IOExceptione){//异常处理
e.printStackTrace();//打印错误栈
continu=false;//标志变量置false,以结束循环
}
}
socket.close();//关闭数据报套接字
}
protectedStringgetNext(){
//成员函数,从文件中读数据
StringreturnValue=null;
try{
if((returnvalue="/in.readLine())="=null){
//从文件中读一行,如果读到了文件尾
in.close();//关闭输入流
continu=false;
//标志变量置false,以结束循环
returnValue="Nomorequotes.Goodbye.";
//置返回值
}//否则返回字符串即为从文件读出的字符串
}
catch(IOEceptione){//异常处理
returnValue="IOExceptionoccurredinserver";
//置异常返回值
}
returnreturnValue;//返回字符串
}
}
ex3:
用数据报进行广播通讯
DatagramSocket只允许数据报发送一个目的地址,包中提供了一个类MulticastSocket,允许数据报以广播方式发送到该端口的所有客户。
MulticastSocket用在客户端,监听服务器广播来的数据。
上面程序作一些修改,利用MulticastSocket实现广播通信。
新程序完成的功能是使同时运行的多个客户程序能够接收到服务器发送来的相同的信息,显示在各自的屏幕上。
1.客户方程序:
MultClient.java
importjava.io.*;
import.*;
importjava.util.*;
publicclassMultClient{
publicstaticvoidmain(Stringargs[])throwsIOException
{
MulticastSocketsocket=newMulticastSocket(6000);
//创建6000端口的广播套接字
InetAddressaddress=InetAddress.getByName("127.0.0.1");
//得到127.0.0.1的地址信息
socket.joinGroup(address);
//使用joinGroup()将广播套接字绑定到地址上
DatagramPacketpacket;
for(inti=0;i<5;i++){
byte[]buf=newbyte[256];//创建缓冲区
packet=newDatagramPacket(buf,buf.length);
//创建接收数据报
socket.receive(packet);//接收
Stringreceived=newString(packet.getData());
//由接收到的数据报得到字节数组,
//并由此构造一个String对象
System.out.println("theMoment:
"+received);
//打印得到的字符串
}//循环5次
socket.leaveGroup(address);
//把广播套接字从地址上解除绑定
socket.close();//关闭广播套接字
}
}
2.服务器方程序:
MultServer.java
publicclassMultServer{
publicstaticvoidmain(Stringargs[])throwsjava.io.IOException
{
newmultServerThread().start();
//启动一个服务器线程
}
}
3.程序multServerThread.java
importjava.io.*;
import.*;
importjava.util.*;
publicclassmultServerThreadextendsServerThread
//从ServerThread继承得到新的服务器线程类multServerThread
{
PrivatelongFIVE_SECOND=5000;//定义常量,5秒钟
publicmultServerThread(Stringname)throwsIOException
{
super("MultServerThread");
//调用父类ServerThread的构造函数
}
publicvoidrun()//重写父类的线程主体
{
while(continu){
//根据标志变量判断是否继续循环
try{
byte[]buf=newbyte[256];//创建缓冲区
StringdString=null;
if(in==null)dString=newDate().toString();
//如果初始化的时候打开文件失败了,则使用日期作为要传送的字符串
elsedString=getNextQuote();
//否则调用成员函数从文件中读出字符串
buf=dString.getByte();
//把String转换成字节数组,以便传送sendit
InetAddressgroup=InetAddress.getByName("127.0.0.1");
DatagramPacketpacket=newDatagramPacket(buf,buf.length,group,6000);
//根据缓冲区,广播地址,和端口号创建DatagramPacket对象
socket.send(packet);//发送该Packet
try{
sleep((long)(Math.random()*FIVE_SECONDS));
//随机等待一段时间,0~5秒之间
}
catch(InterruptedExceptione){}
}
catch(IOExceptione){//异常处理
e.printStackTrace();//打印错误栈
continu=false;//置结束循环标志
}
}
socket.close();//关闭广播套接口
}
}