Java-网络编程

网络通信,IP,TCP/UDP,Socket

1 网络通信的要素

  1. 通信双方的地址(IP+端口号)
  2. 网络通信的协议

2 IP:InetAddress

127.0.0.1 : 本机 localhost

java中InetAddress类负责IP地址相关工作
使用InetAddress:

public class Net {
    public static void main(String[] args) throws UnknownHostException {
        InetAddress byName = InetAddress.getByName("127.0.0.1");
        System.out.println(byName);

        // 查询百度IP
        InetAddress byName1 = InetAddress.getByName("www.baidu.com");
        System.out.println(byName1);

        InetAddress byName2 = InetAddress.getByName("localhost");
        System.out.println(byName2);

        InetAddress byName3 = InetAddress.getLocalHost();
        System.out.println(byName3);
    }
}

3 端口

范围:0~65535

  1. 公有端口:0 ~1023
    HTTP:80
    HTTPS:443
    FTP:21
    Telnet:23
  2. 程序注册端口:1024~49151
    Tomcat:8080
    MySQL:3306
    Oracle:1521
  3. 动态端口:49152~65535

4 TCP实现聊天

4.1 客户端

4.2 服务端

5 TCP实现文件上传

项目目录下放置一个文件(图片2)
在这里插入图片描述

5.1 客户端

客户端先读文件,形成一个文件流,然后使用这个文件流向socket的输出流写入,最后关闭资源

public class TcpClient {
    public static void main(String[] args) throws IOException {
        // 创建socket链接
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);

        // 创建输出流
        OutputStream outputStream = socket.getOutputStream();

        // 读取文件
        FileInputStream fileInputStream = new FileInputStream(new File("图片2.png"));

        // 写出文件
        byte[] bytes = new byte[1024];
        int len;
        while((len=fileInputStream.read(bytes)) != -1){
            outputStream.write(bytes, 0, len);
        }

        // 关闭资源
        fileInputStream.close();
        outputStream.close();
        socket.close();
    }
}

5.2 服务端

创建socket后开始监听,获取到客户端后,从socket获取输入流,从输入流中读取数据,读入文件输出流,保存文件。

public class TcpServer {
    public static void main(String[] args) throws IOException {
        // 创建服务
        ServerSocket serverSocket = new ServerSocket(9000);

        // 监听客户端
        Socket accept = serverSocket.accept();

        // 输入流
        InputStream inputStream = accept.getInputStream();

        // 文件输出
        FileOutputStream receive = new FileOutputStream(new File("receive.png"));

        byte[] bytes = new byte[1024];
        int len;
        while((len=inputStream.read(bytes)) != -1){
            receive.write(bytes, 0, len);
        }

        // 关闭资源
        receive.close();
        inputStream.close();
        accept.close();
        serverSocket.close();
    }
}

5.3 优化

需要客户端确认服务端接收成功才能断开连接,服务端应该在接收完毕后通知客户端

5.3.1 服务端

public class TcpServer {
    public static void main(String[] args) throws IOException {
        // 创建服务
        ServerSocket serverSocket = new ServerSocket(9000);

        // 监听客户端
        Socket accept = serverSocket.accept();

        // 输入流
        InputStream inputStream = accept.getInputStream();

        // 文件输出
        FileOutputStream receive = new FileOutputStream(new File("receive.png"));

        byte[] bytes = new byte[1024];
        int len;
        while((len=inputStream.read(bytes)) != -1){
            receive.write(bytes, 0, len);
        }

        // 通知客户端
        OutputStream outputStream = accept.getOutputStream();
        outputStream.write("received".getBytes());


        // 关闭资源
        receive.close();
        inputStream.close();
        accept.close();
        serverSocket.close();
    }
}

5.3.2 客户端

新增:等待服务器通知

public class TcpClient {
    public static void main(String[] args) throws IOException {
        // 创建socket链接
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);

        // 创建输出流
        OutputStream outputStream = socket.getOutputStream();

        // 读取文件
        FileInputStream fileInputStream = new FileInputStream(new File("图片2.png"));

        // 写出文件
        byte[] bytes = new byte[1024];
        int len;
        while((len=fileInputStream.read(bytes)) != -1){
            outputStream.write(bytes, 0, len);
        }

        // 等待服务端通知
        InputStream inputStream = socket.getInputStream();
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] bytes1 = new byte[1024];
        while((len=inputStream.read(bytes1)) != -1){
            byteArrayOutputStream.write(bytes1, 0, len);
        }

        System.out.println(byteArrayOutputStream.toString());

        // 关闭资源
        fileInputStream.close();
        outputStream.close();
        socket.close();
    }
}

