黑马程序员-Java 网络编程

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

一、概要

学习网络编程需要向对网络模型有一定的了解,主要需要了解的网络模型有OSI参考模型和TCP/IP参考模型,现在TCP/IP模型应用最为广泛,网络编程一般都是针对TCP/IP协议参考模型的编程。但是作为学习时,OSI的学习也是必不可少的,OSI分为七层协议,分别是:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。TCP/IP模型只有四层,分别是:网络访问层、互联网层、传输层和应用层。接下来要学习的内容主要在传输层(UDPTCP协议)。

二、TCPUDP和套接字

说道TCPUDP不得不对两者进行一次比较,首先TCPUDP的相同之处在于其二者都是传输层的协议,提供端到端服务。不同之处在于:

  • UDPUDP不需要建立连接,协议头部简单,不需要反馈帧,适用于对数据准确度要求不到量大,允许丢帧出现,每个暑假的大小限制在64K内,是一种不可靠连接,但是速度快。适用于对数据可靠度要求不高,占用带宽较大,允许丢帧,传输延迟小的情况:如在线播放视频、实时语音/视频聊天、远程监控等。
  • TCPTCP传输数据前需要先建立连接,即三次握手,断开连接时需要四次以释放连接,协议头部数据复杂,数据需要反馈帧来确保数据的每一帧都成功送达,是一种可靠连接,但是效率较UDP稍低。TCP适合用于对数据可靠度要求高的情况:如文件传输、浏览网页、网络聊天、控制信息等。

    套接字是TCP/IP的基本单元,一个IP地址和一个端口号在一起可以称为一个套接字,针对不同的协议UDP对应的套接字为DatagramSocketTCP对应的套接字为SocketServerSocket,是为网络服务提供的一种机制,通信的两端都能拿到Socket,网络通信其实就是Socket之间的通信,数据在两端通过SocketIO进行传输。通信如下图所示:

套接字间的通信

三、UDP

Java中操作UDP协议的相关类为DatagramSocket是用来发送和接收数据报包的套接字,数据报套接字是包投递服务的发送或接收点。每个在数据报套接字上发送或接收的包都是单独编址和路由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。 、DatagramPacketUDP的数据包,用于UDP数据的封装。

一般使用UDP进行数据传输时的步骤如下:

  1. 建立UDP套接字服务。
  2. 封装待发送的数据(用DatagramPacket类进行封装)。
  3. 使用第一步建立的套接字服务将数据发送出去(使用send(DatagramPacket)方法)。
  4. 关闭资源。

UDP数据的一般接收步骤如下:

  1. 建立UDP套接字服务,指定监听的端口。
  2. 定义数据包,用于存储待接收的数据。
  3. 通过UDP套接字服务接收数据(使用receive(DatagramPacket)方法接收数据)。
  4. 通过数据包取出接收到的数据。
  5. 关闭资源。

其中套接字的receive方法是一种线程阻塞式的方法,如同读取键盘输入数据一样,没有数据会等待数据,当数据到来时会唤醒线程。下面通过一个简易聊天程序演示UDP套接字服务的使用:

import java.io.*;
import java.net.*;
// 定义发送线程
class ChatSender implements Runnable{
    private DatagramSocket mSocket;
    public ChatSender(DatagramSocket socket) {
        mSocket = socket;
    }
    public void run() {
        try {
            // 用于读取键盘输入数据
            BufferedReader reader = new BufferedReader(
                new InputStreamReader(System.in));
            String line = null;
            while((line = reader.readLine()) != null) {
                // 当输入end后就不再发送
                if(line.equals("end")) break;
                // 封装发送数据包
                DatagramPacket packet =
                       new DatagramPacket(line.getBytes(),line.length(),
                                    InetAddress.getByName("127.0.0.1"), 8888);
                // 发送数据
                mSocket.send(packet);
                // 接收回复数据
                byte[] buf = new byte[1024];
                DatagramPacket rpacket = new DatagramPacket(buf, buf.length);
                mSocket.receive(rpacket);
                int len = rpacket.getLength();
                String data = new String(rpacket.getData(), 0, len);
                // 显示回复数据
                System.out.println("回复:"+data);
            }
            // 关闭流和套接字
            reader.close();
            mSocket.close();
        } catch(IOException e) {
            e.printStackTrace();
        }
    }
}

