Java网络编程

 

Java网络编程

网络编程相关概念

:可以理解成信件 在运输过程中可能会丢包

TCP连接:打电话 –连接– 需要被接通才能通话

UDP连接:发短信 –发送就完事 –不管接收

计算机网络:计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路和通信设备连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。

网络编程目的:数据交换,通信

想要达到这个效果需要什么:

  1. 如何准确定位网络上的一台主机? ip: 端口,定位到计算机上某个资源
  2. 找到主机如何传输数据?

与网页编程区别:

JavaWeb: 网页编程 B/S架构

网络编程:TCP/IP C/S架构

网络通信的要素

如何实现网络通信?

通信双方地址:

  • ip
  • 端口号
  • 192.168.0.1:8022

规则:网络通信的协议

TCP/IP参考模型

模型

OSI:开放式系统互联通信参考模型(英语:Open System Interconnection Reference Model,缩写为 OSI),简称为OSI模型(OSI model),一种概念模型,由国际标准化组织提出,一个试图使各种计算机在世界范围内互连为网络的标准框架。定义于ISO/IEC 7498-1。

TCP:传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流传输层通信协议,由IETFRFC 793 [1] 定义。

本文网络编程重点针对传输层

小结:

  1. 网络编程中两个主要的问题
    • 如何准确定位到网络上的一台或多台主机
    • 找到主机后如何进行通讯
  2. 网络编程中的要素
    • IP和端口号
    • 网络通信协议
  3. 万物皆对象(java)

IP

ip地址:InetAddress类

  • 唯一定位一台网络计算机

  • 127.0.0.1 localhost 本机回环地址

  • ip分类方式

    • ipv4/ipv6

      • ipv4 127.0.0.1,四个字节组成,42亿个:30亿在北美,亚洲4亿,所以国内申请域名服务器很费劲,2011年用尽

      • ipv6 128位,8个无符号整数表示

        	2001:3CA1:010F:001A:121B:0000:0000:0010
        
    • 公网(互联网)/私网(局域网)

      • ABCD类,见图片ipABCD类

      • 192.168.xx.xx ,专门给组织内部使用

  • 域名:用于解决记忆ip的问题

  • InetAddress类使用

    package com.monki.javanet;
      
    import java.net.InetAddress;
    import java.net.StandardSocketOptions;
    import java.net.UnknownHostException;
      
    //测试ip
    public class InetAddressTest {
        public static void main(String[] args) {
            //构造方法'InetAddress()' is not public in 'java.net.InetAddress'. Cannot be accessed from outside package
            //不能使用 new InetAddress()创建对象;
            try {
                //查询本机地址
                InetAddress inetAddress1 = InetAddress.getByName("127.0.0.1");
                InetAddress inetAddress2 = InetAddress.getByName("localhost");
                InetAddress inetAddress3 = InetAddress.getLocalHost();
      
                //查询网站ip地址
                InetAddress inetAddress4 = InetAddress.getByName("www.baidu.com");
                System.out.println(inetAddress1);///127.0.0.1
                System.out.println(inetAddress2);//localhost/127.0.0.1
                System.out.println(inetAddress3);//LAPTOP-G68G50C5/192.168.0.6
                System.out.println(inetAddress4);//www.baidu.com/112.80.248.75
      
                //常用方法
                System.out.println("-------------------------------------------------");
                //Returns the raw IP address of this InetAddress object. The result is in network byte order: the highest order byte of the address is in getAddress()[0].
                System.out.println(inetAddress4.getAddress());//[B@1b6d3586
                //the fully qualified domain name for this IP address, or if the operation is not allowed by the security check, the textual representation of the IP address.
                System.out.println(inetAddress4.getCanonicalHostName());//112.80.248.75
                  
                //the raw IP address in a string format.
                System.out.println(inetAddress4.getHostAddress());//112.80.248.75
                //the host name for this IP address, or if the operation is not allowed by the security check, the textual representation of the IP address.
                System.out.println(inetAddress4.getHostName());//www.baidu.com
      
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
        }
    }
    
    canonical
      
    美 [kəˈnɑnɪk(ə)l]
      
    英 [kəˈnɒnɪk(ə)l]
      
    - adj.被收入真经篇目的;经典的;按照基督教教会法规的
    - n.(布道时应穿的)法衣
    - 网络.标准;规范的;典型
    
    2023-7-20 编辑所有内容至此
    

端口

端口表示计算机上一个程序的进程;

  • 不同的进程有不同的端口号!用来区分软件!

  • 被规定0-65535(tcp/udp各65536)

  • TCP/UDP: 65535*2 可以tcp用80udp用80,但单个协议下端口号不能冲突

  • 端口分类

    • 公有端口 0~1023(尽量不去使用)

      • HTTP:80
      • HTTPS:443
      • FTP:21
      • SSH:22
      • Telent(远程监听):23
    • 程序注册端口: 1024~49151,分配给用户或程序

      • Tomcat: 8080
      • MySQL: 3306
      • Oracle: 1521
      • Termux :8022
    • 动态 私有: 49152~65536

      netstat -ano #查看所有端口
      netstat -ano|findstr "5900" #查看指定的端口
      tasklist|findstr "8696" #查看指定端口进程
      Ctrl+shift+ESC #打开任务管理器查看进程id(PID)
      

      PID与端口号区别

      • InetSocketAddress类使用
      package com.monki.javanet;
          
      import java.net.InetSocketAddress;
          
      public class InetSocketAddressTest {
          public static void main(String[] args) {
              InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 80_80);
              InetSocketAddress socketAddress2 = new InetSocketAddress("localhost", 80_80);
              System.out.println(socketAddress);
              System.out.println(socketAddress2);
          
              //manner
              System.out.println(socketAddress.getAddress());
              System.out.println(socketAddress.getHostName());
              System.out.println(socketAddress.getPort());
              /*
              /127.0.0.1:8080
              localhost/127.0.0.1:8080
                      /127.0.0.1
              127.0.0.1
              8080
              */
          }
      }
          
      