但此时,客户端与服务端均保持运行,卡死

5.4 进一步优化

分析:客户端发送完毕后,立马等待接收,服务端一直在等待接收客户端文件,但服务端不清楚什么时候客户端发送完毕,所以,需要客户端告诉服务端发送文件完毕。
socket.shutdownOutput(),表示客户端输出完毕
客户端代码:

public class TcpClient {
    public static void main(String[] args) throws IOException {
        // 创建socket链接
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);

        // 创建输出流
        OutputStream outputStream = socket.getOutputStream();

        // 读取文件
        FileInputStream fileInputStream = new FileInputStream(new File("图片2.png"));

        // 写出文件
        byte[] bytes = new byte[1024];
        int len;
        while((len=fileInputStream.read(bytes)) != -1){
            outputStream.write(bytes, 0, len);
        }

        // 通知服务端,文件发送完毕
        socket.shutdownOutput();

        // 等待服务端通知
        InputStream inputStream = socket.getInputStream();
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] bytes1 = new byte[1024];
        while((len=inputStream.read(bytes1)) != -1){
            byteArrayOutputStream.write(bytes1, 0, len);
        }

        System.out.println(byteArrayOutputStream.toString());

        // 关闭资源
        fileInputStream.close();
        outputStream.close();
        socket.close();
    }
}

6 Tomcat

未来的服务器将使用tomcat不需要自己手动编写服务端

6.1 Tomcat启动

Apache Tomcat寻找对应java版本的Tomcat压缩包解压缩后,去bin目录下找startup.bat文件,运行,启动服务,浏览器访问localhost:8080即可访问tomcat首页

7 UDP

7.1 服务端

public class UdpServer {
    public static void main(String[] args) throws IOException {
        // 开放端口
        DatagramSocket datagramSocket = new DatagramSocket(9090);

        // 接收数据
        byte[] bytes = new byte[1024];
        DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length);

        // 阻塞接收信息
        datagramSocket.receive(datagramPacket);

        // 打印数据
        System.out.println(new String(datagramPacket.getData(), 0, datagramPacket.getLength()));

        //关闭资源
        datagramSocket.close();
    }
}

7.2 客户端

public class UdpClient {
    public static void main(String[] args) throws IOException {
        // 建立socket
        DatagramSocket datagramSocket = new DatagramSocket();

        // 建立一个包
        String msg = "Hi, Server";
        InetAddress localhost = InetAddress.getByName("localhost");
        int port = 9090;
        DatagramPacket datagramPacket = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, localhost, port);

        // 发送数据包
        datagramSocket.send(datagramPacket);

        // 关闭资源
        datagramSocket.close();

    }
}

8 UDP聊天

8.1 服务端

public class UdpChatReceiver {
    public static void main(String[] args) throws IOException {
        DatagramSocket datagramSocket = new DatagramSocket(6666);
        // 准备接收数据
        byte[] bytes = new byte[1024];
        DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length);
        datagramSocket.receive(datagramPacket);

        byte[] data = datagramPacket.getData();
        String s = new String(data, 0, data.length);
        System.out.println(s);
        datagramSocket.close();
    }
}

8.2 客户端

public class UdpChatSender {
    public static void main(String[] args) throws IOException {
        DatagramSocket datagramSocket = new DatagramSocket(8888);

        // 准备数据
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        String s = bufferedReader.readLine();
        byte[] bytes = s.getBytes();
        DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length, new InetSocketAddress("localhost", 6666));
        datagramSocket.send(datagramPacket);
        
        datagramSocket.close();
    }
}

8.3 循环接收-服务端

public class UdpChatReceiver {
    public static void main(String[] args) throws IOException {
        DatagramSocket datagramSocket = new DatagramSocket(6666);

        while (true){
            // 准备接收数据
            byte[] bytes = new byte[1024];
            DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length);
            datagramSocket.receive(datagramPacket);

            byte[] data = datagramPacket.getData();
            String s = new String(data, 0, data.length);
            System.out.println(s);

            if(s == "bye"){
                break;
            }

        }
        datagramSocket.close();


    }
}

8.4 循环发送-客户端

public class UdpChatSender {
    public static void main(String[] args) throws IOException {
        DatagramSocket datagramSocket = new DatagramSocket(8888);

        while(true){
            // 准备数据
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            String s = bufferedReader.readLine();
            byte[] bytes = s.getBytes();
            DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length, new InetSocketAddress("localhost", 6666));

            datagramSocket.send(datagramPacket);

            if(s == "bye"){
                break;
            }
        }

