Java 基础加强 01

基础加强·网络编程 和 GUI

网络编程概述

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

* B:网络编程
  * 就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换

 

IP地址概述

* 每个设备在网络中的唯一标识
* 每台网络终端在网络中都有一个独立的地址,我们在网络中传输数据就是使用这个地址

* ipconfig :查看本机 IP
* ping :测试连接

* 本地回路地址 :127.0.0.1 
* 广播地址 :255.255.255.255

* IPv4 :4个字节组成,4个0到255的数,大概42亿,30亿在北美,亚洲4亿,2011年初已经用尽
* IPv6 :8组,每组4个16进制数

 

端口号概述

* 每个程序在设备上的唯一标识
* 每个网络程序都需要绑定一个端口号,传输数据的时候除了确定发到哪台机器上,还要明确发到哪个程序

* 端口号范围从 0 - 65535
* 编写网络应用就需要绑定一个端口号,尽量使用1024以上的,1024以下的基本上都被系统程序占用了

* 常用端口
  * MySQL :3306
  * Oracle :1521
  * web :80
  * tomcat :8080
  * QQ :4000
  * feiQ :2425

 

网络协议概述

* 为计算机网络进行数据交换而建立的规则

* UDP
  * 面向无连接,数据不安全,速度快,不区分客户端与服务端

* TCP
  * 面向连接(三次握手),数据安全,速度略低,分为客户端和服务端
  * 三次握手:客户端先向服务端发起请求,服务端响应请求,传输数据

 

Socket通信原理

* A:Socket套接字概述
  * 网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字
  * 通信的两端都要Socket
  * 网络通信其实就是Socket间的通信
  * 数据在两个Socket间通过 I/O流传输
  * Socket在应用程序中创建,通过一种绑定机制与驱动程序建立关系,告诉自己所对应的 IP和端口号

 

 

UDP传输

* A:发送,Send
  * 创建 DatagramSocket,随机端口号
  * 创建 DatagramPacket,指定数据,长度,地址,端口
  * 使用 DatagramSocket,发送 DatagramPacket
  * 关闭 DatagramSocket

* B:接收,Receive
  * 创建 DatagramSocket,指定端口号
  * 创建 DatagramPacket,是定数组,长度
  * 使用 DatagramSocket,接收 DatagramPacket
  * 关闭 DatagramSocket
  * 从 DatagramPacket中获取数据

* C:接收方获取 ip和端口号
  * String ip = packet.getAddress().getHostAddress();
  * int port = packet.getPort()

package com.heima.socket;

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

public class Demo1_Send {

    public static void main(String[] args) throws IOException {
        String str = "What are you doing?"; // 待发送的数据
        
        DatagramSocket socket = new DatagramSocket(); // 创建socket,相当于创建码头,可以不指定端口号,随机绑定
        DatagramPacket packet = // 创建packet,相当于创建集装箱,用于存储数据报文
                new DatagramPacket(str.getBytes(), str.length(), InetAddress.getByName("192.168.0.101"), 61666);
                                //  字节数组                           数组的有效长度                  主机名                                                                                  端口号
        
        socket.send(packet); // 发送数据,相当于发货
        socket.close(); // 关流,相当于关闭码头
    }
}
send
package com.heima.socket;

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


public class Demo1_Receive {

    public static void main(String[] args) throws IOException {
        DatagramSocket socket = new DatagramSocket(61666); // 创建Socket,绑定端口号
        DatagramPacket packet = new DatagramPacket(new byte[1024], 1024); // 创建Packet,指定接收数组及其长度
        socket.receive(packet); // 接收数据

        byte[] arr = packet.getData(); // 用数组获取数据
        int len = packet.getLength(); // 获取有效的字节个数
        System.out.println(new String(arr,0,len)); // 调用 String的构造方法将数组转化为字符串
        socket.close();
    }
}
receive

 

UDP传输优化

* 发送端 Send ,发送键盘录入数据

* 接收端 Receive,接收并打印

package com.heima.socket;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