class ChatReceiver implements Runnable {
    private DatagramSocket mSocket;
    public ChatReceiver(DatagramSocket socket) {
        mSocket = socket;
    }
    public void run() {
        try {
            while(true) {
                // 接收消息数据
                byte[] buf = new byte[1024];
                DatagramPacket packet = new DatagramPacket(buf, buf.length);
                mSocket.receive(packet);
                int len = packet.getLength();
                // 回复数据(转成大写)
                String data = new String(packet.getData(), 0, len).toUpperCase();
                if(data.contains("END")) break;
                // 封装回复数据
                DatagramPacket spacket =
                       new DatagramPacket(data.getBytes(),data.length(),
                                    InetAddress.getByName("127.0.0.1"), packet.getPort());
                mSocket.send(spacket);
            }
            mSocket.close();
        } catch(IOException e) {
            e.printStackTrace();
        }
    }
}
class ChatDemo {
    public static void main(String[] args) throws Exception{
        // 创建一个udp套接字给发送者
        new Thread(new ChatSender(new DatagramSocket())).start();
        // 创建一个监听8888端口的套接字给接收者
        new Thread(new ChatReceiver(new DatagramSocket(8888))).start();
    }
}

程序的运行效果如下图所示,每当输入一条数据,然后发送,便会得到一条回应,内容是将发送过去的内容装换成大写并返回。

UDP聊天程序运行效果

另外值得说明的是,UDP可以发送广播数据,当将目标地址写为192.168.0.255这种以255结尾的类型IP地址时,此地址为广播地址,在同一个局域网,并且在同一个网段时(也就是地址为192.168.0.1-192.168.0.254内时,便都可以接收到其发送的数据),也可以将目标地址设置为255.255.255.255,这种广播发送的范围会更加广泛,一般整个局域网都可以接收到(如果路由器不丢弃的话)。这种功能可用于扫描等,如飞秋上线时通知其他同局域网内的用户。

四、TCP

TCP用于可靠数据的传输,当数据不能丢包或者不能出错时都会使用TCP协议来传输数据,既然可靠性提高了,那么必须牺牲点什么,TCP牺牲的便是效率,TCP连接通过三次握手建立了有连接的服务,其三次握手可以简单地说如三句话,“我来了”,“欢迎欢迎”,“谢谢”。也就是第一次高速对方我需要请求连接,第二次对方回应你的请求,第三此我收到了对方的请求然后回应对方正式开始。

UDP操作上的不同再用,TCP将请求者和被请求者分别称为客户端和服务端,对应的套接字类分别为SocketServerSocketTCP的数据不用封装成数据包,二是通过IO流相互通信。

下载文件,上传文件等一般都是使用TCP协议,下面通过一个示例演示通过TCP协议进行文件的并发上传操作,文件的并发上传需要注意的是要为每个Socket单独启动一个线程处理上传任务,不能让任何动作影响ServerSocketaccept()方法,这样才能保证并发上传文件。示例代码如下:

import java.io.*;
import java.net.*;
class UploadClient {
    public static void main(String[] args) throws Exception {
        if(args.length < 1) {
            System.out.println("请指定一个文件"); return ;
        }

        File file = new File(args[0]);
        if(!(file.exists() && file.isFile())) {
            System.out.println("文件不存在"); return ;
        }

        // 上传到指定的位置
        Socket socket = new Socket("127.0.0.1", 8888);
        BufferedInputStream bin =
            new BufferedInputStream(new FileInputStream(file));
        BufferedOutputStream bout =
            new BufferedOutputStream(socket.getOutputStream());
        byte[] buf = new byte[1024];
        int len = 0;
        // 将文件写入流中
        while((len=bin.read(buf)) != -1) {
            bout.write(buf, 0, len);
        }
        //告诉服务端数据已写完
        socket.shutdownOutput();

        // 读取服务端的回复
        InputStream in = socket.getInputStream();
        byte[] buffer = new byte[1024];
        int num = in.read(buffer);
        System.out.println(new String(buffer, 0, num));

        bin.close();
        socket.close();
    }
}

class UploadServer {
    public static void main(String[] args) throws Exception {
        ServerSocket mServerSocket = new ServerSocket(8888);
        while(true) {
            // 接收客户端的请求
            Socket socket = mServerSocket.accept();
            // 但接受到了客户端的上传后,便为其开启一个线程用于上传数据
            new Thread(new FileUploadThread(socket)).start();
        }
        //mServerSocket.close();
    }
}
class FileUploadThread implements Runnable {
    private Socket mSocket;
    FileUploadThread(Socket socket) {
        mSocket = socket;
    }
    public void run() {
        // 获取客户端的ip
        String ip  = mSocket.getInetAddress().getHostAddress();
        try {
            System.out.println(ip + "开始上传");
            BufferedInputStream bin =
                new BufferedInputStream(mSocket.getInputStream());
            // 用客户端ip和当前时间作为文件名称
            String filename = "files\\" + ip + "-"
                + Long.toHexString(System.currentTimeMillis()) + ".jpg";
            File file = new File(filename);
            BufferedOutputStream bout =
                new BufferedOutputStream(new FileOutputStream(file));
            byte[] buf = new byte[1024];
            int len = 0;
            while((len=bin.read(buf))!=-1) {
                bout.write(buf, 0, len);
            }
            // 上传结束后,回复客户端一句“上传成功”
            OutputStream out = mSocket.getOutputStream();
            out.write("上传成功".getBytes());
            bout.close();
            mSocket.close();
        }
        catch (Exception e) {
            throw new RuntimeException(ip+"上传失败");
        }
    }
}

