黑马程序员-JAVA.net-网络工具类粗解

来源:互联网 发布:手机会议录音软件 编辑:程序博客网 时间:2024/06/01 08:46

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-


Java自1.0就已集成网络操作的工具,提供了从基于流的Java.net包以实现网络访问和操作,而Java1.4又假如了nio包,利用异步和面向缓冲的新方式实现了更高效的网络操作工具类,并以此重写了部分io包中的内容。

Java.net

java.net 包可以大致分为两个部分:

  • 低级 API,用于处理以下抽象:
    • 地址,也就是网络标识符,如 IP 地址。
    • 套接字,也就是基本双向数据通信机制。
    • 接口,用于描述网络接口。
  • 高级 API,用于处理以下抽象:
    • URI,表示统一资源标识符。
    • URL,表示统一资源定位符。
    • 连接,表示到 URL 所指向资源的连接。

地址

InetAddress 类是表示 IP(Internet 协议)地址的抽象。它拥有两个子类:
- 用于 IPv4 地址的 Inet4Address。
- 用于 IPv6 地址的 Inet6Address。
IP 地址是 IP 使用的4个byte(IPv4)或者16个byte(IPv6);对于Hostname,将通过主机名解析服务(通常是DNS)将他解析为IP地址。

ipv6

并非所有系统和网络环境都支持 IPv6 协议,在 IPv6 不可用或被显式禁用的情况下,在此情况下,大多数方法在使用 Inet6Address 调用时都将抛出异常。

简单的创建InetAddress 的例子

