1 网络通信协议

1.1 IP地址

ip地址.png

1.2 端口号

端口号.png

1.3 InetAddress基本使用

在java中,可以使用一个类表示ip地址,这个类叫做InetAddress
static InetAddress getLocalHost(): 获取到本机的ip地址对象。
static InetAddress getByName(String host):根据主机名获取到ip地址对象。
String getHostName(): 获取字符串类型的主机名。
String getHostAddress(): 获取字符串类型的ip地址。

  1. import java.io.IOException;
  2. import java.net.InetAddress;
  3. import java.net.UnknownHostException;
  4. /*
  5. 在java中,可以使用一个类表示ip地址,这个类叫做InetAddress
  6. static InetAddress getLocalHost(): 获取到本机的ip地址对象。
  7. static InetAddress getByName(String host):根据主机名获取到ip地址对象。
  8. String getHostName(): 获取字符串类型的主机名。
  9. String getHostAddress(): 获取字符串类型的ip地址。
  10. */
  11. public class Demo01InetAddress {
  12. public static void main(String[] args) throws IOException {
  13. method3();
  14. }
  15. /*
  16. * String getHostName(): 获取字符串类型的主机名。
  17. * String getHostAddress(): 获取字符串类型的ip地址。
  18. */
  19. public static void method3() throws IOException {
  20. //先获取一个InetAddress对象。
  21. InetAddress address = InetAddress.getLocalHost();
  22. //InetAddress里面封装了ip地址和主机名。
  23. //我们可以通过getHostName和getHostAddress单独获取到ip地址和主机名
  24. String hostName = address.getHostName();
  25. String ip = address.getHostAddress();
  26. //打印
  27. System.out.println("主机名为:" + hostName);
  28. System.out.println("ip地址为:" + ip);
  29. }
  30. /*
  31. * static InetAddress getByName(String host):根据主机名获取到ip地址对象。
  32. */
  33. public static void method2() throws IOException {
  34. //根据指定的主机名获取到ip地址对象
  35. //如果在网络中有多个相同的主机名,那么只会获取一个。
  36. //这个方法,不仅可以根据主机名获取ip地址对象,也可以根据字符串类型的ip地址去获取。
  37. //参数传递主机名是可以的,如果传递一个ip地址,也是可以的。
  38. InetAddress address = InetAddress.getByName("jn-pc");
  39. System.out.println(address);
  40. }
  41. /*
  42. * static InetAddress getLocalHost(): 获取到本机的ip地址对象。
  43. */
  44. public static void method1() throws IOException {
  45. //获取到的是本机的ip地址对象
  46. InetAddress address = InetAddress.getLocalHost();
  47. //InetAddress这个对象中不仅封装了ip地址,还封装了主机名。
  48. System.out.println(address);
  49. }
  50. }

2 UDP和TCP协议

2.1 UDP协议

UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
特点:
1.面向无连接,传输数据的时候不需要建立连接。
2.传输的数据有大小限制(64k)
3.效率高,不安全。
使用场景:聊天,广播。

2.2 TCP协议

先建立连接,然后传输数据。
TCP三次握手.png
特点(了解)
1.面向连接,发送数据前必须建立连接。
2.传输数据没有大小限制。
3.安全,效率低。
使用场景:长传,下载。

2.3 UDP的发送与接收