public class Demo2_Send {

    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in); // 创建键盘录入对象
        DatagramSocket socket = new DatagramSocket(); // 创建socket

        while (true) {
            String str = sc.nextLine(); // 获取键盘录入的字符串
            
            if ("quit".equals(str)) {
                sc.close(); // 判断字符串并退出循环
                break;
            }
            
            DatagramPacket packet = // 创建packet,将录入的字符串转换为字节数组并打包
                    new DatagramPacket(str.getBytes(), str.length(), InetAddress.getByName("192.168.0.101"), 61666);
            socket.send(packet); // 发送数据
        }
        
        socket.close(); // 关流
    }
}
send
package com.heima.socket;

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

public class Demo2_Receive {

    public static void main(String[] args) throws IOException {
        DatagramSocket socket = new DatagramSocket(61666); // 创建Socket,绑定指定的端口号
        DatagramPacket packet = new DatagramPacket(new byte[1024], 1024); // 创建Packet,指定用来接收的字节数组及其长度

        while (true) { // 定义无限循环
            socket.receive(packet); // 接收数据
            
            byte[] arr = packet.getData(); // 获取数据
            int len = packet.getLength(); // 获取有效的字节个数
            
            String ip = packet.getAddress().getHostAddress(); // 获取ip地址
            int port = packet.getPort(); // 获取端口号
            
            System.out.println(ip + " : " + port + " : " + new String(arr, 0, len)); // 打印数据
        }

    }
}
receive

 

多线程的UDP传输

* 发送和接受在一个窗口内完成

package com.heima.socket;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

public class Demo3_MoreThread {

    public static void main(String[] args) {
        new Receive().start(); // 开启接受线程
        new Send().start(); // 开启发送线程
    }
}

class Receive extends Thread {  // 定义接受类继承Thread
    public void run() { // 重写 run()方法
        try {
            DatagramSocket socket = new DatagramSocket(61666); // 创建Socket,绑定端口号
            DatagramPacket packet = new DatagramPacket(new byte[1024], 1024); // 创建Packet

            while (true) {
                socket.receive(packet); // 接受数据
                byte[] arr = packet.getData(); // 获取数据
                int len = packet.getLength(); // 获取有效的字节个数
                
                String ip = packet.getAddress().getHostAddress(); // 获取ip地址
                int port = packet.getPort(); // 获取端口号
                
                System.out.println(ip + ":" + port + ":" + new String(arr, 0, len));
            }
        } catch (IOException e) { // 选中代码块后 alt + shift + z 一键生成try catch
            e.printStackTrace();
        }
    }
}