使用ServerSocket完成一个简易的服务器,然后使用浏览器访问这个服务器,根据不同的访问内容,返回不同的响应内容,如访问"/Hello"返回“你好啊!”,访问"/Time"返回当前时间等,示例代码如下:

import java.io.*;
import java.net.*;
import java.util.*;
import java.text.*;
class HttpServer {
    public static void main(String[] args) throws Exception {
        ServerSocket mServer = new ServerSocket(8888);
        while(true) {
            // 接受客户端请求
            Socket socket = mServer.accept();
            // 获取请求流
            BufferedInputStream bin =
                new BufferedInputStream(socket.getInputStream());
            byte[] buffer = new byte[1024];
            int len = bin.read(buffer);
            // 获取请求内容如"/index.html"为"127.0.0.1/index.html"的
            String request = new String(buffer, 0, len).split(" ")[1];
            System.out.println(request);

            // 回复流
            BufferedOutputStream bout =
                new BufferedOutputStream(socket.getOutputStream());
            // 请求"http://127.0.0.1/Hello"时返回"你好啊!"
            if(request.equalsIgnoreCase("/Hello")) {
                bout.write("你好啊!".getBytes());
            } else if(request.equalsIgnoreCase("/Time")) {
                // "/Time"时,返回当前时间
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy:MM:DD HH:mm:ss");
                String time  = dateFormat.format(new Date());
                bout.write(time.getBytes());
            } else if(request.equalsIgnoreCase("/Exit")) {
                // "/Exit"时,关闭服务器
                bout.write("已暂停服务".getBytes());
                break;
            }
            bout.close();
        }
        mServer.close();
    }
}

在浏览器中访问"http://localhost:8888/time"时,会获取服务器的当前时间,如下图:

浏览器访问效果图

五、其他

  1. URL类:类 URL 代表一个统一资源定位符,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询。
  2. URLConnection类:代表程序和URL之间的通信链接,一般的使用
    1. 通过在 URL 上调用 openConnection 方法创建连接对象。
    2. 处理设置参数和一般请求属性。
    3. 使用 connect 方法建立到远程对象的实际连接。
    4. 远程对象变为可用。远程对象的头字段和内容变为可访问。

      通过URLConnection从网络上下载一张图片,将其保存至本地。示例代码如下:

import java.net.*;
import java.io.*;
class  URLConnectionDemo {
    public static void main(String[] args) throws Exception {
        // 一个网络图片地址
        URL url = new URL("http://www.itheima.com/images_new/logo.jpg");
        // 打开网络连接
        URLConnection conn = url.openConnection();
        // 输入流
        BufferedInputStream bin = new BufferedInputStream(conn.getInputStream());
        // 输出流,用于保存下载的图片文件
        BufferedOutputStream bout =
            new BufferedOutputStream(new FileOutputStream("pic.jpg"));
        byte[] buf = new byte[1024];
        int len = 0;
        while((len = bin.read(buf)) != -1) {
            bout.write(buf, 0, len);
        }
        // 关闭流
        bin.close();
        bout.close();
    }
}
  1. InetSocketAddress类:此类实现 IP 套接字地址(IP 地址 + 端口号)。即这个类将IP地址和端口封装在了一起,使用起来更加方便些。
  2. 域名解析:是指解析出域名指向网站的IP地址,一般域名解析的步骤为:首先查看系统hosts文件中是否已经存在对应的域名关系,若已经存在,则之间访问其对应的ip,如localhost对应的ip为127.0.0.1,这是本机的回环地址,访问127.0.0.1就是在访问本机自身。如果hosts文件中没有待解析的域名,则系统会启动域名解析服务,即想域名解析服务器请求对应的ip地址(这些域名解析服务器一般都是固定的公共域名解析服务器,电信的、联通的等),找到域名对应的地址后便可以请求改地址。
时间: 2024-10-18 07:35:49

黑马程序员-Java 网络编程的相关文章

黑马程序员------Java网络编程学习总结