UDP发送接收流程
UDP发送接收流程.png
Demo01Sender类

  1. import java.io.IOException;
  2. import java.net.DatagramPacket;
  3. import java.net.DatagramSocket;
  4. import java.net.InetAddress;
  5. import java.net.UnknownHostException;
  6. /*
  7. UDP程序的发送端
  8. 发送端需要用到两个类
  9. DatagramPacket:叫做数据报包。 相当于快递的箱子,用来封装要发送的数据。
  10. DatagramSocket:叫做套接字,用来发送数据报包对象。相当于快递员,用来发送数据。
  11. DatagramPacket 构造方法:
  12. DatagramPacket(byte[] buf, int length, InetAddress address, int port)
  13. 参数buf:表示要发送的数据,是一个字节数组。
  14. 参数length:表示要发送字节数组的几个长度的数据
  15. 参数address: 数据要发给谁。 目标接收端的ip地址。
  16. 参数port: 接收端程序的端口号。
  17. DatagramSocket 构造方法:
  18. DatagramSocket():使用空参数构造方法创建对象即可。
  19. DatagramSocket 其他方法:
  20. void send(DatagramPacket p): 用来发送数据,参数是一个数据报包对象
  21. void close(): 释放资源
  22. 发送端的步骤
  23. 1. 准备要发送的数据。
  24. 2. 根据要发送的数据封装一个DatagramPacket对象。
  25. 3. 创建DatagramSocket对象,用来发送数据。
  26. 4. 调用send方法,将数据发送出去。
  27. 5. 释放资源。
  28. */
  29. public class Demo01Sender {
  30. public static void main(String[] args) throws IOException {
  31. //准备要发送的数据。
  32. byte[] bArr = "你好网络编程,I'm coming".getBytes();
  33. int len = bArr.length;
  34. InetAddress address = InetAddress.getByName("127.0.0.1");//ip地址,表示要发给自己。
  35. int port = 9527;
  36. //根据要发送的数据封装一个DatagramPacket对象。
  37. DatagramPacket packet = new DatagramPacket(bArr, len, address, port);
  38. //创建DatagramSocket对象,用来发送数据。
  39. DatagramSocket socket = new DatagramSocket();
  40. //调用send方法,将数据发送出去。
  41. socket.send(packet);
  42. //释放资源。
  43. socket.close();
  44. }
  45. }

Demo02Receiver类

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

/*
    UDP程序的接收端

    接收端同样也需要用到两个类。
    DatagramPacket:数据报包。用来封装要接收的数据。快递的箱子。
    DatagramSocket:套接字,用来接收数据。 快递员

    DatagramPacket构造方法:
        DatagramPacket(byte[] buf, int length) 
        参数buf:表示要使用这个字节数组去接收数据。
        参数length:表示要使用这个字节数组的几个长度去接收数据。

    DatagramSocket构造方法:
        DatagramSocket(int port):参数是一个端口号。 表示的是此接收端程序的端口号。

    DatagramPacket其他拆包方法:
        byte[] getData():获取保存数据的字节数组
        int getLength():获取接收到数据的长度。
        InetAddress getAddress():获取发送端的ip地址。
        int getPort():获取发送端程序的端口号。

    DatagramSocket的其他方法:
        void receive(DatagramPacket p):接收方法,可以接收到发送端发送过来的数据。

    接收端的步骤:
        1. 准备一个字节数组。
        2. 创建DatagramPacket对象。
        3. 创建DatagramSocket对象,并且才参数位置指明此接收端程序的端口号。 
        4. 调用DatagramSocket的receive方法,接收数据。
        5. 调用DatagramPacket对象的拆包方法,进行拆包。
        6. 打印数据。
        7. 释放资源
 */
public class Demo02Receiver {
    public static void main(String[] args) throws IOException {
        //准备一个字节数组。
        byte[] bArr = new byte[1024];
        //创建DatagramPacket对象,相当于快递的箱子。
        DatagramPacket packet = new DatagramPacket(bArr, bArr.length);
        //创建DatagramSocket对象,并且才参数位置指明此接收端程序的端口号。 
        DatagramSocket socket = new DatagramSocket(9527);
        //调用DatagramSocket的receive方法,接收数据。
        socket.receive(packet);

        //调用DatagramPacket对象的拆包方法,进行拆包。
        byte[] data = packet.getData();//保存收到数据的字节数组
        int len = packet.getLength();//收到数据的长度
        InetAddress address = packet.getAddress();//发送端的ip地址
        int port = packet.getPort();//发送端的端口号
        //打印数据。
        System.out.println("接收端收到数据:" + new String(data, 0, len));
        System.out.println("数据来自:" + address + ":" + port);

        //释放资源
        socket.close();
    }
}