通信协议

协议:一种约定

网络通信协议: 速率,传播码率,代码结构,传输控制…

问题:复杂

所以要分层!

TCP/IP协议簇:实际上是一组协议

重要:

  • TCP: 用户传输协议
  • UDP:用户传输报协议

出名的协议:

  • TCP:
  • IP:网络互联协议

TCP UDP对比:

TCP:打电话

  • 连接,稳定

  • 三次握手 四次挥手

    最少需要三次,保证稳定连接!
    A:你瞅啥?
    B:瞅你咋地?
    A:干一场!
      
      
    A:我要走了
    B:你真的要走了吗?
    B:你真的真的要走了吗?
    A:我真的要走了
    
  • 客户端&服务端
  • 传输完成,释放连接,效率低

UDP:发短信

  • 不连接,不稳定
  • 客户端&服务端:没有明确界限
  • 不管有没有准备号,都可以发给你
  • 导弹
  • DDOS:洪水工具!(饱和攻击)

TCP发送消息

客户端

  1. 连接服务器
  2. 发送消息(io流)
package com.monki.javanet;

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

public class TcpClientDemo1 {

    private static Socket socket;
    private static OutputStream os;

    public static void main(String[] args) {
        try {
            //1.要知道服务器地址
            InetAddress serverIP = InetAddress.getByName("127.0.0.1");
            int port = 9999;
            //2.创建一个socket连接
            socket = new Socket(serverIP,port);
            //3.发送消息 IO流
            os = socket.getOutputStream();

            os.write("梦奇的客户端给服务器发送了一条消息".getBytes());

        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            if(os!=null){
                try {
                    os.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
                if(socket!=null){
                    try {
                        socket.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }}
        }

    }
}

服务端

  1. 建立服务的端口 ServerSocket类
  2. 等待用户的连接 accept
  3. 接收用户的消息(io流)
package com.monki.javanet;

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

public class TcpServerDemo1 {

    private static ServerSocket serverSocket;
    private static Socket accept;
    private static InputStream is;
    private static ByteArrayOutputStream baos;

    public static void main(String[] args) {
        try {
            //1.我得有一个地址
            serverSocket = new ServerSocket(9999);
            while (true){//循环监听
                //2.等待客户端连接过来
                accept = serverSocket.accept();
                //3.读取客户端消息
                is = accept.getInputStream();
                //管道流
                baos = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len;
                while((len= is.read(buffer))!=-1){
				//is.read()方法
                /*
                    Reads some number of bytes from the input stream and 
                    stores them into the buffer array b. 
                    The number of bytes actually read is returned as an integer. 
                */
                    baos.write(buffer,0,len);
                    //baos.write方法
                    /*
                    Writes len bytes from the specified byte array
                    starting at offset off to this byte array output stream.
                     */

                }
                System.out.println(baos.toString());
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //close
            if(baos!=null){
                try {
                    baos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if(is!=null){
                try {
                    is.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if(accept!=null){
                try {
                    accept.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if(serverSocket!=null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

        }
    }
}
socket
美 [ˈsɑkət]
英 [ˈsɒkɪt]
n.插座;灯座;承口;【解】眼窝
v.把…插入插痤
网络套接字;插口;套接口

socket (简称 套接字) 是 进程之间通信一个工具,好比现实生活中的 插 座,所有的家用电器要想工作都是基于插座进行, 进程之间想要进行网络通信需要基于这个 socket

socket

TCP文件上传

服务器端

package com.monki.javanet;

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

public class TcpServerDemo2 {
    public static void main(String[] args) throws Exception {
        //1.创建服务
        ServerSocket serverSocket = new ServerSocket(9000);
        //2.监听客户端的连接
        Socket accept = serverSocket.accept();//阻塞式监听,会一直等待客户端连接
        //3.获取输入流
        InputStream is = accept.getInputStream();

        //4.文件输出
        FileOutputStream fos = new FileOutputStream(new File("receive.jpg"));
        byte[] buffer = new byte[1024];
        int len;
        while ((len = is.read(buffer))!=-1){
            fos.write(buffer,0,len);
        }


        //通知客户端我接收完毕了
        OutputStream os = accept.getOutputStream();
        os.write("我接收完毕了,你可以断开了".getBytes());
        //关闭资源
        os.close();
        fos.close();
        is.close();
        accept.close();
        serverSocket.close();
    }
}

客户端

package com.monki.javanet;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

public class TcpClientDemo2 {
    public static void main(String[] args) throws Exception {//这里直接抛出异常,需要在外层处理
        //1.创建一个socket连接
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);
        //2.创建一个输出流
        OutputStream os = socket.getOutputStream();
        //3.读取文件
        FileInputStream fis = new FileInputStream(new File("monki.png"));
        //4.传出文件
        byte[] buffer = new byte[1024];
        int len;
        while((len = fis.read(buffer))!=-1){
            os.write(buffer,0,len);
        }

        //通知服务器,我已经结束,不然会死锁?
        socket.shutdownOutput();//我已经传输完了

        //确定服务器吗接收完毕,才能断开连接
        InputStream is = socket.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer2 = new byte[1024];
        int len2;
        while((len2=is.read(buffer2))!=-1){
            baos.write(buffer2,0,len2);
        }
        System.out.println(baos);
        //5.关闭资源
        baos.close();
        is.close();
        fis.close();
        os.close();
        socket.close();
    }
}

Tomcat

Tomcat的安装配置 - Monki’s blog (monkifantasy.github.io)

服务端

  • 自定义 S
  • Tomcat服务器 S :Java后台开发!

客户端

  • 自定义 C
  • 浏览器

体会:启动自定义服务端使用8080端口,同时启动Tomcat体会端口占用

启动tomcat后无法再使用8080端口

UDP

发短信,不需要知道对方地址


发送消息

发送端

package com.monki.javanet;

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

//不需要连接服务器
public class UdpClientDemo1 {
    public static void main(String[] args) throws Exception {

        //1.建立一个Socket
        DatagramSocket socket = new DatagramSocket();

        //2.建包
        String message = "你好啊,服务器!";
        InetAddress localhost = InetAddress.getByName("localhost");
        int port =9090;

        //数据,数据长度,发送给谁
        DatagramPacket packet = new DatagramPacket(message.getBytes(), 0, message.getBytes().length, localhost, port);

        //3.发送包
        socket.send(packet);

        //4.关闭流
        socket.close();
    }
}

接收端(不是服务端)

package com.monki.javanet;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

//还是要等待客户端连接
public class UDPServerDemo1 {
    public static void main(String[] args) throws Exception {
        //开放端口
        DatagramSocket socket = new DatagramSocket(9090);

        //接收数据包
        byte[] buffer= new byte[1024];
        DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);

        socket.receive(packet);//阻塞接收

        System.out.println(packet.getAddress().getHostAddress());
        System.out.println(new String(packet.getData(),0, packet.getLength()));

        //关闭连接
        socket.close();
    }
}

咨询

循环发送消息

package com.monki.javanet.chat;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;

public class UdpSenderDemo1 {

    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(8888);

        //准备数据: 控制流读取 System.in
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while (true){

            String data = reader.readLine();
            byte[] datas = data.getBytes();
            DatagramPacket packet = new DatagramPacket(datas,0,datas.length,new InetSocketAddress("localhost",6666));
            //发送包
            socket.send(packet);
            if(data.equals("bye")){
                break;
            }
        }
        //关闭资源
        socket.close();

    }
}

循环接收消息

package com.monki.javanet.chat;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Socket;
import java.net.SocketException;

public class UdpReceiveDemo1 {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(6666);

        while(true){

            //准备接收包裹
            byte[] container = new byte[1024];

            DatagramPacket packet= new DatagramPacket(container,0,container.length);
            socket.receive(packet);//阻塞式接收包裹
            //断开连接 bye
            byte[] data = packet.getData();
            String receiveData = new String(data, 0, data.length).trim();//这里去除多余的空格
            receiveData =  new String(data, 0, packet.getLength());//或者这样去除空格
            //getLength() Returns the length of the data to be sent or the length of the data received.

            System.out.println(receiveData);//打印数据

            if(receiveData.equals("bye")){
                break;
            }

        }

        socket.close();
    }
}

在线咨询:两个人都可以是发送方,也可以是接收方!

2023-7-21 编辑所有内容至此

发送类

package com.monki.javanet.chat;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;

public class TalkSend implements Runnable {

    DatagramSocket socket;
    BufferedReader reader;
    private int fromPort;
    private String toIp;
    private int toPort;

    public TalkSend(int fromPort, String toIp, int toPort) {
        this.fromPort = fromPort;
        this.toIp = toIp;
        this.toPort = toPort;
        try{

            socket = new DatagramSocket(fromPort);

            reader = new BufferedReader(new InputStreamReader(System.in));
        }catch (Exception e){
            e.printStackTrace();
        }finally {

        }
    }

    @Override
    public void run() {



        //准备数据: 控制流读取 System.in
        while (true){
            try{
                String data = reader.readLine();
                byte[] datas = data.getBytes();
                DatagramPacket packet = new DatagramPacket(datas,0,datas.length,new InetSocketAddress(this.toIp,this.toPort));
                //发送包
                socket.send(packet);
                if(data.equals("bye")){
                    break;
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        //关闭资源
        socket.close();
    }


}

接收类

package com.monki.javanet.chat;

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

public class TalkReceive implements Runnable{

    DatagramSocket socket;
    private int port;
    private String msgFrom;

    public TalkReceive(int port,String msgFrom) {
        this.port = port;
        this.msgFrom = msgFrom;
        try {
            socket = new DatagramSocket(port);
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {

        while(true){


            try {
                //准备接收包裹
                byte[] container = new byte[1024];
                DatagramPacket packet= new DatagramPacket(container,0,container.length);
                socket.receive(packet);//阻塞式接收包裹
                //断开连接 bye
                byte[] data = packet.getData();
                String receiveData = new String(data, 0, data.length).trim();//这里去除多余的空格
                receiveData =  new String(data, 0, packet.getLength());//或者这样去除空格
                //getLength() Returns the length of the data to be sent or the length of the data received.

                System.out.println(msgFrom+":"+receiveData);//打印数据

                if(receiveData.equals("bye")){
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        socket.close();
    }
}

学生与老师聊天

学生端

package com.monki.javanet.chat;

public class TackStudent {
    public static void main(String[] args) {
        //开启两个线程
        new Thread(new TalkSend(6666,"localhost",9999)).start();
        new Thread(new TalkReceive(7777,"老师")).start();
    }
}

老师端

package com.monki.javanet.chat;

public class TalkTeacher {
    public static void main(String[] args) {
        new Thread(new TalkSend(8888,"localhost",7777)).start();
        new Thread(new TalkReceive(9999,"学生")).start();
    }
}

学生端与老师端要都说bye才能结束聊天

URL

https://www.baidu.com

统一资源定位符:定位互联网上的某一资源

DNS域名解析: www.baidu.com -> x.x.x.x(ip地址)


协议://ip地址:端口/项目名/资源

URL类方法

package com.monki.javanet;

import java.net.MalformedURLException;
import java.net.URL;

public class URLDemo1 {
    public static void main(String[] args) throws MalformedURLException {
        URL url = new URL("http://localhost:8080/helloworld/index.jsp?username=monki&password=monki");
        System.out.println(url.getProtocol());//协议
        System.out.println(url.getHost());//主机ip
        System.out.println(url.getPort());//端口
        System.out.println(url.getPath());//文件
        System.out.println(url.getFile());//全路径
        System.out.println(url.getQuery());//参数
    }
}

URL下载

package com.monki.javanet;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;


public class UrlDownload {
    public static void main(String[] args) throws Exception {
        //1.下载地址
        URL url = new URL("http://localhost:8080/monki/test.txt");
        //2.连接到这个资源
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

        InputStream is = urlConnection.getInputStream();
        FileOutputStream fos = new FileOutputStream("monki.txt");
        byte[] buffer = new byte[1024];
        int len;
        while ((len=is.read(buffer))!=-1){
            fos.write(buffer,0,len);
        }
        fos.close();
        is.close();
        urlConnection.disconnect();

    }
}
2023-07-22 本文到此完结