网络连接

网络工作模式

  • 专用服务器结构(Server-Based),简称BS 例如使用浏览器浏览网页
  • 客户机/服务器模式(Client/Server,简称C/S结构)
  • 对等式网络:(Peer-to-Peer) P2P 点对点 下载 电驴下载

TCP和UDP区别

这里写图片描述

InetAddress类

  • 拥有一系列方法获取本机IP,本地主机等等
public class IPTest {
    public static void main(String[] args) {
        try {
            InetAddress address=InetAddress.getLocalHost();
            System.out.println(address);
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

TCP/IP协议

  • TCP协议 面向连接的、可靠的协议 类似打电话
  • TCP协议必须要双方都是监视的同一端口号,然后获取Socket对象的输入输出流,既可以完成互相之间的通讯。
  • 只要是隶属同一个局域网,就可以用此协议进行通讯,局域网通讯雏形
  • 运行时,先启动服务器端,再启动客户端
  • 服务器端
public class Server {
    public static void main(String[] args) {

        Socket s;
        try {
            //监听的端口号,必须与客户端的端口号一致
            ServerSocket ss=new ServerSocket(8080);
            System.out.println("服务器启动");
            //阻塞状态,等待客户端的接入
            s=ss.accept();
            System.out.println("成功建立连接");
            sendMessage(s);
            receiveMessage(s);  
        } catch (IOException e) {
            // TODO Auto-generated catch block
            System.out.println("一个客户端下线了");
        }
    }

    private static void receiveMessage(Socket s){
        BufferedReader br;
        try {
            br = new BufferedReader(new InputStreamReader(s.getInputStream()));
            while (true) {
                System.out.println("客户端:"+br.readLine());
            }
        } catch (IOException e) {
            System.out.println("客户端已下线,连接已中断!!!");
        }
    }

    private static void sendMessage(final Socket s) {
        new Thread(new Runnable() {
            Scanner scanner=new Scanner(System.in);
            PrintWriter pw;
            public void run() {
                try {
                    pw=new PrintWriter(s.getOutputStream());
                    while (true) {
                        System.out.println("请输入服务器要发送的消息:");
                        pw.println(scanner.next());
                        pw.flush();
                    }
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
  • 客户端
public class Client {
    public static void main(String[] args) {
        try {
            InetAddress address=InetAddress.getLocalHost();
            String str=address.getHostAddress();
            //请求与其通讯的主机IP和端口号,客户端申请建立连接
            Socket s=new Socket(str,8080);
            System.out.println("建立连接");
            sendMessage(s);
            receiveMessage(s);

        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private static void sendMessage(final Socket s){
        new Thread(new Runnable() {
            public void run() {
                PrintWriter pw;
                Scanner scanner=new Scanner(System.in);
                String str;
                try {
                    pw=new PrintWriter(s.getOutputStream());
                    while (true) {
                        System.out.println("请输入客户端要发送的信息:");
                        pw.println(scanner.next());
                        pw.flush();
                    }
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }).start();

    }


    private static void receiveMessage(Socket s){
        BufferedReader br;
        try {
            br = new BufferedReader(new InputStreamReader(s.getInputStream()));
            while (true) {
                System.out.println("服务器端:"+br.readLine());
            }
        } catch (IOException e) {
            System.out.println("服务器已下线,连接中断!!!");
        }
    }
}

UDP协议

  • UDP协议 无连接、不可靠的协议,发送包 类似发短信
  • Datagrampacket包的概念,UDP协议是发送一个个数据包来传递数据的
  • 用来发送消息的Datagramsocket对象不必要指定端口号,因为在那个端口发送都无所谓,只要数据包中包含要接收该数据的IP和端口号就OK了
  • 用来接收的Datagramsocket的对象就一定要指定接收数据的端口号,到指定的端口接收该数据包
  • UDP协议先启动那个端口都可以,因为是不可靠的连接,发送数据时,不一定接收端在线。
  • 服务器端
public class DatagramServser {
    public static void main(String[] args) {
        try {
            //指定端口号,因为在该端口号上,既需要发信息又要收信息,指定端口号,避免定义端个该类对象实例
            DatagramSocket socket = new DatagramSocket(8080);
            System.out.println("服务器启动成功,等待接受数据");
            receiveMessage(socket);
            sendMessage(socket);
        } catch (SocketException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    private static void receiveMessage(final DatagramSocket socket)
            throws IOException {
        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    try {
                        byte[] data = new byte[64 * 1024];
                        DatagramPacket packet = new DatagramPacket(data, data.length);
                        socket.receive(packet);
                        //必须要在构造时指定仅构造收到的数据的长度
                        System.out.println("客户端:" + new String(data,0,packet.getLength()));
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    private static void sendMessage(final DatagramSocket socket)
            throws UnknownHostException, IOException {
        final Scanner scanner = new Scanner(System.in);
        final InetAddress address = InetAddress.getByAddress(new byte[] {
                (byte) 172, 6, 1, (byte) 144 });
        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    System.out.println("请输入要发送的信息:");
                    try {
                        final byte[] data;
                        data = scanner.next().getBytes();
                        DatagramPacket packet = new DatagramPacket(data,data.length, address, 8090);
                        socket.send(packet);
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}
  • 客户端
public class DatagramClient {
    public static void main(String[] args) {
            DatagramSocket socket;
            try {
                //指定端口号,因为在该端口号上,既需要发信息又要收信息,指定端口号,避免定义端个该类对象实例
                socket = new DatagramSocket(8090);
                System.out.println("客户端启动成功");
                sendMessage(socket);
                receiveMessage(socket);



            } catch (SocketException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (UnknownHostException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

    }

    private static void receiveMessage(final DatagramSocket socket)
            throws IOException {
        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    try {
                        byte[] data=new byte[64*1024];
                        DatagramPacket packet=new DatagramPacket(data, data.length);
                        socket.receive(packet);
                        //必须要在构造时指定仅构造收到的数据的长度
                        System.out.println("服务器端:"+new String(data,0,packet.getLength()));
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    private static void sendMessage(final DatagramSocket socket)
            throws UnknownHostException, IOException {
        final Scanner scanner=new Scanner(System.in);
        final InetAddress address=InetAddress.getByAddress(new byte[]{(byte) 172,6,1,(byte) 144});
        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    System.out.println("请输入要发送的信息:");
                    try {
                         byte[] data=scanner.nextLine().getBytes();
                        DatagramPacket packet=new DatagramPacket(data, data.length,address,8080);
                        socket.send(packet);
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

URL

  • 统一资源定位器,表示Internet上某一资源的地址,定位互联网上的资源。
  • 组成:协议名+资源名
通过URL下载网络上的文件
public class Test2 {
    public static void main(String[] args) {
        try {
            File file=new File("D://路飞.jpeg");
            if (!file.exists()) {
                file.createNewFile();
            }
            URL url=new URL("http://img4.duitang.com/uploads/item/201511/21/20151121084015_fLaFU.jpeg");
            //创建URLConnection对象,获取连接对象
            URLConnection connection=url.openConnection();
            //设置处理参数和一般请求属性,对网络资源的读取和提交
            connection.setDoInput(true);//默认是true
            connection.setDoOutput(true);//默认是false
            //使用connection打开连接
            connection.connect();
            //获取输入流,并将其包装
            InputStream is=connection.getInputStream();
            DataInputStream dis=new DataInputStream(is);
            //获取输出流
            DataOutputStream dos=new DataOutputStream(new FileOutputStream(file));
            byte[] b=new byte[1024];
            int index=dis.read(b);
            while (index!=-1) {
                //每次读取数据并将其写入文件,直至文件读取完毕
                dos.write(b, 0, index);
                index=dis.read(b);
            }
            dos.flush();
            dos.close();
            dis.close();
            is.close();
            System.out.println("图片读取完毕");
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
通过URL实现多线程下载
//开启下载的主线程

public class DownLoadUtils {

    //定义下载为静态方法,参数是URL,文件保存位置,下载的线程数
    //可通过类名直接调用
    public static void download(String urls, String path) {
        download(urls, path, 3);
    }


    public static void download(String urls, String path, int threadNum) {
        try {
            File file = new File(path);
            if (!file.exists()) {
                file.createNewFile();
            }
            URL url = new URL(urls);
            URLConnection conn = url.openConnection();
            conn.setDoOutput(true);
            conn.setDoInput(true);
            //获得文件输入流的总长度
            int fileLength = conn.getContentLength();
            System.out.println("文件总长度:" + fileLength);
            //根据分几个线程下载,划分每个线程的下载的长度
            int partLength = fileLength / threadNum;
            //list用来存放下载的各个子线程
            ArrayList<MyThread> list = new ArrayList<MyThread>();
            for (int i = 0; i < threadNum; i++) {
                if (i == threadNum - 1) {
                    //注意如果是最后一个线程的话,下载的长度可能会多一点,因为不一定分配的刚刚好
                    MyThread myThread = new MyThread(url, file, i * partLength,
                            fileLength - i * partLength);
                    list.add(myThread);
                    myThread.start();
                } else {
                    MyThread myThread = new MyThread(url, file, i * partLength,
                            partLength);
                    list.add(myThread);
                    myThread.start();
                }
            }
            //开启统计下载的百分比的线程
            new ProgressThread(list, fileLength).start();

        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

//下载的子线程代码

public class MyThread extends Thread {
    //子线程开始下载需要的参数 URL file start length
    private URL url;
    private int sum;
    private File file;
    private int start;
    private int length;
    //此处必须传URL,直接传URLConnection或者流对象,各个子线程就会因为使用相同的输入流而产生下载错误
    public MyThread(URL url, File file, int start, int length) {
        this.url = url;
        this.file = file;
        this.start = start;
        this.length = length;
    }

    //获取当前子线程下载的进度的方法
    public int getLength() {
        return sum;
    }



    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName()+"开始位置:"+start+"  结束位置:"+(start+length)
                    +"   要下载的长度:"+length);
            InputStream is = url.openConnection().getInputStream();
            RandomAccessFile accessFile = new RandomAccessFile(file, "rw");
            //将输入流跳转到子线程要下载的开始部分
            is.skip(start);
            //将写出流跳转到文件要开始写出的部分
            accessFile.seek(start);
            byte[] b = new byte[1024];
            int index = is.read(b);
            while (index != -1) {
                //其实这里不是必须要对多读取的部分处理,各部分复写也没问题,这样处理的话,百分比显示更精确
                sum += index;
                if (sum +1024>length) {
                    //最后一次可能会过多读取一部分,此处用于将最后一次写出,正好写出剩余的部分,跳出循环
                    accessFile.write(b, 0, index);
                    index = is.read(b,0,length-sum);
                    accessFile.write(b,0,length-sum);
                    sum=length;
                    break;
                } else {
                    accessFile.write(b, 0, index);
                    index=is.read(b);
                }
                //此处睡眠是为了不下载太快,以让其显示百分比的线程刷新百分比输出
                try {
                    Thread.sleep(400);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            accessFile.close();
            is.close();
            System.out.println(Thread.currentThread().getName() + "子线程下载完成");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

//获取当前下载量,并将下载总百分比输出的子线程

public class ProgressThread extends Thread{
    private ArrayList<MyThread> list;
    private int fileLength;
    private int sum;
    private boolean flag=true;
    //将存放线程的list和文件流的总长度作为参数传递过来
    public ProgressThread(ArrayList<MyThread> list, int fileLength) {
        this.list = list;
        this.fileLength = fileLength;
    }

    @Override
    public void run() {
        //不停的遍历list,将其中的子线程的下载量求和,再求百分比,若下载为100%,则跳出循环,子线程执行完毕
        while (flag) {
            sum=0;
            for (MyThread myThread : list) {
                sum+=myThread.getLength();
            }
            int num=(int) (sum*1.0/fileLength*1000);
            double persent=num/10.0;
            System.out.println("已经下载:"+persent+"%");
            if (persent==100) {
                System.out.println("下载完成");
                break;
            }
            try {
                Thread.sleep(400);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }
}

//测试类

public class Test {
     public static void main(String[] args) {
         //要学习封装,便于以后自己活着他人的直接调用,方便易用易理解
         String urls="http://img4.duitang.com/uploads/item/201511/21/20151121084015_fLaFU.jpeg";
         String path="D://wulallalal.jpg";
        DownLoadUtils.download(urls, path,8);
    }
}

封装的理念

  • 要学习着将自己写的代码进行封装,以使其更便于自己或者他人使用,将自己写的代码封装成一个工具类,仅将必要的参数传递进去,这样更有益于代码的重复使用,这样也更有利于其他人的使用,类似与黑盒子,使其他人不必知道其实现过程,只需要将必要的参数传递过去,就也能实现代码的复用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值