3 TCP通信

3.1 服务器与客户端

服务器和客户端.png

3.2 TCP中的IO技术

TCP中的IO技术.png

3.3 TCP的客户端实现

(插播:
套接字是两台机器间通信的端点。)
TCP程序的客户端(TCP两端分别是客户端和服务器)

在java中可以使用一个类表示客户端,这个类叫做Socket。

Socket的构造方法:
Socket(InetAddress address, int port):使用这个构造方法创建对象时,会尝试着和服务器进行连接。
参数address:目标要连接服务器的ip地址。
参数port:要连接服务器程序的端口号。

Socket的其他方法:
OutputStream getOutputStream():获取字节输出流,用来向服务器发送数据(写数据)。
InputStream getInputStream():获取字节输入流,用来读取服务器发送过来的数据(读数据).
void close(): 释放资源

实现步骤:
1. 创建一个客户端Socket对象,并且在构造方法中传递要连接服务器的ip地址以及端口号。
2. 调用Socket对象的getOutputStream方法,获取到一个输出流,用来向服务器发送数据。
3. 调用输出流的write方法写数据(写数据,其实就是在向服务器发送数据)。
4. 调用Socket对象的getInputStream方法,获取到一个输入流,用来读取服务器发送过来的数据。
5. 调用输入流的read方法,读取数据(接收数据)
6. 释放资源

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;


public class Demo01Client {
    public static void main(String[] args) throws IOException {
        //创建一个客户端Socket对象,并且在构造方法中传递要连接服务器的ip地址以及端口号。
        //创建Socket对象的时候会去和服务器建立连接。如果连接不上就会抛出异常。
        //因为TCP协议是可靠的协议,是面向连接的协议,所以必须要建立连接才能进行其他操作。
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 10010);

        //调用Socket对象的getOutputStream方法,获取到一个输出流,用来向服务器发送数据。
        OutputStream out = socket.getOutputStream();//获取到的这个输出流目的地是服务器,是用来往服务器写数据的。
        //调用输出流的write方法写数据(写数据,其实就是在向服务器发送数据)
        out.write("你好".getBytes());//把数据写给了服务器。

        //调用Socket对象的getInputStream方法,获取到一个输入流,用来读取服务器发送过来的数据。
        InputStream in = socket.getInputStream();
        //调用输入流的read方法,读取数据(接收数据)
        byte[] bArr = new byte[1024];
        int len = in.read(bArr);
        System.out.println("客户端接收到数据:" + new String(bArr, 0, len));

        socket.close();

    }
}

3.4 TCP的服务器端实现

tcp程序的服务器端。

在java中可以使用一个类表示服务器,这个类叫做ServerSocket

ServerSocket的构造方法
ServerSocket(int port):参数为此服务器程序的端口号。

ServerSocket的其他方法:
Socket accept(): 监听并获取客户端套接字。(等着客户端的请求,并拿到客户端的套接字信息(里面包含的发送 过来的数据))
服务器端的使用步骤:
1. 创建ServerSocket对象,这个对象表示服务器。
2. 调用ServerSocket对象的accept方法,等待获取客户端的请求,并得到客户端的套接字对象Socket
3. 调用客户端Socket对象的getInputStream用来读取客户端发过来的信息
4. 调用read方法,读取客户端发过来的数据。
5. 调用客户端Socket对象的getOutputStream,用来发送
6. 调用write把数据发给客户端。
7. 释放资源。

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Demo02Server {
    public static void main(String[] args) throws IOException {
        //创建ServerSocket对象,这个对象表示服务器。
        ServerSocket serverSocket = new ServerSocket(10010);
        //调用ServerSocket对象的accept方法,等待获取客户端的请求,并得到客户端的套接字对象Socket
        Socket socket = serverSocket.accept();
        //调用客户端Socket对象的getInputStream用来读取客户端发过来的信息
        InputStream in = socket.getInputStream();
        //调用read方法,读取客户端发过来的数据。
        byte[] bArr = new byte[1024];
        int len = in.read(bArr);
        System.out.println("服务器端收到消息:" + new String(bArr, 0, len));

        //调用客户端Socket对象的getOutputStream,用来发送
        OutputStream out = socket.getOutputStream();
        //调用write把数据发给客户端。
        out.write("作为服务器的我已经收到了消息,谢谢".getBytes());

        serverSocket.close();
    }
}