        datagramSocket.close();
    }
}

9 多线程实现多人UDP聊天

分析:聊天就是一边可以发送同时还能接收,即使用多线程来实现,一个线程负责发送,另一个线程负责接收

9.1 接收数据服务代码

public class TalkReceive implements Runnable{
    private DatagramSocket datagramSocket = null;
    private int fromPort;

    public TalkReceive(int fromPort) throws SocketException {
        this.fromPort = fromPort;
        this.datagramSocket = new DatagramSocket(fromPort);
    }

    @Override
    public void run() {

        while (true){
            // 准备接收数据
            byte[] bytes = new byte[1024];
            DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length);
            try {
                datagramSocket.receive(datagramPacket);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            byte[] data = datagramPacket.getData();
            String s = new String(data, 0, data.length);
            System.out.println("From:" + datagramPacket.getSocketAddress().toString() + ":" + s);

            if(s == "bye"){
                break;
            }

        }
        datagramSocket.close();
    }

9.2 发送数据服务代码

public class TalkSend implements Runnable{
    private DatagramSocket datagramSocket = null;
    private BufferedReader bufferedReader = null;
    private String toIP;
    private int toPort;
    private int fromPort;

    public TalkSend(String toIP, int toPort, int fromPort) throws SocketException {
        this.toIP = toIP;
        this.toPort = toPort;
        this.fromPort = fromPort;
        datagramSocket = new DatagramSocket(this.fromPort);
        bufferedReader = new BufferedReader(new InputStreamReader(System.in));
    }


    @Override
    public void run() {

        while(true){
            // 准备数据
            String s = null;
            try {
                s = bufferedReader.readLine();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            byte[] bytes = s.getBytes();
            DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length, new InetSocketAddress(this.toIP, this.toPort));

            try {
                this.datagramSocket.send(datagramPacket);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            if(s == "bye"){
                break;
            }
        }

        datagramSocket.close();
    }
}

9.3 学生端

学生端需要实现发送信息和接收信息两个功能,即开启两个线程

public class TalkStudent {
    public static void main(String[] args) throws SocketException {
        // 开两个线程
        new Thread(new TalkSend("localhost", 7777, 8888)).start();
        new Thread(new TalkReceive(8889)).start();

    }
}

9.4 教师端

教师端开启两个功能

public class TalkTeacher {
    public static void main(String[] args) throws SocketException {
        new Thread(new TalkSend("localhost", 8889, 7776)).start();
        new Thread(new TalkReceive(7777)).start();
    }
}

双方端口对齐后双方就可以通信

10 URL

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

测试URL下载
开启tomcat,在tomcat服务器上开设自己的项目与文件
在这里插入图片描述
此时,可通过浏览器访问该文件
在这里插入图片描述
使用javaURL类实现从服务器下载文件资源

public class Main {
    public static void main(String[] args) throws IOException {
        URL url = new URL("http://localhost:8080/helloworld/test.txt");

        // 连接到这个资源
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

        InputStream inputStream = urlConnection.getInputStream();

        FileOutputStream fileOutputStream = new FileOutputStream("test.txt");

        byte[] bytes = new byte[1024];
        int len;
        while((len=inputStream.read(bytes)) != -1){
            fileOutputStream.write(bytes, 0, len);
        }
        fileOutputStream.close();
        inputStream.close();
        urlConnection.disconnect();
    }
}

在这里插入图片描述

爬取真实网络文件

在网易云上获取到歌曲的url链接
https://m701.music.126.net/20240311213604/a6f286f1de5cbf2ae6b1416d91bd6533/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/34215387885/14a1/817a/88ad/1916048c71eded9536dac87783af9977.m4a
运行程序,获取到.m4a文件即可播放

public class Main {
    public static void main(String[] args) throws IOException {
        URL url = new URL("https://m701.music.126.net/20240311213604/a6f286f1de5cbf2ae6b1416d91bd6533/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/34215387885/14a1/817a/88ad/1916048c71eded9536dac87783af9977.m4a");

        // 连接到这个资源
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

        InputStream inputStream = urlConnection.getInputStream();

        FileOutputStream fileOutputStream = new FileOutputStream("7.m4a");

        byte[] bytes = new byte[1024];
        int len;
        while((len=inputStream.read(bytes)) != -1){
            fileOutputStream.write(bytes, 0, len);
        }
        fileOutputStream.close();
        inputStream.close();
        urlConnection.disconnect();
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值