Java网络编程

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。

http://52android.blog.51cto.com/2554429/482954


Java对于网络通讯有着非常强大的支持。不仅可以获取网络资源,传递参数到远程服务器,还可以通过Socket对象实现TCP协议,通过DatagramSocket对象实现UDP协议。同时,对于多点广播以及代理服务器也有着非常强大的支持。以下是本人在学习过程中的总结和归纳。


1. Java的基本网络支持


1.1 InetAddress
    Java中的InetAddress是一个代表IP地址的对象。IP地址可以由字节数组和字符串来分别表示,InetAddress将IP地址以对象的形式进行封装,可以更方便的操作和获取其属性。InetAddress没有构造方法,可以通过两个静态方法获得它的对象。代码如下:

 
 

     
     
  1. //根据主机名来获取对应的InetAddress实例  
  2.         InetAddress ip = InetAddress.getByName("www.oneedu.cn");  
  3.         //判断是否可达  
  4.         System.out.println("oneedu是否可达:" + ip.isReachable(2000));   
  5.         //获取该InetAddress实例的IP字符串  
  6.         System.out.println(ip.getHostAddress());  
  7.         //根据原始IP地址(字节数组形式)来获取对应的InetAddress实例  
  8.         InetAddress local = InetAddress.getByAddress(new byte[]  
  9.         {127,0,0,1});  
  10.         System.out.println("本机是否可达:" + local.isReachable(5000));   
  11.         //获取该InetAddress实例对应的全限定域名  
  12.         System.out.println(local.getCanonicalHostName()); 


1.2 URLDecoder和URLEncoder
    这两个类可以别用于将application/x-www-form-urlencoded MIME类型的字符串转换为普通字符串,将普通字符串转换为这类特殊型的字符串。使用URLDecoder类的静态方法decode()用于解码,URLEncoder类的静态方法encode()用于编码。具体使用方法如下。

 
 

     
     
  1. //将application/x-www-form-urlencoded字符串  
  2.         //转换成普通字符串  
  3.                 String keyWord = URLDecoder.decode(  
  4.             "%E6%9D%8E%E5%88%9A+j2ee""UTF-8");  
  5.         System.out.println(keyWord);  
  6.         //将普通字符串转换成  
  7.         //application/x-www-form-urlencoded字符串  
  8.         String urlStr = URLEncoder.encode(  
  9.             "ROR敏捷开发最佳指南" , "GBK");  
  10.         System.out.println(urlStr); 


1.3 URL和URLConnection
    URL可以被认为是指向互联网资源的“指针”,通过URL可以获得互联网资源相关信息,包括获得URL的InputStream对象获取资源的信息,以及一个到URL所引用远程对象的连接URLConnection。
    URLConnection对象可以向所代表的URL发送请求和读取URL的资源。通常,创建一个和URL的连接,需要如下几个步骤:
    a. 创建URL对象,并通过调用openConnection方法获得URLConnection对象;
    b. 设置URLConnection参数和普通请求属性;
    c. 向远程资源发送请求;
    d. 远程资源变为可用,程序可以访问远程资源的头字段和通过输入流来读取远程资源返回的信息。
    这里需要重点讨论一下第三步:如果只是发送GET方式请求,使用connect方法建立和远程资源的连接即可;如果是需要发送POST方式的请求,则需要获取URLConnection对象所对应的输出流来发送请求。这里需要注意的是,由于GET方法的参数传递方式是将参数显式追加在地址后面,那么在构造URL对象时的参数就应当是包含了参数的完整URL地址,而在获得了URLConnection对象之后,就直接调用connect方法即可发送请求。