3.5 TCP发送接收流程

TCP流程.png

4 文件上传案例

4.1 上传案例流程

上传流程.png

4.2 上传案例客户端的实现

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

/*
    上传案例的客户端。

    就是把自己的文件读取出来,然后写入到服务器就可以了。
    因为文件的字节比较大,所以需要使用循环读写。

    步骤:
        1. 创建客户端Socket对象。参数位置给出要连接服务器的信息。
        2. 通过Socket对象获取到一个输出流,用来向服务器写数据。
        3. 创建一个FileInputStream,用来读取要上传的文件。
        4. 开始读写,每读取一个字节数组,就将读取到的内容写入到目的地(服务器)
        5. 通过Socket对象获取到一个输入流,用来读取服务器返回过来的数据。
        6. 调用read方法读取数据。
        7. 释放资源

 */
public class Demo03UploadClient {
    public static void main(String[] args) throws IOException {
        //创建客户端Socket对象。参数位置给出要连接服务器的信息。
        //第一个参数ip地址也可以是字符串类型。
        Socket socket = new Socket("127.0.0.1", 10000);
        //通过Socket对象获取到一个输出流,用来向服务器写数据。
        OutputStream out = socket.getOutputStream();
        //创建一个FileInputStream,用来读取要上传的文件。
        FileInputStream fis = new FileInputStream("d:\\client\\aa.jpg");
        //开始读写,每读取一个字节数组,就将读取到的内容写入到目的地(服务器)
        byte[] bArr = new byte[1024];
        int len;
        while((len = fis.read(bArr)) != -1) {
            out.write(bArr, 0, len);
        }
        //执行到这里表示把文件中的所有的数据都写给了服务器。
        //就可以告诉给服务器,我不会再给你写东西。
        socket.shutdownOutput();
        //释放输入流
        fis.close();

        //通过Socket对象获取到一个输入流,用来读取服务器返回过来的数据。
        InputStream in = socket.getInputStream();
        //调用read方法读取数据。
        len = in.read(bArr);
        System.out.println("客户端收到消息:" + new String(bArr, 0, len));

        //释放资源
        socket.close();
    }
}

4.3 上传案例服务器端的实现

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;

/*
    上传案例的服务器。
    接收客户端发过来的字节数据,每接收到,就把接收到的这些字节数据写入到自己服务器电脑的文件。

    步骤:
        1. 创建服务器ServerSocket对象,
        2. 监听并获取客户端Socket对象。
        3. 调用客户端Socket对象的getInputStream,得到输入流,用来读取客户端发过来的字节。
        4. 创建FileOutputStream,用来将读取到的数据写入到自己电脑。
        5. 开始读取,每读取到数据,就把读取到的数据写入到电脑中。
        6. 调用客户端的Socket对象的getOutputStream,得到输入流,用来向客户端发消息。
        7. 调用write方法,发送消息。
        8. 释放资源。
 */