Java培训.Android培训.iOS培训..Net培训</a>.期待与您交流! 网络模型: OSI参考模型 TCP/IP参考模型 网络参考模型图 网络通讯的要素: IP地址:InetAddress 网络中设备的标识 不容易记忆,可以用主机名 本地回环地址:127.0.0.1:主机名:localhost 端口号 用于标识进程的逻辑地址,不同进程的标识 有效端口:0~65535,其中0~1024为系统使用端口或保留端口 网络协议  是一种网络通讯的规则  常见协议:TCP,UDP UDP: 1

黑马程序员——Java网络编程之UDP传输

网络编程 网络模型 通讯要素:InetAddress(对象):ip地址,网络中设备的标识,不可记忆,可用主机名,本地回环地址:127.0.0.1主机名localhost 端口号 传输协议:UDP,将数据的源及目的封装成数据包中,不需要建立连接,每个数据包的大小限制在64K内,无连接,是不可靠协议,不需要建立连接,速度快.力求速度,不求数据的准确性.比如聊天软件,网络会议. TCP:建立连接,形成传输数据的通道,在连接中进行大数据量传输,通过三次握手完成连接,是可靠协议,必须建立连接效率稍低. S

黑马程序员_Java网络编程

1,IP地址和InetAddress IP地址是互联网上每台计算机都有的自己的标记,IP地址分为5类,A类保留给政府,B类给中等规模的公司,C类给任何需要的人,D类用于组播,E类用于实验. A类  1.0.0.1-126.255.255.254  B类  128.0.0.1-191.255.255.254   C类  192.0.0.1-223.255.255.254  D类 244.0.0.1-239.255.255.254 E类 240.0.0.1-255.255.255.254 在以后开发

黑马程序员 ---------- Java网络技术之 ---正则表达式 (Day06)

---------------------- ASP.Net+Unity开发..Net培训.期待与您交流! ---------------------- 正则表达式 正则表达式:基本知识 1  字符,   2 字符类 , 3 预定义字符类 , 4 边界匹配器 ,5 Greedy 数量词,6 Logical 运算符 详解: 1 字符 x   字符 x \\ 反斜线字符 \0n 带有八进制值 0 的字符 n (0 <= n <= 7) \0nn 带有八进制值 0 的字符 nn (0 <= n

黑马程序员__GUI_网络编程

------<a href="http://www.itheima.com" target="blank">Java培训.Android培训.iOS培训..Net培训</a>.期待与您交流! ------- GUI 1.事件处理就是处理用户的一个操作这个事件,在事件发生的时候程序应做出什么反应.当事件发生在哪个组件上, 哪个组件就可以说是事件源.给事件源添加一个监听器对象,监听器对象中包含若干事件处理方法.如果事件发生了, 事件处理方法就会自

黑马程序员_JAVASE_网络编程上

网络模型: OSI模型 TCP/IP模型 网络通信要素 ip地址 端口 通信协议 通信过程 1.找到对方IP地址 2.数据要发送到对方指定的应用程序上,为了标识这些应用程序,用唯一的数字进行标识,这些数字符号就是端口 3.定义通信规则,tcp还是udp还是其他 应用层协议 http,ftp,smtp 解析主机地址: InetAddress ia = InetAddress.getByName("www.baidu.com"); String ip = ia.getHostName();

黑马程序员-Java面向对象编程学习总结

------<a href="http://www.itheima.com" target="blank">Java培训.Android培训.iOS培训..Net培训</a>.期待与您交流! ------- Java面向对象编程学习总结: 重要知识点总结如下: 1,抽象,封装,继承,多态是面向对象程序设计中得四个特点. 2,面向对象得软件开发大体分为:面向对象的分析,面向对象的设计,面向对象的实现. 可概括为如下过程:分析用户需求,从问题中抽

黑马程序员_网络编程

IP地址 1.IP地址的分类 A类1.0.0.0到126.0.0.0 B类128.1.0.0到191.254.0.0 C类192.0.1.0到223.255.254.0 D类224.0.0.0到239.255.255.255 E类240.0.0.0到255.255.255.254 局域网内的IP 192.168开头 本地回环地址:127.0.0.1.本机的IP地址,只要网卡工作正常,127.0.0.1 2.java.net.InetAddress 描述IP地址对象的类,没有构造方法 InetAd

黑马程序员——Java I/O基础知识之I/O流

I/O流基础知识--字节流和字符流 文件存储在硬盘中,是以二进制表示的,只有内存中才能形成字符.数据的来源可以有硬盘,内存,控制台,网络,Java把数据从一个地方转到另一个地方的现象称为流,用InputStream和OutputStream接口来表示,这两个流里面的都是以字节为单位的,后来加入了Reader和Writer,里面操作的是字符,是两个字节为单位的. 字节流 字节流将数据写入文件 try { File file =new File("d:" +File .separator+