黑马程序员——网络编程

来源:互联网 发布:wow7.0前夕插件mac 编辑:程序博客网 时间:2024/05/17 08:46

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

网络就是数据传输,跟IO一样,只是在网络上进行数据传输 
网络通讯的要素: 
1、IP地址 
2、端口 
3、协议

步骤: 
1、选择相关的协议 
(TCP(Socket),UDP(DatagramSocket,DatagramPacket)) 
2、确定目的的IP地址和端口

InetAddress 
|–Inet4Address 
|–Inet6Address 
方法: 
getLocalhost();获取本机ip地址主机名 
getHostAddress();主机地址,不包含主机名 
getByName(host);通过主机名获取ip地址(常用) 
getAllByName(host);获取这个主机名下的所有地址(www.baidu.com,就不止一个IP地址)

Udp:用户数据报协议 
1、面向无连接 
2、数据包小于64k 
3、传输速度快 
4、数据源和目的地址和数据封装在数据包中 
5、UDP适用于一次只传送少量数据、对可靠性要求不高的应用环境 
如:网络直播,网络视频会议等 
UDP建立Socket使用 :DatagramSocket 
传输数据包使用:DatagramPacket 
UDP无论客户端还是服务端都是使用DatagramSocket,不像TCP有区分

"/**

    客户端

   */"

    publicstatic void udpDemo() {

       new Thread(new Runnable() {

 

           @Override

           public void run() {

                try {

                    "// 1、建立socket连接,发送端指定端口为10000"

                    DatagramSocket ds = newDatagramSocket(10000);

                   "// 2、创建数据包,创建要发送的数据"

                    byte[] by = "abcdefg".getBytes();

                    "// 设置dp的发送数据内容,长度,目标地址,目标端口"

                    DatagramPacket dp = newDatagramPacket(by, by.length,

                            InetAddress.getByName("192.168.1.189"),10001);

                    "// 3、socket发送dp中的数据"

                    ds.send(dp);

                    "// 4、关闭资源"

                    ds.close();

                } catch (SocketException e) {

                    e.printStackTrace();

                } catch (UnknownHostExceptione) {"// 未知主机异常"

                    e.printStackTrace();

                } catch (IOException e) {

                    e.printStackTrace();

                }

           }

       }).start();

 

       "/**

       服务端

       */"

       new Thread(new Runnable() {

 

           @Override

           public void run() {

                try {

                    "// 1、建立socket连接,接收端指定端口为10000"

                    DatagramSocket ds = newDatagramSocket(10001);

                    "// 2、创建数据包,并创建byte缓冲区,接收数据大小"

                    byte[] by = new byte[1024];

                    "// 设置接收端的存储数组,存储的长度"

                    DatagramPacket dp = newDatagramPacket(by, by.length);

                    "// 3、把接受的数据存入dp中,receive()方法是一个阻塞式方法,若没有接收数据,则会一直阻塞,直到接受到数据"

                    ds.receive(dp);

                    "// dp.getData()接收到的数据是byte[]类型的"

                   System.out.println(dp.getPort()

                            + newString(dp.getData(), 0, dp.getLength()) + " : "

                            + dp.getLength() + ": " + dp.getAddress() + " : ");

                    ds.close();

 

                } catch (SocketException e) {

                    e.printStackTrace();

                } catch (UnknownHostExceptione) {"// 未知主机异常"

                    e.printStackTrace();

                } catch (IOException e) {                   e.printStackTrace();

                }

           }

       }).start();

    }