而POST方法传递参数时仅仅需要页面URL,而参数通过需要通过输出流来传递。另外还需要设置头字段。以下是两种方式的代码。

 
 

     
     
  1. //1. 向指定URL发送GET方法的请求  
  2. String urlName = url + "?" + param;  
  3.             URL realUrl = new URL(urlName);  
  4.             //打开和URL之间的连接  
  5.             URLConnection conn = realUrl.openConnection();  
  6.             //设置通用的请求属性  
  7.             conn.setRequestProperty("accept""*/*");   
  8.             conn.setRequestProperty("connection""Keep-Alive");   
  9.             conn.setRequestProperty("user-agent",   
  10.                 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");   
  11.             //建立实际的连接  
  12.             conn.connect();   
  13.  
  14. //2. 向指定URL发送POST方法的请求  
  15. URL realUrl = new URL(url);  
  16.             //打开和URL之间的连接  
  17.             URLConnection conn = realUrl.openConnection();  
  18.             //设置通用的请求属性  
  19.             conn.setRequestProperty("accept""*/*");   
  20.             conn.setRequestProperty("connection""Keep-Alive");   
  21.             conn.setRequestProperty("user-agent",   
  22.                 "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");   
  23.             //发送POST请求必须设置如下两行  
  24.             conn.setDoOutput(true);  
  25.             conn.setDoInput(true);  
  26.             //获取URLConnection对象对应的输出流  
  27.             out = new PrintWriter(conn.getOutputStream());  
  28.             //发送请求参数  
  29.             out.print(param);  
  30.  


    另外需要注意的是,如果既需要读取又需要发送,一定要先使用输出流,再使用输入流。因为远程资源不会主动向本地发送请求,必须要先请求资源。


2. 基于TCP协议的网络编程
    TCP协议是一种可靠的通络协议,通信两端的Socket使得它们之间形成网络虚拟链路,两端的程序可以通过虚拟链路进行通讯。Java使用socket对象代表两端的通信端口,并通过socket产生的IO流来进行网络通信。


2.1 ServerSocket
    在两个通信端没有建立虚拟链路之前,必须有一个通信实体首先主动监听来自另一端的请求。ServerSocket对象使用accept()方法用于监听来自客户端的Socket连接,如果收到一个客户端Socket的连接请求,该方法将返回一个与客户端Socket对应的Socket对象。如果没有连接,它将一直处于等待状态。通常情况下,服务器不应只接受一个客户端请求,而应该通过循环调用accept()不断接受来自客户端的所有请求。
    这里需要注意的是,对于多次接收客户端数据的情况来说,一方面可以每次都在客户端建立一个新的Socket对象然后通过输入输出通讯,这样对于服务器端来说,每次循环所接收的内容也不一样,被认为是不同的客户端。另外,也可以只建立一次,然后在这个虚拟链路上通信,这样在服务器端一次循环的内容就是通信的全过程。
    服务器端的示例代码:

 
 

     
     
  1. //创建一个ServerSocket,用于监听客户端Socket的连接请求  
  2.         ServerSocket ss = new ServerSocket(30000);  
  3.         //采用循环不断接受来自客户端的请求  
  4.         while (true)  
  5.         {  
  6.             //每当接受到客户端Socket的请求,服务器端也对应产生一个Socket  
  7.             Socket s = ss.accept();  
  8.             //将Socket对应的输出流包装成PrintStream  
  9.             PrintStream ps = new PrintStream(s.getOutputStream());  
  10.             //进行普通IO操作  
  11.             ps.println("您好,您收到了服务器的新年祝福!");  
  12.             //关闭输出流,关闭Socket  
  13.             ps.close();  
  14.             s.close();  
  15.         } 
   


2.2 Socket
    使用Socket可以主动连接到服务器端,使用服务器的IP地址和端口号初始化之后,服务器端的accept便可以解除阻塞继续向下执行,这样就建立了一对互相连接的Socket。
    客户端示例代码:

 
 

     
     
  1. Socket socket = new Socket("127.0.0.1" , 30000);  
  2.         //将Socket对应的输入流包装成BufferedReader  
  3.         BufferedReader br = new BufferedReader(  
  4.             new InputStreamReader(socket.getInputStream()));  
  5.         //进行普通IO操作  
  6.         String line = br.readLine();  
  7.         System.out.println("来自服务器的数据:" + line);  
  8.         //关闭输入流、socket  
  9.         br.close();  
  10.         socket.close(); 


2.3 使用多线程
    在复杂的通讯中,使用多线程非常必要。对于服务器来说,它需要接收来自多个客户端的连接请求,处理多个客户端通讯需要并发执行,那么就需要对每一个传过来的Socket在不同的线程中进行处理,每条线程需要负责与一个客户端进行通信。以防止其中一个客户端的处理阻塞会影响到其他的线程。对于客户端来说,一方面要读取来自服务器端的数据,另一方面又要向服务器端输出数据,它们同样也需要在不同的线程中分别处理。
具体代码如下,服务器端:

 
 

     
     
  1. public class MyServer  
  2. {  
  3.     //定义保存所有Socket的ArrayList  
  4.     public static ArrayList<Socket> socketList = new ArrayList<Socket>();  
  5.     public static void main(String[] args)   
  6.         throws IOException  
  7.     {  
  8.         ServerSocket ss = new ServerSocket(30000);  
  9.         while(true)  
  10.         {  
  11.             //此行代码会阻塞,将一直等待别人的连接  
  12.             Socket s = ss.accept();  
  13.             socketList.add(s);  
  14.             //每当客户端连接后启动一条ServerThread线程为该客户端服务  
  15.             new Thread(new ServerThread(s)).start();  
  16.         }  
  17.     }  

    客户端:

 
 

     
     
  1. public class MyClient  
  2. {  
  3.     public static void main(String[] args)  
  4.         throws IOException   
  5.     {  
  6.         Socket s = s = new Socket("127.0.0.1" , 30000);  
  7.         //客户端启动ClientThread线程不断读取来自服务器的数据  
  8.         new Thread(new ClientThread(s)).start();  
  9.         //获取该Socket对应的输出流  
  10.         PrintStream ps = new PrintStream(s.getOutputStream());  
  11.         String line = null;  
  12.         //不断读取键盘输入  
  13.         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));  
  14.         while ((line = br.readLine()) != null)  
  15.         {  
  16.             //将用户的键盘输入内容写入Socket对应的输出流  
  17.             ps.println(line);  
  18.         }  
  19.     }  

2.4 使用协议字符
    协议字符用于标识一些字段的特定功能,用于说明传输内容的特性。它可以由用户自定义。一般情况下,可以定义一个存放这些协议字符的接口。如下:

   

 

     
     
  1. public interface YeekuProtocol  
  2. {  
  3.     //定义协议字符串的长度  
  4.     int PROTOCOL_LEN = 2;  
  5.     //下面是一些协议字符串,服务器和客户端交换的信息  
  6.     //都应该在前、后添加这种特殊字符串。  
  7.     String MSG_ROUND = "§γ";  
  8.     String USER_ROUND = "∏∑";  
  9.     String LOGIN_SUCCESS = "1";  
  10.     String NAME_REP = "-1";  
  11.     String PRIVATE_ROUND = "★【";  
  12.     String SPLIT_SIGN = "※";  


    在字段时可以加上这些字符,如下代码:

 
 

     
     
  1. while(true)  
  2.             {  
  3.                 String userName = JOptionPane.showInputDialog(tip + "输入用户名");  
  4.                 //将用户输入的用户名的前后增加协议字符串后发送  
  5.                 ps.println(YeekuProtocol.USER_ROUND + userName  
  6.                     + YeekuProtocol.USER_ROUND);  
  7.                 //读取服务器的响应  
  8.                 String result = brServer.readLine();  
  9.                 //如果用户重复,开始下次循环  
  10.                 if (result.equals(YeekuProtocol.NAME_REP))  
  11.                 {  
  12.                     tip = "用户名重复!请重新";  
  13.                     continue;  
  14.                 }  
  15.                 //如果服务器返回登陆成功,结束循环  
  16.                 if (result.equals(YeekuProtocol.LOGIN_SUCCESS))  
  17.                 {  
  18.                     break;  
  19.                 }  
  20.             } 


    收到发送来的字段时候,也再次拆分成所需要的部分,如下代码:

 
 

     
     
  1. if (line.startsWith(YeekuProtocol.PRIVATE_ROUND)   
  2.                     && line.endsWith(YeekuProtocol.PRIVATE_ROUND))  
  3.                 {  
  4.                     //得到真实消息  
  5.                     String userAndMsg = getRealMsg(line);  
  6.                     //以SPLIT_SIGN来分割字符串,前面部分是私聊用户,后面部分是聊天信息  
  7.                     String user = userAndMsg.split(YeekuProtocol.SPLIT_SIGN)[0];  
  8.                     String msg = userAndMsg.split(YeekuProtocol.SPLIT_SIGN)[1];  
  9.                     //获取私聊用户对应的输出流,并发送私聊信息  
  10.                     Server.clients.get(user).println(  
  11.                         Server.clients.getKeyByValue(ps) + "悄悄地对你说:" + msg);  
  12.                 } 


3. UDP协议的网络编程
    UDP协议是一种不可靠的网络协议,它在通讯实例的两端个建立一个Socket,但这两个Socket之间并没有虚拟链路,这两个Socket只是发送和接受数据报的对象,Java提供了DatagramSocket对象作为基于UDP协议的Socket,使用DatagramPacket代表DatagramSocket发送和接收的数据报。


3.1 使用DatagramSocket发送、接收数据
    DatagramSocket本身并不负责维护状态和产生IO流。它仅仅负责接收和发送数据报。使用receive(DatagramPacket p)方法接收,使用send(DatagramPacket p)方法发送。
    这里需要首先明确的是,DatagramPacket对象的构造。DatagramPacket的内部实际上采用了一个字节型数组来保存数据,它的初始化方法如下:

 

     
     
  1. //接收端的DatagaramSocket内部包含一个空的数组,接收传递过来的数据报中的数组信息。可以通过DatagaramSocket对象的getData()方法返回的数组来获取其中的包含的数组。  
  2. Private DatagaramSocket udpSocket=new DatagaramSocket(buf,buf.length);  
  3. //发送端的DatagaramSocket内部包含一个将要传递的数组,同时需要包含目标IP和端口。如果初始化时传递的数组参数是空,可以通过调用DatagaramSocket对象的setData()方法设置内容。  
  4. Private DatagaramSocket udpSocket=new DatagaramSocket(buf,buf.length,IP,PORT);  
  5. udpSocket。setData(outBuf); 

    作为这两个方法的参数,作用和构造不同的。作为接收方法中的参数,DatagramPacket中的数组一个空的数组,用来存放接收到的DatagramPacket对象中的数组;而作为发送方法参数,DatagramPacket本身含有了目的端的IP和端口,以及存储了要发送内容的指定了长度的字节型数组。
    另外,DatagramPacket对象还提供了setData(Byte[] b)和Byte[] b= getData()方法,用于设置DatagramPacket中包含的数组内容和获得其中包含数组的内容。
    使用TCP和UDP通讯的编码区别:
    a. 在TCP中,目标IP和端口由Socket指定包含;UDP中,目标IP由DatagramPacket包含指定,DatagramSocket只负责发送和接受。
    b. 在TCP中,通讯是通过Socket获得的IO流来实现;在UDP中,则通过DatagramSocket的send和receive方法。
 

3.2 使用MulticastSocket实现多点广播
    MulticastSocket是DatagramSocket的子类,可以将数据报以广播形式发送到数量不等的多个客户端。实现策略就是定义一个广播地址,使得每个MulticastSocket都加入到这个地址中。从而每次使用MulticastSocket发送数据报(包含的广播地址)时,所有加入了这个广播地址的MulticastSocket对象都可以收到信息。
    MulticastSocket的初始化需要传递端口号作为参数,特别对于需要接受信息的端来说,它的端口号需要与发送端数据报中包含的端口号一致。具体代码如下:

 
 

     
     
  1. //创建用于发送、接收数据的MulticastSocket对象  
  2.             //因为该MulticastSocket对象需要接收,所以有指定端口  
  3.             socket = new MulticastSocket(BROADCAST_PORT);  
  4.             broadcastAddress = InetAddress.getByName(BROADCAST_IP);  
  5.             //将该socket加入指定的多点广播地址  
  6.             socket.joinGroup(broadcastAddress);  
  7.             //设置本MulticastSocket发送的数据报被回送到自身  
  8.             socket.setLoopbackMode(false);  
  9.             //初始化发送用的DatagramSocket,它包含一个长度为0的字节数组  
  10.             outPacket = new DatagramPacket(new byte[0] , 0 ,  
  11.                 broadcastAddress , BROADCAST_PORT); 


4. 使用代理服务器
    Java中可以使用Proxy直接创建连接代理服务器,具体使用方法如下:

 
 

     
     
  1. public class ProxyTest  
  2. {  
  3.     Proxy proxy;  
  4.     URL url;  
  5.     URLConnection conn;  
  6.     //从网络通过代理读数据  
  7.     Scanner scan;  
  8.     PrintStream ps ;  
  9.     //下面是代理服务器的地址和端口,  
  10.     //换成实际有效的代理服务器的地址和端口  
  11.     String proxyAddress = "202.128.23.32";  
  12.     int proxyPort;  
  13.     //下面是你试图打开的网站地址  
  14.     String urlStr = "http://www.oneedu.cn";  
  15.  
  16.     public void init()  
  17.     {  
  18.         try 
  19.         {  
  20.             url = new URL(urlStr);  
  21.             //创建一个代理服务器对象  
  22.             proxy = new Proxy(Proxy.Type.HTTP,  
  23.                 new InetSocketAddress(proxyAddress , proxyPort));  
  24.             //使用指定的代理服务器打开连接  
  25.             conn = url.openConnection(proxy);  
  26.             //设置超时时长。  
  27.             conn.setConnectTimeout(5000);  
  28.             scan = new Scanner(conn.getInputStream());  
  29.             //初始化输出流  
  30.             ps = new PrintStream("Index.htm");  
  31.             while (scan.hasNextLine())  
  32.             {  
  33.                 String line = scan.nextLine();  
  34.                 //在控制台输出网页资源内容  
  35.                 System.out.println(line);  
  36.                 //将网页资源内容输出到指定输出流  
  37.                 ps.println(line);  
  38.             }  
  39.         }  
  40.         catch(MalformedURLException ex)  
  41.         {  
  42.             System.out.println(urlStr + "不是有效的网站地址!");  
  43.         }  
  44.         catch(IOException ex)  
  45.         {  
  46.             ex.printStackTrace();  
  47.         }  
  48.         //关闭资源  
  49.         finally 
  50.         {  
  51.             if (ps != null)  
  52.             {  
  53.                 ps.close();  
  54.             }  
  55.         }  
  56.     }  
  57.  
  58.     

5. 编码中的问题总结

    a. 双方初始化套接字以后,就等于建立了链接,表示双方互相可以知晓对方的状态。服务器端可以调用接收到的客户端套接字进行输入输出流操作,客户端可以调用自身内部的套接字对象进行输入输出操作。这样可以保持输入输出的流畅性。例如,客户端向服务器端发送消息时,可以隔一段的时间输入一段信息,然后服务器端使用循环不断的读取传过来的输入流。
    b. 对于可能出现阻塞的方法,例如客户端进行循环不断读取来自服务器端的响应信息时,如果此时服务器端并没有向客户端进行输出,那么读取的方法将处于阻塞状态,直到收到信息为止才向下执行代码。那么对于这样容易产生阻塞的代码,就需要将它放在一个单独的线程中处理。
    c. 有一些流是顺承的。例如,服务器端在收到客户端的消息以后,就将消息再通过输出流向其他所有服务器发送。那么,这个来自客户端的输入流和发向客户端的输出流就是顺接的关系,不必对它们分在两个不同的线程。
    d. println()方法对应readLine()。
    e. 在JFrame类中,一般不要将自己的代码写进main方法中,可以将代码写到自定义的方法中,然后在main方法中调用。

Java中的DatagramPacket与DatagramSocket的初步

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。

http://sunspot.blog.51cto.com/372554/130313

1.基本概念:
   a.DatagramPacket与DatagramSocket位于java.net包中
  b.DatagramPacket表示存放数据的数据报,DatagramSocket表示接受或发送数据报的套接字
  c.由这两个类所有构成的网络链接是基于UDP协议,是一种不可靠的协议。
  
    之所以不可靠是因为发送方不负责数据是否发送成功,接收方收到数据时也不会     向发送方反馈成功消息,容易导致信息的丢失。
    但是这种协议却是快捷的,比如CS(半条命)游戏就是基于UDP协议,否则我们估计     要玩一次游戏后就会把机子砸了,所以我们可以看到游戏有时会失帧。

2.使用方法:
  要建立基于UDP协议的链接,我们应该先建立套接字<DatagramSocket>(即发送站或接收站),之后通过套接字发送或接受数据<DatagramPacket>。
  我们先了解一下DatagramSocket和DatagramPacket类
  
DatagramSocket类:
  构造方法
  DatagramSocket():
    表示创建一个默认的套接字,并绑定到本地地址和一个随机的端口号
  DatagramSocket(
int  port):
    与上面不同的是,绑定到特定端口号,其它不变
  DatagramSocket(
int  port, InetAddress iad):
    表示创建一个套接字,绑定到特定的端口号及指定地址
  DatagramSocket(SocketAddress sad);
    表示创建一个套接字,绑定到特定的套接字地址

    小提示:所谓的本地地址我们可以这样来理解,我们通过“控制面板-本地连接-支持”中可以看到一个IP地址,这就是本地的IP地址,也暂且理解为本地地址。
   
  基本方法:
  close():
    关闭套接字
  recevie(DatagramPacket dp):
    接受数据报
  send(DatagramPacket dp):
    发送数据报

    这里触及了数据报,下面介绍数据报:


DatagramPacket类:
  构造方法:(只列出主要的)

  接受类型:
  DatagramPacket(
byte [] buf,  int  length):
    用来接受长度为length的buf数据(即数据存于字节数组buf中)


  发送类型:
  DatagramPacket(
byte [] buf,  int  length, InetAddress address,  int  port);
    将length长的buf数据发送到指定的地址的端口号处
  DatagramPacket(
byte [] buf,  int  length, SocketAddress address);
     将length长的buf数据发送到指定的套接字地址处
  
  有上面可知,我们发送数据时构造的数据报应该是发送类型的,而接受数据报时应该是接受类型的        
开始书写代码
代码意图:
1.一个接收方,一个发送方
2.当接收方收到发送方发送的消息后,打印发送的消息及发送方的地址和端口号,之后向发送反馈一条信息“接受方:我收到了!”
3.发送方打印出接收方反馈的消息

详细思路见代码:
接收方:Getter.java
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketAddress;

public class Getter {

  public static void main(String[] args) {
    try {
      //确定接受方的IP和端口号,IP地址为本地机器地址
      InetAddress ip = InetAddress.getLocalHost();
      int port = 8888;
      
      //创建接收方的套接字,并制定端口号和IP地址
      DatagramSocket getSocket = new DatagramSocket(port,ip);
      
      //确定数据报接受的数据的数组大小
      byte[] buf = new byte[1024];
      
      //创建接受类型的数据报,数据将存储在buf中
      DatagramPacket getPacket = new DatagramPacket(buf,buf.length);
      
      //通过套接字接收数据
      getSocket.receive(getPacket);
      
      //解析发送方传递的消息,并打印
      String getMes = new String(buf,0,getPacket.getLength());
      System.out.println("对方发送的消息:"+getMes);
      
      //通过数据报得到发送方的IP和端口号,并打印
      InetAddress sendIP = getPacket.getAddress();
      int sendPort = getPacket.getPort();
      System.out.println("对方的IP地址是:"+sendIP.getHostAddress());
      System.out.println("对方的端口号是:"+sendPort);
      
      //通过数据报得到发送方的套接字地址
      SocketAddress sendAddress = getPacket.getSocketAddress();
      
      //确定要反馈发送方的消息内容,并转换为字节数组
      String feedback = "接收方说:我收到了!";
      byte[] backBuf = feedback.getBytes();
      
      //创建发送类型的数据报
      DatagramPacket sendPacket = new DatagramPacket(backBuf,backBuf.length,sendAddress);
      
      //通过套接字发送数据
      getSocket.send(sendPacket);
      
      //关闭套接字
      getSocket.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

}


 

发送方: Sender.java
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class Sender {
  public static void main(String[] args) {
    try {
      //创建发送方的套接字,IP默认为本地,端口号随机
      DatagramSocket sendSocket = new DatagramSocket();
      
      //确定要发送的消息:
      String mes = "你好!接收方!";
      
      //由于数据报的数据是以字符数组传的形式存储的,所以传转数据
      byte[] buf = mes.getBytes();
      
      //确定发送方的IP地址及端口号,地址为本地机器地址
      int port = 8888;
      InetAddress ip = InetAddress.getLocalHost();
      
      //创建发送类型的数据报:
      DatagramPacket sendPacket = new DatagramPacket(buf,buf.length,ip,port);
      
      //通过套接字发送数据:
      sendSocket.send(sendPacket);
      
      //确定接受反馈数据的缓冲存储器,即存储数据的字节数组
      byte[] getBuf = new byte[1024];
      
      //创建接受类型的数据报
      DatagramPacket getPacket = new DatagramPacket(getBuf,getBuf.length);
      
      //通过套接字接受数据
      sendSocket.receive(getPacket);
      
      //解析反馈的消息,并打印
      String backMes = new String(getBuf,0,getPacket.getLength());
      System.out.println("接受方返回的消息:"+backMes);
      
      //关闭套接字
      sendSocket.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
    
  }
}
测试已通过
先启动接收方,然后启动发送方即可

本文出自 “博远至静” 博客,请务必保留此出处http://sunspot.blog.51cto.com/372554/130313

 用java获取本机IP地址

	Enumeration allNetInterfaces = NetworkInterface.getNetworkInterfaces();
			InetAddress ip = null;
			while (allNetInterfaces.hasMoreElements())
			{
				NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
				System.out.println(netInterface.getName());
				Enumeration addresses = netInterface.getInetAddresses();
				while (addresses.hasMoreElements())
				{
					ip = (InetAddress) addresses.nextElement();
					if (ip != null && ip instanceof Inet4Address)
					{
						System.out.println("本机的IP = " + ip.getHostAddress());
					} 
				}
			}


 

 

 

 

 

 

 

 

 

java udp广播代码示例

应用场景: 一个学校,每当下课时间到了提供提示下课功能。

分析:用UDP实现广播向同网段电脑发送数据,客户端接受接受到数据后,弹出提示对话框。服务端需要一个循环来判断时间是否到下课时间,这里如果用死循环,很占系统资源,我测试CUP在100%,用java的Timer类可以很好的解决这个问题,既能保证程序始终运行,又不怎么占系统资源,具体什么原理,没有仔细研究过。下面来看代码具体实现

服务段代码:

import java.io.IOException;  
import java.net.DatagramPacket;  
import java.net.DatagramSocket;  
import java.net.InetAddress;  
import java.net.SocketException;  
import java.net.UnknownHostException;  
import java.text.SimpleDateFormat;  
import java.util.Date;  
import java.util.Timer;  
public class UdpSend {  
    public void sendData()throws SocketException,  
    UnknownHostException{  
        DatagramSocket ds = new DatagramSocket();// 创建用来发送数据报包的套接字   
        String str = "1";  
        DatagramPacket dp = new DatagramPacket(str.getBytes(),  
                str.getBytes().length,  
                InetAddress.getByName("255.255.255.255"), 3001);  
        // 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号   
        try {  
            ds.send(dp);  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
        ds.close();  
          
    }  
    public static void main(String[] args) {  
        Timer timer = new Timer();  
        timer.schedule(new MyTask(), 1000, 1000);  
    }  
    static class MyTask extends java.util.TimerTask{   
        @Override  
        public void run() {   
            UdpSend tt = new UdpSend();  
            Date d = new Date();  
            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");  
            String strdate = sdf.format(d);  
            String[] classTime = {"17:18:00","17:19:00","17:20:00"};  
            for(int i = 0;i<classTime.length;i++){  
                 if(classTime[i].equals(strdate)){  
                     try {  
                        tt.sendData();  
                    } catch (SocketException e) {  
                        // TODO Auto-generated catch block   
                        e.printStackTrace();  
                    } catch (UnknownHostException e) {  
                        // TODO Auto-generated catch block   
                        e.printStackTrace();  
                    }  
                 }  
            }     
        }  
    }  
}  


 

客户单简单代码示例,没有做循环

package udp;  
import java.net.DatagramPacket;  
import java.net.DatagramSocket;  
public class UdpRecv {  
    public static void main(String[] args) throws Exception {  
        DatagramSocket ds = new DatagramSocket(3001);// 创建接收数据报套接字并将其绑定到本地主机上的指定端口   
        byte[] buf = new byte[1024];  
        DatagramPacket dp = new DatagramPacket(buf, 1024);  
        ds.receive(dp);  
        String strRecv = new String(dp.getData(), 0, dp.getLength()) + " from "  
                + dp.getAddress().getHostAddress() + ":" + dp.getPort();  
        System.out.println(strRecv);  
        ds.close();  
    }  
}  


 

java利用udp实现广播[java]

http://kkito.cn/index.php/blog/getArticle/183/java%E5%88%A9%E7%94%A8udp%E5%AE%9E%E7%8E%B0%E5%B9%BF%E6%92%AD

在局域网的两台机器上,一台跑server向广播地址的某端口发,一台做客户机监听某端口。

和多播一样向广播地址发实际就是给路由或者交换机发,通过这些设备在局域网里广播。

代码如下:

package cn.kk.socket;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class BroadCastTest {
	
	public static void main(String args[])throws Exception{
		//sendBroadcast();
		receiveBroadcast();
	}
	
	public static void sendBroadcast()throws Exception{
		DatagramSocket socket;
		DatagramPacket packet;
		byte[] data={1,2,3,4};
		
		socket = new DatagramSocket();
		socket.setBroadcast(true); //有没有没啥不同
		//send端指定接受端的端口,自己的端口是随机的
		packet = new DatagramPacket(data,data.length,InetAddress.getByName("255.255.255.255"),8300);
		for(int i = 0 ; i < 50 ; i++){
			Thread.sleep(1000);
			socket.send(packet);
		}
	}
	
	public static void receiveBroadcast()throws Exception{
		byte[] buffer = new byte[65507];
		DatagramSocket server = new DatagramSocket(8300);
		DatagramPacket packet = new DatagramPacket(buffer , buffer.length);
		for(;;){
			server.receive(packet);
			String s = new String(packet.getData( ), 0, packet.getLength( ));
			System.out.println(packet.getAddress( ) + " at port " 
			           + packet.getPort( ) + " says " + s);
		}
	}

}

 

Java发送广播

package socket;

import java.net.*; //导入java.net包

import java.util.Scanner;

 

public class Weather extends Thread { // 创建类。该类为多线程执行程序

int port = 9898; // 定义端口

InetAddress iaddress = null; // 创建InetAddress对象

MulticastSocket socket = null; // 声明多点广播套接字

Weather() { // 构造方法

try {

iaddress = InetAddress.getByName("225.225.225.255"); // 实例化InetAddress,指定地址

socket = new MulticastSocket(port); // 实例化多点广播套接字

socket.setTimeToLive(1); // 指定发送范围是本地网络

socket.setBroadcast(true);

socket.joinGroup(iaddress); // 加入广播组

} catch (Exception e) {

e.printStackTrace(); // 输出异常信息

}

}

 

public void run() { // run()方法

Scanner canScanner=new Scanner(System.in);

while (true) {

DatagramPacket packet = null; // 声明DatagramPacket对象

 

byte data[] = canScanner.next().getBytes(); // 声明字节数组

packet = new DatagramPacket(data, data.length, iaddress, port); // 将数据打包

try {

socket.send(packet); // 发送数据

} catch (Exception e) {

e.printStackTrace(); // 输出异常信息

}

 


}

}

 

public static void main(String[] args) { // 主方法

Weather w = new Weather(); // 创建本类对象

w.start(); // 启动线程

}

}

 

JAVA实现广播消息DEMO
http://www.oschina.net/code/snippet_147955_4878

package test;

import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class MulticastSender {

	private int port;
	private String host;
	private String data;

	public MulticastSender(String data, String host, int port) {
		this.data = data;
		this.host = host;
		this.port = port;
	}

	public void send() {
		try {
			InetAddress ip = InetAddress.getByName(this.host);
			DatagramPacket packet = new DatagramPacket(this.data.getBytes(),
					this.data.length(), ip, this.port);
			MulticastSocket ms = new MulticastSocket();
			ms.send(packet);
			ms.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		int port = 1234;
		String host = "224.0.0.1";
		String data = "hello world.";
		MulticastSender ms = new MulticastSender(data, host, port);
		ms.send();
	}

}


 

package test;

import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class MulticastListener {

	private int port;
	private String host;

	public MulticastListener(String host, int port) {
		this.host = host;
		this.port = port;
	}

	public void listen() {
		byte[] data = new byte[256];
		try {
			InetAddress ip = InetAddress.getByName(this.host);
			MulticastSocket ms = new MulticastSocket(this.port);
			ms.joinGroup(ip);
			DatagramPacket packet = new DatagramPacket(data, data.length);
			ms.receive(packet);
			String message = new String(packet.getData(), 0, packet.getLength());
			System.out.println(message);
			ms.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		int port = 1234;
		String host = "224.0.0.1";
		MulticastListener ml = new MulticastListener(host, port);
		while(true) {
			ml.listen();
		}
	}

}

MulticastSocket类的使用 

 

写了个MulticastSocket的程序,以作备忘:
import java.io.*;
import java.net.*;
import java.util.*;
public class MulticastClient
{
       public static void main(String[] args) throws IOException
  {
               MulticastSocket socket = new MulticastSocket(4446);
               InetAddress address = InetAddress.getByName("230.0.0.1");
    socket.joinGroup(address);
               DatagramPacket packet;
    //发送数据包
         byte[] buf = "Hello,This is a member of multicast!".getBytes();
               packet = new DatagramPacket(buf, buf.length,address,4445);
               socket.send(packet);
    //接收数据包并打印
               byte[] rev = new byte[512];
               packet = new DatagramPacket(rev, rev.length);
               socket.receive(packet);
               String received = new String(packet.getData()).trim();
               System.out.println("received: " + received);
    //退出组播组,关闭socket
    socket.leaveGroup(address);
    socket.close();
  }
}
---------------------------------------
import java.io.*;
import java.net.*;
import java.util.*;
public class AMulticastClient
{
       public static void main(String[] args) throws IOException
  {
               MulticastSocket socket = new MulticastSocket(4445);
               InetAddress address = InetAddress.getByName("230.0.0.1");
    socket.joinGroup(address);
               DatagramPacket packet;
    //接收数据包
    byte[] buf = new byte[512];
               packet = new DatagramPacket(buf, buf.length);
               socket.receive(packet);
    //打印数据包
               String received = new String(packet.getData()).trim();
               System.out.println("received: " + received);
    //发送数据包
    byte[] sen=received.getBytes();
    packet=new DatagramPacket(sen,sen.length,address,4446);
    socket.send(packet);
    //退出组播组,关闭socket
    socket.leaveGroup(address);
    socket.close();
  }
}



 

 


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值