try {            InetAddress ip = null;            //localhost            ip = Inet4Address.getLocalHost();            sp(ip);//xxxx-xxxx/192.168.1.100 for example            //loopback            ip= Inet4Address.getLoopbackAddress();            sp(ip);//localhost/127.0.0.1             //Indicate a ip & hostname without NS            ip=Inet4Address.getByAddress("Its.A.Fake.Domian",                     new byte[]{(byte) 0xfe,(byte) 0xfe,(byte) 0xfe,(byte) 0xfe});            sp(ip);//Its.A.Fake.Domian/254.254.254.254            //Indicate a ip with 4bytes for ipv4            ip=Inet4Address.getByAddress(                    new byte[]{(byte) 0xfe,(byte) 0xfe,(byte) 0xfe,(byte) 0xfe});            sp(ip);///254.254.254.254            //Indicate a ip for hostname using NS            ip=Inet4Address.getByName("www.oracle.com");            sp(ip);//www.oracle.com/184.50.90.127        } catch (UnknownHostException e) {            e.printStackTrace();        }

套接字

套接字是实际操作传输层的Java工具对象,基于UDP和TCP的传输都可以通过构建套接字来完成。

端口

端口是TCP和UDP协议中对同一IP地址不同应用对象的区分手段,TCP与UDP的端口是互不影响的。

UDP与DatagramSocket

UDP:User Datagram Protocol是一种面向无连接,基于数据包的传输层协议,一个UDP数据包最大为65535bytes。但是UDP不含任何传输保证,数据的有效性必须由应用自行处理,但相对于TCP,它的连接开销小,相对速度快,很多协议也是基于UDP的,比如很重要的DNS(UDP53)协议,DHCP(UDP67/68)协议;另外很多协议同时支持TCP以及UDP以实现更好的数据交换。

DatagramSocket是Java中处理UDP点对点传输的网络端点对象。通过传输DatagramPacket网络包实现UDP协议的传输。将DatagramPacket目标指定为广播地址可以实现域内网络多播。

DatagramSocket的构造方法允许指定端口,当绑定端口失败时会抛出SocketException;若不指定,将由JVM随机安排一个可用的端口。
由于端口属于系统资源,必须保证端口在使用完后通过close()方法释放。

MulticastSocket

UDP的组播,多播组通过 D 类 IP 地址和标准 UDP 端口号指定,通过joinGroup(group)加入组,将消息发送到多播组时,该主机和端口的所有预定接收者都将接收到消息,多个 MulticastSocket 可以同时预定多播组和端口,并且都会接收到组数据报。

UDP的简单例子

一个简单的控制台通过本地环回的UDP信息收发

        final int port = 36314;        try {            UdpReciver serv = new UdpReciver(port);            StringUdpSender cli=new StringUdpSender(port);            new Thread(serv).start();            new Thread(cli).start();            ts(10000);            serv.stop();        } catch (IOException e) {            e.printStackTrace();        }class StringUdpSender implements Runnable {    private DatagramSocket soc;    private final String exitFlag;    private final int port;    public StringUdpSender(int port,String exitFlag) throws SocketException {        super();        this.exitFlag = exitFlag;        soc=new DatagramSocket();        this.port=port;    }     public StringUdpSender(int port) throws SocketException {         this(port,"exit");    }    @Override    public void run() {        String msg=null;        BufferedReader br= new BufferedReader(new  InputStreamReader(System.in));        DatagramPacket dp=null;        while(true){            try {                msg=br.readLine();                if(!exitFlag.equals(msg)){                    dp=new DatagramPacket(msg.getBytes(),                             msg.getBytes().length, InetAddress.getByName("localhost"), port);                    soc.send(dp);                }else{                    break;                }            } catch (IOException e) {                e.printStackTrace();            }        }        if (soc != null && !soc.isClosed()) {            soc.close();        }    }}class UdpReciver implements Runnable {    private DatagramSocket soc;    private volatile boolean runFlag = false;    public UdpReciver(int port) throws SocketException {        super();        this.soc = new DatagramSocket(port);    }    public void stop() {        this.setRunFlag(false);    }    public boolean isRunning() {        return runFlag;    }    private synchronized void setRunFlag(boolean runFlag) {        this.runFlag = runFlag;    }    @Override    public void run() {        this.setRunFlag(true);        DatagramPacket dp = new DatagramPacket(new byte[1024], 1024);        while (runFlag) {            try {                this.soc.receive(dp);            } catch (IOException e) {                e.printStackTrace();            }            sp(dp.getAddress());            sp(new String(dp.getData(),0,dp.getLength()));        }        if (soc != null && !soc.isClosed()) {            soc.close();            sp("Server stop");        }    }    @Override    protected void finalize() throws Throwable {        super.finalize();        if (soc != null && !soc.isClosed()) {            soc.close();        }    }}

TCP

TCP(Transfer Control Protocol)是面向连接的,相对可靠的数据传输协议,TCP的双方必须经过3次握手来达成一个连接,有了连接之后才能开始传输数据,TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和,一旦有问题将请求重发,包丢失也将请求重发,而重复的包将丢弃。这样TCP就达成了一个可靠的数据传送。
TCP的连接时有状态的,双方通过协议信号更新彼此的状态,常见的服务端LISTEN表示监听中,任一方ESTABLISHED 表示连接已建立等。

Socket

Socket是Java中TCP端点的抽象,套接字的实际工作由 SocketImpl 类的实例执行。
客户端可以通过构造一个包含服务器IP和端口的Socket尝试打开TCP连接。
打开连接之后,可以通过getInputStream和getOutputStream与对端通信。

ServerSocket

ServerSocket是Java中TCP服务端的抽象,绑定一个端口即可开始服务。服务器套接字的实际工作由 SocketImpl 类的实例执行。
ServerSocket通过accept() 阻塞方法获得客户端的连接,然后可以操作这个连接来处理交互。

TCP服务器和客户端的简单例子

这个例子实现了简单的TCP服务器和客户端,服务器对客户端送来的String简单的+上”response”返回客户端,服务端实现了简单的异步消息和连接统计。

public class TcpDemo {    public static void main(String[] args) {        final int serverPort = 23333;        InetAddress serverAddr = null;        try {            serverAddr = InetAddress.getLocalHost();        } catch (UnknownHostException e1) {            e1.printStackTrace();        }        //start Server        try {            new Thread(new TcpServer(serverPort)).start();        } catch (IOException e1) {            e1.printStackTrace();        }        //start Client        Socket s = null;        //5 thread client//      for (int i = 0; i < 5; i++) {//          try {//              s = new Socket(serverAddr, serverPort);//          } catch (IOException e) {//              e.printStackTrace();//          }//          new Thread(new TcpClient(s)).start();//      }        //about 400 thread client        while(true){            ts(10);            try {                s = new Socket(serverAddr, serverPort);            } catch (IOException e) {                e.printStackTrace();            }            new Thread(new TcpClient(s)).start();        }    }}class TcpClient implements Runnable {    //end connection key word    public static final String ENDCONN="##$$$$##";    Socket socket;    public TcpClient(Socket socket) {        super();        this.socket = socket;    }    @Override    public void run() {//      sp(socket+" Client");        //client request        //emulate chat        Random r=new Random();        int times=r.nextInt(20)+5;        PrintWriter pw=null;        BufferedReader bfr=null;        try {            pw = new PrintWriter(socket.getOutputStream());            bfr=new BufferedReader(new InputStreamReader(socket.getInputStream()));        } catch (IOException e1) {            e1.printStackTrace();        }        for (int i = 0; i < times; i++) {            String msg = "hello from"+socket.getLocalPort()+"##"+i;            pw.println(msg);            pw.flush();            try {                msg=bfr.readLine();            } catch (IOException e) {                e.printStackTrace();            }//          sp(msg);            ts(300);        }        pw.println(TcpClient.ENDCONN);        pw.flush();//      if(socket!=null && !socket.isClosed()){//          try {//              socket.close();//          } catch (IOException e) {//              e.printStackTrace();//          }//      }    }}class TcpServer implements Runnable {    private ServerSocket s;    private ExecutorService exes;    private final BlockingQueue<Socket> connects;    private volatile ConcurrentHashMap<String, Future<String>> results;    public TcpServer(int port) throws IOException {        super();        this.s = new ServerSocket(port);        this.exes = Executors.newCachedThreadPool();        this.connects = new SynchronousQueue<Socket>();        this.results = new ConcurrentHashMap<String, Future<String>>();    }    @Override    public void run() {        // Dispatcher thread        new Thread(new Runnable() {            @Override            public void run() {                while (true) {                    try {                        Socket s = connects.take();                        String stamp = s.toString() + Instant.now().toString();                        Future<String> result = exes.submit(new RequestHandler(                                s));                        results.put(stamp, result);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        }).start();        // Result collector thread        new Thread(new Runnable() {            @Override            public void run() {                while (true) {                    ts(1000);                    List<String> remove=new ArrayList<String>();                    results.forEach((String stamp,Future<String> result) -> {                        if(result.isDone()){                            try {//                              sp(result.get());                                remove.add(stamp);                            } catch (Exception e) {                                e.printStackTrace();                            }                        }else if(result.isCancelled()){//                          sp(stamp+" is Cancelled");                            remove.add(stamp);                        }                    });                    for (String string : remove) {                        results.remove(string);                    }                    sp("Missions : "+results.size());                }            }        }).start();        //Serv to Client        while (true) {            try {                final Socket socket = s.accept();                try {                    this.connects.put(socket);                } catch (InterruptedException e) {                    e.printStackTrace();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }}class RequestHandler implements Callable<String> {    private Socket socket;    public RequestHandler(Socket conn) {        super();        this.socket = conn;    }    @Override    public String call() throws Exception {        //server Business        PrintWriter pw=null;        BufferedReader bfr=null;        String msg=null;        try {            pw = new PrintWriter(socket.getOutputStream());            bfr=new BufferedReader(new InputStreamReader(socket.getInputStream()));        } catch (IOException e1) {            e1.printStackTrace();        }        while(true){            msg=bfr.readLine();            if(TcpClient.ENDCONN.equals(msg)){//              sp(this+" recieved ENDCONN");                break;            }//          sp(msg);            pw.println(msg+" : responsed");            pw.flush();        }        socket.close();        return "done :"+socket;    }}

网络接口

NetworkInterface 实际指的是网络设备,包括硬件和软件模拟的,比如本地网络环回(lo)就是一个操作系统虚拟的网络设备。
NetworkInterface类提供Java访问本地NetworkInterface的工具,通过它,可以查询比如MAC地址,接口是否物理/虚拟,当前传输MTU值等等有价值的硬件信息,它还有一个很便捷的方法NetworkInterface getByInetAddress(InetAddress addr):可以查询绑定了指定IP地址的网络接口。

高级 API

URI

Uniform Resource Identifier 统一资源标识符,是表示一个网络中资源的标识字符串,这种标识指定了某种协议以便使用者通过合适的方式使用。
URI由URL(地址)和URN(名称)组成,常见的URI有不透明URI和分层URI
比如mailto:java-net@java.sun.com urn:isbn:096139210x 就是不透明URI,其特点是方案定义部分不以’/’开始,其结构可以表示为:[scheme:]scheme-specific-part[#fragment]
而更常见的是分层URI,http://java.sun.com/j2se/1.3/ docs/guide/collections/designfaq.html#28 ../../../demo/jfc/SwingSet2/src/SwingSet2.java 等都是分层URI,分层 URI 还要按照下面的语法进行进一步的解析:[scheme:][//authority][path][?query][#fragment]

Java.net中的URI类提供了构造和解析URI的方便工具,可以通过字符串解析URI的各个部分,也可以通过已有URI构建新的URI。最后,可以把从URI提取URL来使用,这也是最常用的。

URL

Uniform Resource Location 统一资源定位符 ,它是指向互联网“资源”的指针。URL是URI的定位部分,他们之间可以用toURI() 和toURL() 方法相互转换 。
URL 可选择指定一个“端口”,它是用于建立到远程主机 TCP 连接的端口号。如果未指定该端口号,则使用协议默认的端口。例如,http 协议的默认端口为 80。还可以指定一个备用端口,用.au:端口号

Java.net中的URL类除了像URI一样可以解析和构造(解析和构造也可以使用URLDecoder和URLEncoder)以外,特别的,它提供了一个极其方便的方法 openConnection()返回一个URLConnection对象,这个方法通过反射查询当前jre支持的协议,如果支持就打开一个协议连接,然后就可以直接操作了。

URLConnection

这里是一个打开HttpURLConnection的例子:

            URL url=new URL("http://www.java.com");            URLConnection conn=url.openConnection();            BufferedReader bfr=new BufferedReader(new InputStreamReader(conn.getInputStream()));            String str;            while((str=bfr.readLine())!=null){                sp(str);            }

协议处理程序

Java中不仅可以直接使用系统的协议处理程序打开连接,也可以自定义协议处理程序,只需要继承URLStreamHandler抽象类即可。

Java.nio 中的网络相关

@Since Java 1.4
Java1.4中加入了新的IO体系(JSR 51),包括面向缓冲区的各种Buffer对象,双向连接的Channel,Selector模式的多路复用。
在Java1.7中,nio又得到了补充(JSR 203),包括更多的文件系统操作API(包括可插拔的自定义的文件系统), 还提供了对socket和文件的异步(Async) I/O操作。

用Async方法实现异步网络服务

下面是一个基于异步非阻塞模式的服务器简单例子,双方仅仅是简单的进行几十次字符串传递即关闭连接。

import java.io.*;import java.net.*;import java.nio.*;import java.nio.channels.*;import java.time.Instant;import java.util.*;import java.util.concurrent.*;import static cc.sisel.util.Quic.*;/** * @author lz * */public class AsyncTcpDemo {    public static void main(String[] args) throws IOException,            InterruptedException, ExecutionException {        timeMark();        InetSocketAddress servAddr = new InetSocketAddress("localhost", 22333);        // start server        new Thread(new Runnable() {            @Override            public void run() {                new AsyncServer(servAddr.getPort(), 100).start();                // blocking server main thread to keep async alive                while (true) {                    try {                        Thread.sleep(Integer.MAX_VALUE);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        }).start();        // wait server init        ts(300);        Random r = new Random();        // 5000 clients        for (int i = 0; i < 5000; i++) {            ts(10);            new Thread(new AsyncClient(servAddr, r.nextInt(50) + 10)).start();        }        sp("ALL DONE");    }}class AsyncServer {    private AsynchronousServerSocketChannel servsc;    private final int port, backlog;    private boolean started;    public AsyncServer(int port, int backlog) {        super();        this.port = port;        this.backlog = backlog;        init();    }    public void start() {        // start by accept a connection channel        servsc.accept(null, new AcceptCompletionHandler());        this.started = true;    }    private void init() {        // build async group        AsynchronousChannelGroup asyncGroup = null;        try {            // async thread pool            asyncGroup = AsynchronousChannelGroup.withCachedThreadPool(                    Executors.newCachedThreadPool(), 10);        } catch (IOException e) {            e.printStackTrace();        }        // open serversocket        try {            this.servsc = AsynchronousServerSocketChannel.open(asyncGroup);        } catch (IOException e) {            e.printStackTrace();        }        // config addr resuse & tcp recieve buffer        try {            this.servsc.setOption(StandardSocketOptions.SO_REUSEADDR, true);            this.servsc.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024);        } catch (IOException e) {            e.printStackTrace();        }        // bind to port ;ip= 0.0.0.0 for everyone        try {            this.servsc.bind(new InetSocketAddress("0.0.0.0", this.port),                    this.backlog);        } catch (IOException e) {            e.printStackTrace();        }    }    public void pendingAccept() {        // after accepted channel reaccept cyclicly        if (this.started && this.servsc.isOpen()) {            servsc.accept(null, new AcceptCompletionHandler());        } else {            throw new IllegalStateException("Controller has been closed");        }    }    // accept complete event handler    class AcceptCompletionHandler implements            CompletionHandler<AsynchronousSocketChannel, Object> {        final ByteBuffer buffer = ByteBuffer.allocate(1024);        @Override        public void completed(AsynchronousSocketChannel channel,                Object attachment) {            new ASession(channel, AsyncServer.this).start();        }        @Override        public void failed(Throwable exc, Object attachment) {            System.out.println("failed: " + exc);            pendingAccept();        }    }}/** * server session handle it's read & write *  * @author lz * */class ASession {    private AsynchronousSocketChannel channel;    private AsyncServer server;    private final ByteBuffer buffer = ByteBuffer.allocate(1024);    private WriteCompletionHandler writeh;    private ReadCompletionHandler readh;    public ASession(AsynchronousSocketChannel channel, AsyncServer server) {        this.channel = channel;        this.writeh = new WriteCompletionHandler();        this.readh = new ReadCompletionHandler();        this.server = server;    }    public void start() {        channel.read(buffer, null, readh);    }    class WriteCompletionHandler implements CompletionHandler<Integer, Object> {        @Override        public void completed(Integer result, Object attachment) {            if (channel.isOpen()) {                buffer.clear();                channel.read(buffer, 20, TimeUnit.SECONDS, null, readh);            }        }        @Override        public void failed(Throwable exc, Object attachment) {            sp("########################## WRITE FAIL IN SERVER");            exc.printStackTrace();        }    }    class ReadCompletionHandler implements CompletionHandler<Integer, Object> {        @Override        public void completed(Integer result, Object attachment) {            buffer.flip();            String msg = readBuffer(buffer);            // sp("server recieved:" + msg);            buffer.clear();            // if ENDCODE then close session and pending server next            if (AsyncClient.ENDCODE.equals(msg)) {                if (channel.isOpen()) {                    try {                        channel.close();                    } catch (IOException e) {                        e.printStackTrace();                    }                }                server.pendingAccept();            } else {                // write response                if (channel != null && channel.isOpen()) {                    channel.write(                            ByteBuffer.wrap((msg + " responsed.").getBytes()),                            10, TimeUnit.SECONDS, null, writeh);                }            }        }        @Override        public void failed(Throwable exc, Object attachment) {            sp("########################## READ FAIL IN SERVER");            exc.printStackTrace();        }    }}/** * client emualtor send simple message & recieve *  * @author lz * */class AsyncClient implements Runnable {    public static final String ENDCODE = "&#$$#&$$";    private final InetSocketAddress serverAddr;    private int chatCount;    private AsynchronousSocketChannel client = null;    private final ByteBuffer buffer = ByteBuffer.allocate(1024);    // event handles    private ConnectCompletionHandler connecth;    private WriteCompletionHandler writeh;    private ReadCompletionHandler readh;    private NullCompletionHandler endh;    public AsyncClient(InetSocketAddress serverAddr, int chatCount) {        super();        this.serverAddr = serverAddr;        this.chatCount = chatCount < 1 ? 1 : chatCount;        this.connecth = new ConnectCompletionHandler();        this.writeh = new WriteCompletionHandler();        this.readh = new ReadCompletionHandler();        this.endh = new NullCompletionHandler();    }    @Override    public void run() {        try {            client = AsynchronousSocketChannel.open();        } catch (IOException e) {            e.printStackTrace();        }        // launch conn        if (client != null && client.isOpen()) {            client.connect(serverAddr, null, connecth);        }    }    class ConnectCompletionHandler implements CompletionHandler<Void, Object> {        @Override        public void completed(Void result, Object attachment) {            buffer.clear();            if (chatCount > 0) {                ts(300);                if (client.isOpen()) {                    client.write(ByteBuffer.wrap(("message:" + chatCount)                            .getBytes()), 10, TimeUnit.SECONDS, null, writeh);                    chatCount--;                }            } else {                if (client.isOpen()) {                    client.write(ByteBuffer.wrap(ENDCODE.getBytes()), 10,                            TimeUnit.SECONDS, null, endh);                }            }        }        @Override        public void failed(Throwable exc, Object attachment) {            sp("########################## CONNECT FAIL IN CLIENT");            ts(300);            client.connect(serverAddr, null, connecth);            exc.printStackTrace();        }    }    class WriteCompletionHandler implements CompletionHandler<Integer, Object> {        @Override        public void completed(Integer result, Object attachment) {            if (client.isOpen()) {                buffer.clear();                client.read(buffer, 20, TimeUnit.SECONDS, null, readh);            }        }        @Override        public void failed(Throwable exc, Object attachment) {            sp("########################## WRITE FAIL IN CLIENT");            exc.printStackTrace();        }    }    class ReadCompletionHandler implements CompletionHandler<Integer, Object> {        @Override        public void completed(Integer result, Object attachment) {            if (client.isOpen()) {                buffer.flip();                String msg = readBuffer(buffer);                // sp("client recieved :" + msg);                buffer.clear();                if (chatCount > 0) {                    // ts(300);                    client.write(ByteBuffer.wrap(("message:" + chatCount)                            .getBytes()), 10, TimeUnit.SECONDS, null, writeh);                    chatCount--;                } else {                    client.write(ByteBuffer.wrap(ENDCODE.getBytes()), 10,                            TimeUnit.SECONDS, null, endh);                }            }        }        @Override        public void failed(Throwable exc, Object attachment) {            sp("########################## READ FAIL IN CLIENT");            exc.printStackTrace();        }    }}class NullCompletionHandler implements CompletionHandler<Integer, Object> {    @Override    public void completed(Integer result, Object attachment) {        //just do nothing    }    @Override    public void failed(Throwable exc, Object attachment) {        //just do nothing    }}

附:
- sp和ts方法,偷懒用:

    /**     * 简单的在控制台输出 对于非数组类型,输出.toString() 对于数组类型,输出:类型[内容] 不递归     *      * @param o     *            被打印的对象     */    public static void sp(Object o) {        if (o == null) {            System.out.println("null");            return;        }        if (o.getClass().isArray()) {            StringBuilder sb = new StringBuilder();            int len = Array.getLength(o);            sb.append(o.getClass().getComponentType().getSimpleName());            if (len == 0) {                sb.append("[]");                System.out.println(sb.toString());                return;            }            sb.append('[');            for (int i = 0; i < len; i++) {                if (i != 0) {                    sb.append(':');                }                sb.append(Array.get(o, i));            }            sb.append(']');            System.out.println(sb.toString());        } else {            System.out.println(o);        }    }    /**     * 简单的换行 调用sp("");     */    public static void sp() {        sp("");    }    /**     * !!仅用于test 或者 demo 简单的包装了Thread.sleep(millis); 并catch     * (InterruptedException e)不作任何处理     *      * @param millis 毫秒数     *            @see java.lang.Thread#sleep(long millis)     */    public static void ts(long millis) {        if(millis<0){            return;        }        try {            Thread.sleep(millis);        } catch (InterruptedException e) {            e.printStackTrace();        }    }
0 0
原创粉丝点击