键盘聊天程序

 

    publicstatic void udpDemo2() {

 

       "/**

        * 发送端

        */"

       new Thread(new Runnable() {

 

           @Override

           public void run() {

 

                try {

                "//创建DatagramSocket对象,并指定端口"

                    DatagramSocket ds = newDatagramSocket(2004);

                    BufferedReader br = newBufferedReader(new InputStreamReader(

                            System.in));

                    String line = null;

                    while ((line =br.readLine()) != null) {

                        "// 需要放在while内部,line是读取的数据,在readLine()是收到数据赋值给line"

                        DatagramPacket dp = newDatagramPacket(line.getBytes(),

                                line.length(),InetAddress.getByName("192.168.1.189"),

                                2003);

                        ds.send(dp);

                        "// 先发送之后再判断是不是结束,若是放在开头则会出现发送端断开,接受断不断开,因为数据没有发送出去"

                        if ("over".equals(line))

                            break;

                    }

                    ds.close();

                    br.close();

 

                } catch (Exception e) {

                    e.printStackTrace();

                }

           }

       }).start();

       "/**

        * 接收端

        * 注意:

        * new String(dp.getDate(),0,dp.getLength())要指定从0开始,否则出现数据异常

        */"

       new Thread(new Runnable() {

 

           @Override

           public void run() {

 

                try {

                    DatagramSocket ds = newDatagramSocket(2003);

                    byte[] by = new byte[1024];

                    "// 定义在外面不用每一次都重新建立一个数据包"

                    DatagramPacket dp = new DatagramPacket(by,by.length);

                    while (true) {

                        ds.receive(dp);

                        String data = newString(dp.getData(), 0, dp.getLength());

                        "// 判断是不是结束标示"

                        if (data.equals("over"))

                            break;

                        System.out.println(data+ ":" + dp.getAddress());

                    }

                    "// 关闭资源"

                    ds.close();

                } catch (Exception e) {

                    e.printStackTrace();

                }

           }

       }).start();

    }

TCP:传输控制协议 
1、面向连接,使用三次握手机制 
2、链接稳定,可靠性高 
3、主要用于传输大文件数据 
如:下载大文件

1、客户端使用的是:Socket,一开始可以明确服务端的ip和端口 
2、服务端使用的是:ServerSocket,明确自己的端口,(不需要ip指定地址),可以指定同时连接的最大数 
3、为了区分不同客户端之间的数据,服务端的accept()方法获取的是客户端的socket流对象,因为要操作的数据客户端自己最清楚,使用客户端的Socket对象来操作数据 
Socket流在Socket对象一建立的时候就自动创建了 
getInputStream()获取输入流 
getOutputStream()获取输出流