class Send extends Thread { // 定义发送类继承Thread
    public void run() { // 重写 run()方法
        try {
            Scanner sc = new Scanner(System.in); // 创建键盘录入对象
            DatagramSocket socket = new DatagramSocket(); // 创建socket

            while (true) { 
                String str = sc.nextLine(); // 键盘录入字符串
                
                if ("quit".equals(str)) {
                    sc.close();
                    break;
                }
                
                DatagramPacket packet = // 创建packet,将字符串转换为字节数组并打包
                        new DatagramPacket(str.getBytes(), str.length(), InetAddress.getByName("192.168.0.101"), 61666);
                socket.send(packet); // 发送数据
            }
            
            socket.close(); // 关流
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
thread

 

GUI制作界面并用UDP实现聊天室

 

package com.heima.socket;

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo4_GUIChat extends Frame {
    // GUI聊天
    private static final long serialVersionUID = 1L;
    private TextField tf;
    private Button send;
    private Button log;
    private Button clear;
    private Button shake;
    private TextArea viewText;
    private TextArea sendText;
    private DatagramSocket socket; 
    private BufferedWriter bw;

    public Demo4_GUIChat() {
        init(); // 初始化
        southPanel(); // 设置南边的Panel
        centerPanel(); // 设置中间的Panel
        event(); // 设置按钮的功能
    }

    public void event() {
        this.addWindowListener(new WindowAdapter() { // 对当前对象添加窗口监听,并传入适配器
            @Override
            public void windowClosing(WindowEvent e) { // 重写窗口关闭的方法,传入窗口事件对象
                try {
                    socket.close(); // 关流
                    bw.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }

                System.exit(0); // 退出程序
            }
        });

        send.addActionListener(new ActionListener() { // 在send按钮上添加动作监听

            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    send(); // 调用发送信息的方法
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }

        });

        log.addActionListener(new ActionListener() { // 在log按钮上添加动作监听

            @Override
            public void actionPerformed(ActionEvent e) {
                LogFile(); // 调用获取聊天记录的方法
            }

        });

        clear.addActionListener(new ActionListener() { // 在clear按钮上添加动作监听

            @Override
            public void actionPerformed(ActionEvent e) {
                viewText.setText(""); // 把显示区清空
            }
        });

        shake.addActionListener(new ActionListener() { // 在shake按钮上添加动作监听

            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    send(new byte[] { -1 }, tf.getText()); // 发送一个元素为-1的字节数组
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                // shake();
            }

        });

        sendText.addKeyListener(new KeyAdapter() { // 为输入区添加键盘监听
            @Override
            public void keyReleased(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ENTER && e.isControlDown()) { // 如果键盘上被按下的键为 ctrl 和 回车 
                    try {
                        send(); // 调用send方法,把信息发出去
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        });
    }

    public void shake() {
        int x = this.getLocation().x; // 获取横坐标位置
        int y = this.getLocation().y; // 获取纵坐标位置

        for (int i = 0; i < 5; i++) {
            try {
                this.setLocation(x + 20, y + 20); // 改变窗体的位置
                Thread.sleep(20); // 使线程睡20毫秒
                this.setLocation(x + 20, y - 20);
                Thread.sleep(20);
                this.setLocation(x - 20, y + 20);
                Thread.sleep(20);
                this.setLocation(x - 20, y - 20);
                Thread.sleep(20);
                this.setLocation(x, y);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

    public void LogFile() { // 获取聊天内容
        try {
            bw.flush(); // 刷新输出流的缓冲区
            FileInputStream fis = new FileInputStream("config.txt"); // 创建字节输入流
            ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 创建内存流读取文件

            int len;
            byte[] arr = new byte[8192]; // 指定数组大小
            while ((len = fis.read(arr)) != -1) { // 读取数据
                baos.write(arr, 0, len); // 将数据写到内存中
            }

            String str = baos.toString(); // 将内存中的内容转换为字符串
            viewText.setText(str); // 将文件中的内容打印到显示区
            fis.close(); // 关闭字节输入流

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void send(byte[] arr, String ip) throws IOException { // 创建重载方法
        DatagramPacket packet = // 将字节数组打包
                new DatagramPacket(arr, arr.length, InetAddress.getByName(ip), 9999);
        socket.send(packet); // 发送数据
    }

    private void send() throws IOException { // 定义发送信息的方法
        String message = sendText.getText(); // 从用于发送的文本区域处获取内容
        String ip = tf.getText(); // 从单行文本组件处获取ip地址
        ip = ip.trim().length() == 0 ? "255.255.255.255" : ip; // 如果是空的,就设置为广播

        send(message.getBytes(), ip); // 调用重载方法发送信息

        String time = getCurrentTime(); // 获取当前的时间
        String str = time + "   我对:" + (ip.equals("255.255.255.255") ? " 所有人 " : ip) + "  说: \r\n" + message + "\r\n\r\n"; // 拼接字符串
        viewText.append(str); // 将发送的信息添加到显示区域中       alt + shift + l 抽取局部变量
        bw.write(str); // 将信息写到数据库中

        sendText.setText(""); // 清空用于发送的文本区域的内容
    }

    private String getCurrentTime() {
        Date d = new Date(); // 创建日期对象
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); // 创建日期格式化对象
        return sdf.format(d); // 将时间格式化并返回
    }

    public void centerPanel() { // 设置中间的Panel
        Panel center = new Panel(); // 创建Panel

        viewText = new TextArea(); // 创建用于显示的多行文本区域
        sendText = new TextArea(5, 1); // 创建用于发送的多行文本区域

        center.setLayout(new BorderLayout()); // 把当前Panel设置为边界布局管理器
        center.add(sendText, BorderLayout.SOUTH); // 把用于发送的文本区域放在南边
        center.add(viewText, BorderLayout.CENTER); // 把用于显示的文本区域放在中间

        viewText.setEditable(false); // 将显示区域设置为不可编辑
        viewText.setBackground(Color.WHITE); // 将显示区域的颜色设置为白色
        viewText.setFont(new Font("xxx", Font.PLAIN, 15)); // 设置显示区域的字体及大小
        sendText.setFont(new Font("xxx", Font.PLAIN, 15)); // 设置发送区域的字体及大小

        this.add(center, BorderLayout.CENTER); // 把当前Panel方法Frame的中间
    }

    public void southPanel() { // 设置南边的Panel
        Panel south = new Panel(); // 创建Panel

        tf = new TextField(15); // 创建单行文本组件并设置大小
        tf.setText("127.0.0.1"); // 设置文本组件的大小,并且初始化为本地回路地址

        send = new Button("send"); // 创建按钮,并为其命名
        log = new Button("log");
        clear = new Button("clear");
        shake = new Button("shake");

        south.add(tf); // 添加文本组件到南边的Panel中
        
        south.add(send); // 添加按钮到南边的Panel中
        south.add(log);
        south.add(clear);
        south.add(shake);

        this.add(south, BorderLayout.SOUTH); // 设置边界布局管理器,并将该Panel放在Frame的南边
    }

    public void init() {
        this.setLocation(500, 50); // 设置位置
        this.setSize(400, 600); // 设置大小

        new Receive().start(); // 开启接受数据的线程

        try {
            socket = new DatagramSocket();
            bw = new BufferedWriter(new FileWriter("config.txt", true)); // 创建内存输出流,关联历史记录文件,注意添加参数true,因为我们需要在尾部追加
        } catch (Exception e) {
            e.printStackTrace();
        }

        this.setVisible(true); // 设置窗体可见
    }

    private class Receive extends Thread { // 接收和发送需要同时执行,因此把数据接收功能定义成多线程的
        public void run() { // 重写run()方法
            try {
                DatagramSocket socket = new DatagramSocket(9999); // 创建socke并指定端口号
                DatagramPacket packet = new DatagramPacket(new byte[8192], 8192); // 指定用于接收数据的字节数组及其大小

                while (true) {
                    socket.receive(packet); // 接收信息
                    byte[] arr = packet.getData(); // 获取信息
                    int len = packet.getLength(); // 获取有效字节数
                    
                    if (arr[0] == -1 && len == 1) { // 如果发过来的数组的第一个存储的值是-1,并且数组长度是1,就调用shake方法
                        shake(); // 调用shake方法
                        continue; // 终止本次循环,继续下一次循环,因为震动后不需要执行下面的代码
                    }

                    String message = new String(arr, 0, len); // 转换成字符串
                    String ip = packet.getAddress().getHostAddress(); // 获取发送方的ip地址
                    String time = getCurrentTime(); // 获取当前时间
                    
                    String str = time + " " + ip + "对我说:\r\n" + message + "\r\n\r\n"; // 拼接字符串
                    viewText.append(str); // 是用于显示的文本区域显示字符串
                    bw.write(str); // 将字符串写入数据库
                    
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    public static void main(String[] args) {
        new Demo4_GUIChat(); // 把所有的代码写在构造方法里
    }

}
ChatGUI

 

 

TCP协议

* A:客户端
  * 创建 Socket 连接服务端(指定 ip 地址,端口号),通过 ip 地址找到对应的服务器
  * 调用 Socket 的 getInputStream()和 getOutputStream() 方法获取和服务端相连的 IO流
  * 输入流可以读取服务端输出流写出的数据
  * 输出流可以写出数据到服务端的输入流

* B:服务端
  * 创建 ServerSocket(指定端口号)
  * 调用 ServerSocket 的 accept()方法接收一个客户端请求,得到一个 Socket
  * 调用 Socket 的 getInputStream()和 getOutputStream()方法获取和客户端相连的 IO流
  * 输入流可以读取客户端输出流写出的数据
  * 输出流可以写出数据到客户端的输入流 

package com.heima.tcp;

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

public class Demo1_Client {

    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1",12345); // 创建socket对象,指定目标ip地址和端口号
        
        InputStream is = socket.getInputStream(); // 获取客户端输入流
        OutputStream os = socket.getOutputStream(); // 获取客户端输出流
        
        byte[] arr = new byte[1024]; // 创建数组存储数据
        int len  = is.read(arr); // 读取服务器发过来的数据
        System.out.println(new String(arr, 0, len)); // 将数据转换从字符串并打印
        
        os.write("学习挖掘机哪家强?".getBytes()); // 客户端向服务器写数据
        socket.close(); // 关流
    }
}
Client
package com.heima.tcp;

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

public class Demo1_Server {

    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(12345); // 创建ServerSocket并指定端口
        Socket socket = ss.accept(); // 接收客户端的请求
        
        InputStream is = socket.getInputStream(); // 获取服务器输入流
        OutputStream os = socket.getOutputStream(); // 获取服务器输出流
        
        os.write("百度一下,你就知道".getBytes()); // 服务器向客户端写出数据
        
        byte[] arr = new byte[1024]; // 定义数组存储数据
        int len  = is.read(arr); // 读取客户端发过来的数据
        System.out.println(new String(arr, 0, len)); // 将数据转换为字符串并打印
        
        socket.close(); // 关流
    }
}
Server

 

 

TCP代码优化

* 客户端

* 服务端

package com.heima.tcp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;

public class Demo2_Client {

    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1",12345); // 创建Socket对象,指定ip地址和端口号
        BufferedReader br = // 创建带缓冲的字符输入流
                new BufferedReader(new InputStreamReader(socket.getInputStream())); // 将字节流包装成了字符流
        PrintStream ps = new PrintStream(socket.getOutputStream()); // PrintStream中有写出换行的方法
        
        System.out.println(br.readLine()); // 整行读取
        ps.println("how are you"); // 通过打印流向服务器输出
        System.out.println(br.readLine());
        
        socket.close(); // 关流
    }
}
Client
package com.heima.tcp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Demo2_Server {

    public static void main(String[] args) throws IOException {
        // demo1(); // 单线程
        // demo2(); // 多线程
    }

    public static void demo2() throws IOException {
        ServerSocket ss = new ServerSocket(12345); // 创建ServerSocket对象,指定端口号
        while (true) {
            Socket socket = ss.accept(); // 接收客户端的请求

            new Thread() { // 创建Thread的匿名子类
                public void run() { // 重写 run()方法
                    try {
                        BufferedReader br = // 创建带缓冲的字符输入流
                                new BufferedReader(new InputStreamReader(socket.getInputStream())); // 将字节流包装成了字符流
                        PrintStream ps = new PrintStream(socket.getOutputStream()); // PrintStream中有写出换行的方法

                        ps.println("hello"); // 调用打印流向客户端输出数据
                        System.out.println(br.readLine()); // 整行读取
                        ps.println("fine,thanks");
                        
                        socket.close(); // 关流
                        
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }
            }.start(); // 开启线程

        }
    }

    public static void demo1() throws IOException {
        ServerSocket ss = new ServerSocket(12345); // 创建ServerSocket对象
        Socket socket = ss.accept(); // 接收客户端的请求

        BufferedReader br = // 创建带缓冲的字符输入流
                new BufferedReader(new InputStreamReader(socket.getInputStream())); // 将字节流包装成了字符流
        PrintStream ps = new PrintStream(socket.getOutputStream()); // PrintStream中有写出换行的方法

        ps.println("hello"); // 调用打印流向客户端输出数据
        System.out.println(br.readLine()); // 整行读取
        ps.println("fine,thanks");

        socket.close(); // 关流
    }
}
Server

 

 

练习1

* 客户端向服务器写字符串(键盘录入),服务器(多线程)将字符串反转后写回,客户端再次读取到反转后的字符串

package com.heima.test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class Test1_Client {

    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in); // 创建键盘录入对象
        Socket socket = new Socket("127.0.0.1", 54321); // 创建客户端,指定ip地址和端口号
        
        BufferedReader br = // 创建带缓冲的字符输入流
                new BufferedReader(new InputStreamReader(socket.getInputStream())); // 将字节流包装为字符流
        PrintStream ps = new PrintStream(socket.getOutputStream()); // 获取打印输出流
        
        ps.println(sc.nextLine()); // 将字符串写到服务器
        System.out.println(br.readLine()); // 打印从服务器读取到的反转后的字符串
        
        sc.close(); // 关流
        socket.close(); // 关闭socket的同时也会关闭br和ps
    }
}
Client
package com.heima.test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Test1_Server {

    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(54321); // 创建ServerSocket,绑定端口号
        System.out.println("服务器启动,绑定54321端口");
        
        while (true) {
            Socket socket = server.accept(); // 接收客户端的请求
            
            new Thread() { // 开启一条线程
                public void run() { // 重写 run()方法
                    try {
                        BufferedReader br = // 创建带缓冲的字符输入流
                                new BufferedReader(new InputStreamReader(socket.getInputStream())); // 将字节流包装为字符流
                        PrintStream ps = new PrintStream(socket.getOutputStream()); // 获取输出流
                        
                        String line = br.readLine(); // 将客户端写过来的数据读取出来
                        
                        /*StringBuffer sb = new StringBuffer(line);
                        sb.reverse();
                        line = sb.toString();*/
                        
                        line = new StringBuffer(line).reverse().toString(); // 链式编程
                        ps.println(line); // 反转后写回去
                        socket.close(); // 关流
                        
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    
                }
            }.start(); // 开启线程
            
        }
    }
}
Server

 

 

练习2

* 从客户端向服务器上传文件

package com.heima.test;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class Test2_Client {

    public static void main(String[] args) throws IOException {
        File file = getFile(); // 提示输入需要上传的文件路径,验证路径是否存在,以及是否是文件夹
        Socket socket = new Socket("127.0.0.1", 12345); // 创建Socket对象,指定ip和端口号
        BufferedReader br = 
                new BufferedReader(new InputStreamReader(socket.getInputStream()));
        PrintStream ps = new PrintStream(socket.getOutputStream());
        
        ps.println(file.getName()); // 发送文件名到服务器
        String result = br.readLine(); // 读取文件是否存在的结果
        
        if ("文件已存在".equals(result)) {
            System.out.println("您上传的文件已经存在,请不要重复上传");
            socket.close();
            return; // 退出程序
        }
        
        FileInputStream fis = new FileInputStream(file); // 必须使用字节流读取
        byte[] arr = new byte[8192];
        int len;
        while ((len = fis.read(arr))!= -1) {
            ps.write(arr, 0, len); // 注意用 write()方法而不是 println()方法
        }
        
        fis.close(); // 关流
        socket.close();
    }

    private static File getFile() {
        Scanner sc = new Scanner(System.in); // 创建键盘录入对象
        System.out.println("请输入一个文件路径:"); // 提示输入
        while (true) {
            String line = sc.nextLine();
            File file = new File(line);
            
            if (!file.exists()) {
                System.out.println("您录入的文件路径不存在,请重新输入");
            } else if (file.isDirectory()) {
                System.out.println("您录入的是文件夹,请重新录入一个文件路径");
            } else {
                sc.close();
                return file;
            }
        }
    }
}
Client
package com.heima.test;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Test2_Server {
    
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(12345); // 建立多线程的服务器
        System.out.println("服务器启动,绑定12345端口号");
        
        while (true) {
            Socket socket = server.accept(); // 接收客户端的请求
            
            new Thread() {
                public void run() {
                    try {
                        InputStream is = socket.getInputStream(); // 创建字节流,便于非文本类文件的读写
                        BufferedReader br = 
                                new BufferedReader(new InputStreamReader(is));
                        PrintStream ps = new PrintStream(socket.getOutputStream());
                        String fileName = br.readLine();
                        
                        File dir = new File("update"); // 判断文件是否存在,将结果发回客户端
                        dir.mkdir(); // 创建文件夹
                        File file = new File(dir,fileName); // 封装成File对象
                        
                        if (file.exists()) { // 如果服务器中已经存在该文件
                            ps.println("文件已存在"); // 给予提示
                            socket.close(); // 关闭socket,退出程序
                            return; // 退出程序
                        } else {
                            ps.println("文件不存在");
                        }
                        
                        FileOutputStream fos = new FileOutputStream(file); // 从网络读取数据,存储到本地
                        byte[] arr = new byte[8192];
                        int len;
                        while ((len = is.read(arr))!= -1) { // 用字节流读取
                            fos.write(arr, 0, len); // 用字节流写入
                        }
                        
                        fos.close(); // 关流
                        socket.close();
                        
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    
                }
            }.start();
        }
    }
}
Server

 

posted @ 2020-04-18 21:13  小么VinVin  阅读(109)  评论(0编辑  收藏  举报