Java网络编程技术2

3. UDP数据报通信

UDP通信中,需要建立一个DatagramSocket,与Socket不同,它不存在“连接”的概念,取而代之的是一个数据报包——DatagramPacket。这个数据报包必须知道自己来自何处,以及打算去哪里。所以本身必须包含IP地址、端口号和数据内容。

3.1 示例程序——用UDP实现的聊天程序

用UDP协议通信不需要使用服务器,所以用于聊天的程序只要写一个,分别在不同的机器上运行就可以了,而无须写成服务端和客户端两种形式。

例9. 用UDP实现的聊天程序示例。

package Net.UDPchat;

import java.awt.BorderLayout;

import java.awt.Container;

import java.awt.FlowLayout;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.io.IOException;

import java.net.*;



import javax.swing.*;

public class UDPchat implements ActionListener,Runnable {

    JFrame jf;

    JLabel jl1,jl2,jl3;

    JTextField recPortText,sendPortText,IPText,msgText;

    JButton startBtn,sendBtn;

    JTextArea showArea;

    JScrollPane jsp;

    JPanel jp1,jp2;

    Container con;

    Thread thread = null;

    DatagramSocket receiveSocket,sendSocket;

    DatagramPacket receivePack,sendPack;

    private InetAddress sendIP;

    private int sendPort,recPort;

    private byte inBuf[],outBuf[];

    private static final int BUFSIZE = 1024;

    

    public UDPchat(){

        jf = new JFrame("聊天————UDP协议");

        jl1 = new JLabel("接收端口号:");

        jl2 = new JLabel("发送端口号:");

        jl3 = new JLabel("对方的地址:");

        recPortText = new JTextField();

        recPortText.setColumns(5);

        sendPortText = new JTextField();

        sendPortText.setColumns(5);

        IPText = new JTextField();

        IPText.setColumns(8);

        msgText = new JTextField();

        msgText.setColumns(40);

        msgText.setEditable(false);

        msgText.addActionListener(this);

        startBtn = new JButton("开始");

        startBtn.addActionListener(this);

        sendBtn = new JButton("发送");

        sendBtn.setEnabled(false);

        sendBtn.addActionListener(this);

        showArea = new JTextArea();

        showArea.setEditable(false);

        showArea.setLineWrap(true); //自动换行

        jsp = new JScrollPane(showArea);

        jp1 = new JPanel();

        jp2 = new JPanel();

        jp1.setLayout(new FlowLayout());

        jp2.setLayout(new FlowLayout());

        jp1.add(jl1);

        jp1.add(recPortText);

        jp1.add(jl2);

        jp1.add(sendPortText);

        jp1.add(jl3);

        jp1.add(IPText);

        jp1.add(startBtn);

        jp2.add(msgText);

        jp2.add(sendBtn);

        con = jf.getContentPane();

        con.add(jp1, BorderLayout.NORTH);

        con.add(jsp, BorderLayout.CENTER);

        con.add(jp2, BorderLayout.SOUTH);

        jf.setSize(600, 400);

        jf.setLocation(300, 200);

        jf.setVisible(true);

        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    }

    //在线程中接收数据

    public void run() {

        String str;

        while(true){

            try {

                receiveSocket.receive(receivePack);

                str = new String(receivePack.getData(),0,receivePack.getLength());

                showArea.append("对方说:"+str+"\n");

            } catch (IOException e) {

                showArea.append("接收数据出错!\n");

            }

        }

        

    }



    public void actionPerformed(ActionEvent e) {

        try {

            if(e.getSource()==startBtn){

                inBuf = new byte[BUFSIZE];

                sendPort = Integer.parseInt(sendPortText.getText());

                recPort = Integer.parseInt(recPortText.getText());

                sendIP = InetAddress.getByName(IPText.getText());

                sendSocket = new DatagramSocket();

                //创建接收数据包

                receivePack = new DatagramPacket(inBuf,BUFSIZE);

                //指定接收数据的端口

                receiveSocket = new DatagramSocket(recPort);

                //创建线程准备接收对方的信息

                thread = new Thread(this);

                thread.setPriority(Thread.MIN_PRIORITY);

                thread.start();

                msgText.setEditable(true);

                sendBtn.setEnabled(true);

                startBtn.setEnabled(false);

            }else{ //按下了“发送”按钮或回车键

                outBuf = msgText.getText().getBytes();

                //组装要发送的数据

                sendPack = new DatagramPacket(outBuf,outBuf.length,sendIP,sendPort);

                //发送数据

                sendSocket.send(sendPack);

                showArea.append("我说:"+msgText.getText()+"\n");

                msgText.setText(null);

            }

        } catch (NumberFormatException e1) {

            e1.printStackTrace();

        } catch (UnknownHostException e1) {

            showArea.append("无法连接到指定地址\n");

        } catch (SocketException e1) {

            showArea.append("无法打开指定端口\n");

        } catch (IOException e1) {

            showArea.append("发送数据失败!\n");

        }

        

    }