注意:服务端必须要先打开,客户端才能开始链接并发送数据 
ServerSocket ss = new ServerSocket(2005,3);3是指定客户端同时连接进来的最大数 
Socket属于传输层

    publicstatic void tcpDemo1() {

       "/**

        * 服务端

        */"

       new Thread(new Runnable() {

           @Override

           public void run() {

                try {

                    "// 1、建立ServerSocket对象,并确定端口号"

                   ServerSocket ss = newServerSocket(2005);

                    "/**

                     * 2、返回一个socket对象,这个对象就是链接进来的客户端对象

                     * 使用链接进来的客户端对象来区分不同客户端数据

                     * ss.accept()是一个阻塞式方法,没有客户端链接进来的时候阻塞,不会消耗资源

                    */"

                    Socket s = ss.accept();

                    "// 3、获取客户端发来的数据,然后就是io操作"

                    InputStream in =s.getInputStream();

                    int len = 0;

                    while ((len = in.read()) !=-1) {

                        System.out.print((char)len);

                    }

                    "// 服务端必须关闭资源,防止资源被持续占用"

                    s.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

           }

       }).start();

       "/**

        * 客户端

        */"

       new Thread(new Runnable() {

           @Override

           public void run() {

                try {

                    "// 1、建立Socket对象,同时会自动产生Socket流"

                    Socket s = newSocket(InetAddress.getByName("192.168.1.189"), 2005);

                    "// 2、获取输出流,就是客户端要发送数据到服务端"

                    OutputStream out =s.getOutputStream();

                    "// 3、传输的数据是字节流"

                    out.write("abcdefg".getBytes());

                    "// 4、客户端关闭链接"

                    s.close();

                } catch (UnknownHostExceptione) {

                    e.printStackTrace();

                } catch (IOException e) {

                    e.printStackTrace();

                }

           }

       }).start();

    }

文件转换器: 
客户端发送一段字母,服务端变为大写并返回给客户端 
客户端可以一直发送,直到发送over,并且over不会被转换并打印

必须要注意的问题是回车符 问题 
readLine()会读取回车符之前的数据,不包含回车符 
当使用readline()时:一旦按下回车符,客户端会发送数据,若是使用了缓存必须要先flusd()才会发送数据 
发送的数据中是不包含回车符,服务器接收端的readLine()方法读取不到回车符就会一直等待(阻塞式方法) 
所以在发送数据时必须要在write()中手动加入回车符:\r\n

不使用readLine()时候,而是使用自定义缓存也会产生回车符 问题 
自定义缓存会发送你所有的输入数据,此时你在客户端按下回车,这时候会发送数据 
但是同时发送的数据中会加入回车符 (比你输入的字符多两个字符) 
回车符也会传入到服务器端,這時候輸出的數據就會自动换行,因为有回车符 
同时进行over判断的时候也会返回false,因为多了回车符

public static void tcpToUpperCaseDemo() {

 

       "/**

       * 服务端

       */"

       new Thread(new Runnable() {

 

           @Override

           public void run() {

                try {

                    "// 建立serversocket连接,指定端口号"

                    ServerSocket ss = newServerSocket(2011);

                    "// 获取链接进来的客户端socket对象"

                   Socket s = ss.accept();

                    System.out.println("获取链接:" +s.getInetAddress());

                    "/ 定义输入流和输出流的缓冲区对象"

                    BufferedReader br = newBufferedReader(new InputStreamReader(

                            s.getInputStream()));

                    BufferedWriter bw = newBufferedWriter(new OutputStreamWriter(

                           s.getOutputStream()));

                    int num = 0;

                    char[] by = new char[1024];

                    while ((num = br.read(by)) != -1) {

                        "// 这里并不需要num-2,因为在数据传输过来的时候已经去掉了回车符"

                        String message = newString(by, 0, num);

                        System.out.println("收到数据是:"+ message);

                        "// 返回大写数据"

                       bw.write(message.toUpperCase());

                        bw.flush();

                    }

                    s.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

       }).start();

       "/**

       * 客户端

       */"

       new Thread(new Runnable() {

           @Override

           public void run() {

                try {

                    "// 建立客户端socket连接,指定服务器ip地址和端口"

                    Socket s = newSocket(InetAddress.getByName("192.168.1.189"), 2011);

                    "// 定义socket流的输入流"

                    BufferedReader br = newBufferedReader(new InputStreamReader(

                           s.getInputStream()));

                    "// 定义socket流的输出流"

                    BufferedWriter bw = newBufferedWriter(new OutputStreamWriter(

                           s.getOutputStream()));

                    "// 定义键盘的输入流"

                    BufferedReader in = newBufferedReader(new InputStreamReader(

                            System.in));

                    int num = 0;

                    int snum = 0;

                    char[] ch = new char[1024];

                    char[] sin = new char[1024];

                    System.out.println("开始录入数据:");

                    while ((snum =in.read(sin)) != -1) {

                        "// 把获取的数据变成字符串,同时 -2 是把输入数据的回车符(\r\n)去掉,否则下面的判断返回false"

                        String message = newString(sin, 0, snum - 2);

                        System.out.println("发送数据数据:"+ message);

                        "// 发送数据"

                        bw.write(message);

                        "// 必须要flush(),因为使用缓存默认会保存在缓存中,刷新后才会正式发送出去"

                        bw.flush();

                        "// 定义接收socket返回的数据"

                        while ((num =br.read(ch)) != -1) {

                            String back = newString(ch, 0, num);

                           System.out.println(back);

                            "// 当返回了一次数据后就不用接收了,因为没有数据,所以用break跳出"

                            break;

                        }

                        if ("over".equals(message)){

                            break;

                        }

                    }

                    System.out.println("结束+1");

                   in.close();

                    s.close();

                } catch (UnknownHostExceptione) {

                    e.printStackTrace();

                } catch (IOException e) {

                    e.printStackTrace();

                }

           }

       }).start();

    }

tcp图片上传

public static void photoUp() {

    "/**

    *服务器端

   */"

    newThread(new Runnable() {

       @Override

       public void run() {

           try {

                ServerSocket ss = newServerSocket(2003, 3);

                Socket s = ss.accept();

                System.out.println("链接:" +s.getInetAddress());

                "获取网络对象输入流"

                InputStream in =s.getInputStream();

                FileOutputStream fos = newFileOutputStream("c://demo//0.mp3");

                int len = 0;

                byte[] by = new byte[1024];

                while ((len = in.read(by)) != -1){

                    fos.write(by, 0, len);

                }

                "//上传完毕后返回信息给客户端"

                out.write("上传完毕!!".getBytes());

                fos.close();

                s.close();

           } catch (IOException e) {

                e.printStackTrace();

           }

       }

   }).start();

 

    "/**

    * 客户端

    */"

    newThread(new Runnable() {

        @Override

       public void run() {

           try {

                "//建立Socket"

                Socket s = newSocket(InetAddress.getByName("192.168.1.189"), 2003);

                "获取网络兑现输出流"

                OutputStream out =s.getOutputStream();

                FileInputStream in = newFileInputStream("c://demo//love.mp3");

                "获取网络对象输入流"

                BufferedReader in = newBufferedReader(new InputStreamReader(s.getInputStream()));

                int len = 0;

                byte[] by = new byte[1024];

                while ((len = in.read(by)) != -1){

                    out.write(by, 0, len);

                    out.flush();

                }

                "//通知服务器上传完成,否则服务器一直等待"

                s.shutdownOutput();

                "获取服务器返回的数据"

                String line = in.readLine();

                System.out.println(line);

                in.close();

                s.close();

           } catch (UnknownHostException e) {

                e.printStackTrace();

           } catch (IOException e) {

                e.printStackTrace();

           }

       }

   }).start();

}

实现多个客户端同时连接服务器

    publicstatic void servers() {

       "/**

        * 服务端

        */"

       new Thread(new Runnable() {

           @Override

           public void run() {

                try {

                    ServerSocket ss = new ServerSocket(2004);

                    "// 创建3个连接"

                    for (int i = 0; i < 3;i++) {

                        Socket s = ss.accept();"accept()是阻塞式方法,当没有客户端连接时进行阻塞"

                        methoserver(s, i);

                    }

                } catch (IOException e) {

                    e.printStackTrace();

                }

           }

       }).start();

       "/**

        * 客户端,创建3个客户端,同时连接

        */"

       for (int i = 1; i < 4; i++) {

           methodclient(i);

       }

    }

 

    "/**

    * 封装服务器的共性方法

    * @param s

    */"

    publicstatic void methoserver(final Socket s, final int count) {

       new Thread(new Runnable() {

           @Override

           public void run() {

               System.out.println("链接:" +s.getInetAddress());

                InputStream in;

                try {

                    in = s.getInputStream();

                    FileOutputStream fos = newFileOutputStream("c://demo//" + count

                            + "1.mp3");

                    int len = 0;

                    byte[] by = new byte[1024];

                    while ((len = in.read(by))!= -1) {

                        fos.write(by, 0, len);

                    }

                    fos.close();

                    s.close();

                    System.out.println("客户端连接断开");

                } catch (IOException e) {

                    e.printStackTrace();

                }

           }

       }).start();

    }

 

    "/**

    * 封装客户端方法

     * 创建一个客户端,并上传数据

    */"

    publicstatic void methodclient(final int count) {

       new Thread(new Runnable() {

           @Override

           public void run() {

                try {

                    System.out.println("创建一个客户端:"+ Thread.currentThread());

                    Socket s = newSocket(InetAddress.getByName("192.168.1.189"), 2004);

                    OutputStream out =s.getOutputStream();

                    System.out.println("开始读取文件...");

                    FileInputStream in = newFileInputStream("c://demo//" + count

                            + ".mp3");

                    int len = 0;

                    byte[] by = new byte[1024];

                    while ((len = in.read(by))!= -1) {

                        out.write(by, 0, len);

                        out.flush();

                    }

                    System.out.println("数据发送完毕");

                    in.close();

                    s.close();

                    System.out.println("连接断开:" +Thread.currentThread());

 

                } catch (UnknownHostExceptione) {

                    e.printStackTrace();

                } catch (IOException e) {

                    e.printStackTrace();

                }

           }

       }).start();

    }

多线程下载文件: 
HttpURLConnection: 
通过URL获取网络资源的位置,内部封装了Socket的相关方法,getInputStream()和getOutputStream() 
conn.connect();连接 
conn.disconnect();断开连接,Socket也会断开

实现方式: 
1、获取网络文件的大小 
2、在本地通过RandomAccessFile 创建一个文件,大小为网络文件大小,关闭网络资源 
3、开启线程,在每一个线程中下载网络文件的一段数据 
* |–1、首先通过httpURLconnection连接网络资源 
* |–2、设置网络的获取方法,超时时间等 
* |–3、通过setRequestProperty(“Range”, “bytes=”+start+”-“+end);设置下载数据的开始和结尾(格式绝对不能写错) 

HTTP 协议的 Range 头可以指定从文件的什么位置开始下载,下载到什么位置结束。单位为 byte ,数据范围包含头不包含尾.

假如 Range 指定要读取到文件的 5104389 的字节数位置,但是下载的文件本身只有 4104389 个长度。

那么下载操作自动会在 4104389 处停止,因此不会下载到多余的无效数据. 
4、开始下载数据

public static void httpUrlConnectionDemo(){

    try{

       "// url获取远程网络对象"

       URL url = new URL("http://127.0.0.1:8080/demo.avi");

       "获取HttpURLConnection 对象"

       HttpURLConnection conn = (HttpURLConnection) url.openConnection();

       conn.setReadTimeout(3000);

       conn.setRequestMethod("GET");

       conn.connect();

       "// 获取远程对象的文件大小"

       int length = conn.getContentLength();

       conn.disconnect();

       "// 创建本地文件,并进行健壮性判断"

       File file = new File("c://demo/ccc.avi");

       if (file.exists()) {

           file.delete();

           file.createNewFile();

       } else {

           file.createNewFile();

       }

       "// 设置文件的长度"

       RandomAccessFile raf = new RandomAccessFile(file, "rwd");

       raf.setLength(length);

       "// 开启3个线程进行下载"

       int start = 0;

       int end = 0;

       "// 获取每一段结尾的长度"

       int[] lenend = new int[] { length / 3, length / 3 * 2, length };

       for (int i = 0; i < 3; i++) {

           end = lenend[i];

           "// 开启线程并进行下载"

           threadDownload(start, end);

           start = end;

       }

    }catch (Exception e) {

       e.printStackTrace();

    }

}

 

"/**

 * 开始进行下载

 *@param start

 *@param end

 */"

public static void threadDownload(final intstart, final int end) {

    newThread(new Runnable() {

       @Override

       public void run() {

           URL url = null;

           File file = null;

           HttpURLConnection conn = null;

           try {

                "// 获取远程网络文件对象"

                url = new URL("http://127.0.0.1:8080/demo.avi");

                conn = (HttpURLConnection)url.openConnection();

                "//设置超时时间"

                conn.setReadTimeout(3000);

                "//设置方法"

                conn.setRequestMethod("GET");

                "// 设置获取的数据范围, 重点"

                conn.setRequestProperty("Range","bytes=" + start + "-" + end);

                conn.connect();

           } catch (Exception e1) {

                e1.printStackTrace();

           }

           try {

                "// 输出文件的目地地址"

                file = new File("c://demo/ccc.avi");

                "// 创建随机文件读写对象,并声明权限"

                    RandomAccessFile raf = newRandomAccessFile(file, "rwd");

 

"// 设置文件的指针位置从那里开始:重要,不设置默认从0开始写入"

                    raf.seek(start);

                    "// 开始下载数据"

                    InputStream in =conn.getInputStream();

                    int len = 0;

                    "对文件进行写入"

                    while ((len = in.read(by))!= -1) {

                        raf.write(by, 0, len);

                    }

                    System.out.println(start + "  : " + end);

                    System.out.println(file.length()+ " : " + Thread.currentThread());

                    raf.close();

                    conn.disconnect();

 

                } catch (Exception e) {

                    e.printStackTrace();

                }

           }

       }).start();

    }

}

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

 


0 0
原创粉丝点击