public class Demo04UploadServer {
    public static void main(String[] args) throws IOException {
        //创建服务器ServerSocket对象
        ServerSocket serverSocket = new ServerSocket(10000);
        //监听并获取客户端Socket对象。
        Socket socket = serverSocket.accept();
        //调用客户端Socket对象的getInputStream,得到输入流,用来读取客户端发过来的字节。
        InputStream in = socket.getInputStream();
        //创建FileOutputStream,用来将读取到的数据写入到自己电脑。
        //FileOutputStream fos = new FileOutputStream("d:\\server\\aa.jpg");
        //为了保证文件不会被覆盖掉,一般使用一个唯一的名字。
        //FileOutputStream fos = new FileOutputStream("d:\\server\\" + System.currentTimeMillis() + ".jpg");
        //有一个类,可以生成一个唯一的字符串。这个类叫做UUID
        FileOutputStream fos = new FileOutputStream("d:\\server\\" + UUID.randomUUID().toString() + ".jpg");

        //开始读取,每读取到数据,就把读取到的数据写入到电脑中。
        byte[] bArr = new byte[1024];
        int len;
        while((len = in.read(bArr)) != -1) {
            //把读取到的数据写入到本机
            fos.write(bArr, 0, len);
        }
        fos.close();
        //调用客户端的Socket对象的getOutputStream,得到输入流,用来向客户端发消息。
        OutputStream out = socket.getOutputStream();
        //调用方法,写给客户端一些数据
        out.write("上传成功".getBytes());
        //释放资源
        serverSocket.close();

    }
}

5 多线程上传案例的实现

多线程上传分析.png

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/*
    多线程上传服务器端代码。

    即使多次上传,这个服务器也不会停。

    步骤:
        1. 创建服务器Socket对象。
        2. 监听并获取客户端的socket。
        3. 创建一个新的线程,去给这个客户端执行上传操作。

        把第2步和第3步放入死循环,保证服务器停不下了。


 */
public class Demo04UploadConcurrentServer {
    public static void main(String[] args) throws IOException {
        //创建服务器Socket对象。
        ServerSocket serverSocket = new ServerSocket(10000);
        while(true) {
            //监听并获取客户端socket
            Socket socket = serverSocket.accept();
            //创建一个新的线程,去给这个客户端执行上传操作。
            //创建一个Runnable接口实现类对象
            Task task = new Task(socket);
            //开启新线程执行上传任务
            new Thread(task).start();
        }
    }
}
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;

/*
    线程任务类,里面定义了线程要执行的任务。
    线程要执行的任务就是为客户端执行上传的操作
 */
public class Task implements Runnable{

    private Socket socket;

    public Task(Socket socket) {
        this.socket = socket;
    }


    //在run方法中编写上传的操作。
    @Override
    public void run() {
        try {
            //调用客户端Socket对象的getInputStream,得到输入流,用来读取客户端发过来的字节。
            InputStream in = socket.getInputStream();
            //创建FileOutputStream,用来将读取到的数据写入到自己电脑。
            FileOutputStream fos = new FileOutputStream("d:\\server\\" + UUID.randomUUID().toString() + ".jpg");

            //开始读取,每读取到数据,就把读取到的数据写入到电脑中。
            byte[] bArr = new byte[1024];
            int len;
            while((len = in.read(bArr)) != -1) {
                //把读取到的数据写入到本机
                fos.write(bArr, 0, len);
            }
            fos.close();
            //调用客户端的Socket对象的getOutputStream,得到输入流,用来向客户端发消息。
            OutputStream out = socket.getOutputStream();
            //调用方法,写给客户端一些数据
            out.write("上传成功".getBytes());
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

(插播:
1.
this 和super不能出现在静态方法里.
2.查看自己电脑的ip
cmd中 输入ipconfig 查看本机ip
有一个ip表示本机ip 127.0.0.1
localhost这个单词也可以表示本机
3.如何查看自己电脑和指定电脑是否通畅
ping ip地址
4.
端口号一般1-1024被系统占用,尽量不要动,自己定义的时候要设置在这个范围之外(1025-65535)
5.最重要的:
ip:在网络中对于计算机的标识
端口号: 在计算机中对于应用程序的标识。

6.软件的结构:
c/s: 客户端/服务器。 可以把它看成桌面应用程序。 比如迅雷,微信,QQ。
b/s: 浏览器/服务器。 淘宝,京东,世纪佳缘,百合网,珍爱网..