    public static void main(String[] args) {

        new UDPchat();



    }

}

4. Java网络编程的新特性(jdk1.7)

4.1 轻量级的HTTP服务

例10. HTTP服务实现的实例。

package Net.http;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.io.*;

import com.sun.net.httpserver.HttpExchange;

import com.sun.net.httpserver.HttpHandler;

import com.sun.net.httpserver.HttpServer;

import com.sun.net.httpserver.spi.HttpServerProvider;

public class HTTPServer {

    

    public static void main(String[] args) throws Exception {

        //通过HttpServerProvider的静态方法provider,获取HttpServerProvider的对象

        HttpServerProvider httpServerProvider = HttpServerProvider.provider();

        //通过InetSocketAddress类,绑定8080作为服务端口

        InetSocketAddress addr = new InetSocketAddress(8088);

        //调用createHttpServer,创建HTTP服务

        HttpServer httpServer = httpServerProvider.createHttpServer(addr, 1);

        //指定HTTP服务的路径

        httpServer.createContext("/myapp/", new MyHttpHandler());

        httpServer.setExecutor(null);

        //启动服务执行

        httpServer.start();

        //输出服务开始的信息

        System.out.println("started");

    }



    static class MyHttpHandler implements HttpHandler {

        //声明抛出异常

        public void handle(HttpExchange httpExchange) throws IOException {

            // 返回客户端的一个字符串

            String response = "This is a simple HTTP Server!";

            // 返回HTTP访问的状态码

            httpExchange.sendResponseHeaders(200, response.length());

            //将返回结果信息输出到客户端

            OutputStream out = httpExchange.getResponseBody();

            out.write(response.getBytes());

            out.close();    

        }

    }

}

上述代码是一个可直接运行的Java程序,启动程序运行后,在浏览地址栏中输入访问地址:http://localhost:8088/myapp/,就可以打开页面。

httpExchange.sendResponseHeaders(int code, int length);其中的code是Http响应的返回值,比如404、403、500等。length指的是response的长度,以字节为单位。

5. IPv6网络应用程序的开发

5.1 获取本机IPv6地址

对地址进行过滤,选出确实可用的地址。下述代码实现了这一功能,思路是遍历网络接口的各个地址,直至找到符合要求的地址。

例11. 通过Java程序获取本机的IPv6地址。

package Net.http;

import java.net.*;

import java.util.Enumeration;

import java.io.IOException;



public class GetIPv6Add {

    public static String getLocalIPv6Address() throws IOException{

        InetAddress inetAddress = null;

        //定义一个枚举变量,列出所有的网卡信息

        Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();

        outer:

            //遍历所有的网卡,从中找到包含IPv6的地址信息

            while(networkInterfaces.hasMoreElements()){

                Enumeration<InetAddress> inetAds = networkInterfaces.nextElement().getInetAddresses();

                while(inetAds.hasMoreElements()){

                    //判断是否是IPv6地址

                    if(inetAddress instanceof Inet6Address && !isReservedAddr(inetAddress)){

                        break outer;

                    }

                }

            }

        String ipAddr = inetAddress.getHostAddress();

        //过滤掉非地址信息的符号,有些Windows平台上,IP地址的版本信息后面跟着一个%,因而要去掉

        int index = ipAddr.indexOf("%");

        if(index>0){

            ipAddr = ipAddr.substring(0, index);

        }

        return ipAddr;

    }

    /**

     * 过滤本机特殊IP地址

     * @param inetAddr(传入的IP地址作为参数)

     * @return(对传入的IP地址进行判断,返回判断结果

     */

    private static boolean isReservedAddr(InetAddress inetAddr){

        if(inetAddr.isAnyLocalAddress() || inetAddr.isLinkLocalAddress() || inetAddr.isLoopbackAddress()){

            return true;

        }

        return false;

    }

    

    public static void main(String[] args) {

        try {

            System.out.println(getLocalIPv6Address());

        } catch (IOException e) {

            e.printStackTrace();

        }



    }



}

注意:在windows平台上,取得的IPv6地址后面可能跟了一个百分号加数字。这里的数字是本机网络适配器的编号。这个后缀并不是IPv6标准地址的一部分,可以去除。

 

你可能感兴趣的:(java网络编程)