Java_网络编程

网络编程

定义:

        网络编程就是计算机跟计算机之间通过网络进行数据传输

常见的软件架构:

        1.C/S(Client/Server):客户端/服务器模式

        2.B/S(Browser/Server):浏览器/服务器模式

区别和优缺点:

        C/S需要开发客户端和服务端,

        B/S不需要开发客户端。

        C/S适合定制专业化的软件如:IDEA、一些画面精美的大型游戏,

        B/S适合移动互联网应用,可以在任何地方随时访问的系统。

网络编程三要素

1.IP:

        设备在网络中的地址,是唯一的标识

IP的作用:

        设备在网络中的地址,是唯一的标识

IPv4有什么特点:

        是目前的主流方案

        最多只有2^32个ip,目前已经用完了

IPv6有什么特点:

        为了解决IPv4不够用而出现的

        最多有2^128个ip

IPv4小细节:

        1.现在如何解决IPv4不够的问题?

                利用局域网IP解决IP不够的问题

        2.特殊的IP:

                127.0.0.1(永远表示本机)

        3.常见的两个CMD命令:

                ipconfig:查看本机IP地址

                ping(+IP地址/网址):检查网络是否连通

InetAddress类的使用:
        代码演示:
public class netAddressDemo1 {
    public static void main(String[] args) throws UnknownHostException {
        //InetAddress类的使用
        /*
        static InetAddress getByName(String host)   确定主机名称的IP地址,主机名称可以是机器名称,也可以是IP地址
        String getHostName()                        获取此IP地址的主机名
        String getHostAddress()                     返回文本显示中的IP地址字符串
         */

        //获取InetAddress对象
        InetAddress address = InetAddress.getByName("LAPTOP-8RU5E3RO");
        System.out.println(address);

        //String getHostName()  获取此IP地址的主机名
        String hostName = address.getHostName();
        System.out.println(hostName);

        //String getHostAddress()   返回文本显示中的IP地址字符串
        String hostAddress = address.getHostAddress();
        System.out.println(hostAddress);


    }
}
运行结果:

2.端口号:

        应用程序在设备中唯一的标识

端口号介绍:

        由两个字节表示的整数,取值范围:0~65535,其中0~1023之间的端口号用于一些知名的网络服务或者应用。我们自己使用1024以上的端口号就可以了。

        注意:一个端口号只能被一个应用程序使用

3.协议:

        数据在网络中传输的规则,常见的协议有UDP、TCP、http、https、ftp

UDP协议:

        用户数据报协议(User Datagram Protocol)

        UDP是面向无连接通信协议

        速度快,有大小限制一次最多发送64K,数据不安全,易丢失数据

        适用情况有:网络会议、在线视频、在线通话等

发送数据:
        步骤:

                1.创建DatagramSocket对象(快递公司)

        (参数为端口号,空参表示在所有可用的端口中随机使用一个,有参则使用指定的端口号)

                2.打包数据

                3.发送数据

                4.释放资源

        代码演示:
public class SendMessageDemo1 {
    public static void main(String[] args) throws IOException {
        /*
        1.创建DatagramSocket对象(快递公司)
        2.打包数据
        3.发送数据
        4.释放资源
         */

        //1.创建DatagramSocket对象(快递公司)
        DatagramSocket ds = new DatagramSocket();

        //2.打包数据
        String str = "要发送的数据";
        byte[] bytes = str.getBytes();
        InetAddress address = InetAddress.getByName("127.0.0.1");
        int port = 10086;
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);

        //3.发送数据
        ds.send(dp);

        //4.释放资源
        ds.close();

    }
}
 接收数据:
        步骤:

                1.创建DatagramSocket对象(快递公司)

        (接收的时候必须要绑定端口,且绑定的端口要跟发送的端口保持一致)

                2.接收数据包

                3.解析数据包

                4.释放资源

        代码演示:
public class ReceiveMessageDemo1 {
    public static void main(String[] args) throws IOException {
        /*
        1.创建DatagramSocket对象(快递公司)
       (接收的时候必须要绑定端口,且绑定的端口要跟发送的端口保持一致)
        2.接收数据包
        3.解析数据包
        4.释放资源
         */

        //1.创建DatagramSocket对象(快递公司)
        DatagramSocket ds = new DatagramSocket(10086);

        //2.接收数据包
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
        ds.receive(dp);//该方法是阻塞的,即这句代码运行后,不接受到消息,是不会往下继续运行的

        //3.解析数据包
        byte[] data = dp.getData();
        int length = dp.getLength();
        InetAddress address = dp.getAddress();
        int port = dp.getPort();
        System.out.println("从" + address + "端口号为" + port
                + "接收到长度为" + length + "的数据:" + new String(data,0,length));

        //4.释放资源
        ds.close();

    }
}
运行结果:

简易聊天室小练习:

        按照下面的要求实现程序:

        UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束

        UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收

代码演示:

接收端:

public class ReceiveMessageDemo2 {
    public static void main(String[] args) throws IOException {

        //1.创建DatagramSocket对象
        DatagramSocket ds = new DatagramSocket(10086);

        //2.接收数据包
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
        while (true) {
            ds.receive(dp);

            //3.解析数据
            String ip = dp.getAddress().getHostAddress();
            String hostName = dp.getAddress().getHostName();
            byte[] data = dp.getData();
            System.out.println("接收到从IP地址为:" + ip + "主机名为:" + hostName + "的数据:" + new String(data));
        }

    }
}

发送端:

public class SendMessageDemo2 {
    public static void main(String[] args) throws IOException {
        /*
        按照下面的要求实现程序:
        UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束
        UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收
         */

        //1.创建DatagramSocket对象
        DatagramSocket ds = new DatagramSocket();

        //2.打包数据
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入你要发送的内容(发送886则程序关闭)");
            String message = sc.nextLine();
            if("886".equals(message)) {
                break;
            }
            byte[] bytes = message.getBytes();
            InetAddress address = InetAddress.getByName("127.0.0.1");
            int port = 10086;
            DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);

            //3.发送数据
            ds.send(dp);
        }

        //4.释放资源
        ds.close();

    }
}
运行结果:

发送端:

接收端:

UDP的三种通讯方式

        1.单播

                前面的代码就是单播

        2.组播

                组播地址:224.0.0.0 ~ 239.255.255.255

                其中224.0.0.0 ~ 224.0.0.255为预留的组播地址

        3.广播

                广播地址:255.255.255.255

        因为前面的代码就是单播,这里只演示组播和广播代码

组播代码演示:

发送端:

public class SendMessageDemo {
    public static void main(String[] args) throws IOException {
        /*
        1.创建DatagramSocket对象(快递公司)
        2.打包数据
        3.发送数据
        4.释放资源
         */

        //1.创建MulticastSocket对象(快递公司)
        MulticastSocket ms = new MulticastSocket();

        //2.打包数据
        String str = "要发送的数据";
        byte[] bytes = str.getBytes();
        InetAddress address = InetAddress.getByName("224.0.0.1");
        int port = 10000;
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);

        //3.发送数据
        ms.send(dp);

        //4.释放资源
        ms.close();

    }
}

三个接收端:

接收端1:

public class ReceiveMessageDemo1 {
    public static void main(String[] args) throws IOException {
        //1.创建MulticastSocket对象
        MulticastSocket ms = new MulticastSocket(10000);

        //2.将本机添加到224.0.0.1这一组当中
        InetAddress address = InetAddress.getByName("224.0.0.1");
        ms.joinGroup(address);

        //3.接收数据包
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
        ms.receive(dp);

        //4.解析数据
        String ip = dp.getAddress().getHostAddress();
        String hostName = dp.getAddress().getHostName();
        byte[] data = dp.getData();
        System.out.println("接收到IP地址为:" + ip + "主机名为:" + hostName + "的数据:" + new String(data));

        //5.释放资源
        ms.close();

    }
}

接收端2:

public class ReceiveMessageDemo2 {
    public static void main(String[] args) throws IOException {
        //1.创建MulticastSocket对象
        MulticastSocket ms = new MulticastSocket(10000);

        //2.将本机添加到224.0.0.1这一组当中
        InetAddress address = InetAddress.getByName("224.0.0.1");
        ms.joinGroup(address);

        //3.接收数据包
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
        ms.receive(dp);

        //4.解析数据
        String ip = dp.getAddress().getHostAddress();
        String hostName = dp.getAddress().getHostName();
        byte[] data = dp.getData();
        System.out.println("接收到IP地址为:" + ip + "主机名为:" + hostName + "的数据:" + new String(data));

        //5.释放资源
        ms.close();

    }
}

接收端3:

public class ReceiveMessageDemo3 {
    public static void main(String[] args) throws IOException {
        //1.创建MulticastSocket对象
        MulticastSocket ms = new MulticastSocket(10000);

        //2.将本机添加到224.0.0.1这一组当中
        InetAddress address = InetAddress.getByName("224.0.0.1");
        ms.joinGroup(address);

        //3.接收数据包
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
        ms.receive(dp);

        //4.解析数据
        String ip = dp.getAddress().getHostAddress();
        String hostName = dp.getAddress().getHostName();
        byte[] data = dp.getData();
        System.out.println("接收到IP地址为:" + ip + "主机名为:" + hostName + "的数据:" + new String(data));

        //5.释放资源
        ms.close();

    }
}
运行结果:

接收端1:

接收端2:

接收端3:

广播代码演示:

将单播的传输目的地址改为255.255.255.255即可

public class SendMessageDemo {
    public static void main(String[] args) throws IOException {
        /*
        1.创建DatagramSocket对象(快递公司)
        2.打包数据
        3.发送数据
        4.释放资源
         */

        //1.创建DatagramSocket对象(快递公司)
        DatagramSocket ds = new DatagramSocket();

        //2.打包数据
        String str = "要发送的数据";
        byte[] bytes = str.getBytes();
        InetAddress address = InetAddress.getByName("255.255.255.255");
        int port = 10086;
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);

        //3.发送数据
        ds.send(dp);

        //4.释放资源
        ds.close();

    }
}

TCP协议:

        传输控制协议TCP(Transmission Control Protocol)

        TCP协议是面向连接的通信协议。

        速度慢,没有大小限制,数据安全。

        适用情况有:下载软件、文字聊天、发送邮件等

TCP通信程序:

        TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象

        通信之前要保证连接已经建立

        通过Socket产生IO流来进行网络通信

编写步骤:

        客户端:

                ①创建客户端的Socket对象(Socket)与指定服务端连接(创建对象的同时会连接服务器)

                    Socket(String host, int port)

                ②获取输出流,写数据(写出的数据应为字节数组)

                    OutputStream getOutputStream()

                ③释放资源

                    void close()

        服务端:

                ①创建服务器端的Socket对象(ServerSocket)

                    ServerSocket(int port)

                ②监听客户端连接,返回一个Socket对象(也是阻塞方法)

                    Socket accept()

                ③获取输入流,读数据,并把数据显示在控制台

                    InputStream getInputStream()

                ④释放资源

                    void close()

代码演示1:

客户端:

public class Client {
    public static void main(String[] args) throws IOException {
        /*
        1.创建客户端的Socket对象(Socket)与指定服务端连接
            Socket(String host, int port)
        2.获取输出流,写数据
            OutputStream getOutputStream()
        3.释放资源
            void close()
        */

        //1.创建客户端的Socket对象(Socket)与指定服务端连接
        Socket socket = new Socket("127.0.0.1",10000);

        //2.获取输出流,写数据
        OutputStream os = socket.getOutputStream();
        os.write("aaa".getBytes());//注意要写字节数组

        //3.释放资源
        os.close();
        socket.close();

    }
}

服务端:

public class Server {
    public static void main(String[] args) throws IOException {
        /*
        1.创建服务器端的Socket对象(ServerSocket)
             ServerSocket(int port)
        2.监听客户端连接,返回一个Socket对象
             Socket accept()
        3.获取输入流,读数据,并把数据显示在控制台
             InputStream getInputStream()
        4.释放资源
             void close()
        */

        //1.创建服务器端的Socket对象(ServerSocket)
        ServerSocket ss = new ServerSocket(10000);

        //2.监听客户端连接,返回一个Socket对象
        Socket socket = ss.accept();

        //3.获取输入流,读数据,并把数据显示在控制台
        InputStream is = socket.getInputStream();
        int b;
        while ((b = is.read()) != -1) {
            System.out.print((char) b);
        }

        //4.释放资源
        is.close();
        socket.close();
        ss.close();


    }
}
运行结果1:

注意:

        这种方式发送中文信息会产生乱码,因为汉字转换成字节数组后字节流是一个一个字节去读的。

        下面是可以传输中文的代码:

代码演示2

 客户端:

public class Client {
    public static void main(String[] args) throws IOException {
        /*
        1.创建客户端的Socket对象(Socket)与指定服务端连接
            Socket(String host, int port)
        2.获取输出流,写数据
            OutputStream getOutputStream()
        3.释放资源
            void close()
        */

        //1.创建客户端的Socket对象(Socket)与指定服务端连接
        Socket socket = new Socket("127.0.0.1",10000);

        //2.获取输出流,写数据
        OutputStream os = socket.getOutputStream();
        os.write("你好啊".getBytes());//注意要写字节数组

        //3.释放资源
        os.close();
        socket.close();

    }
}

服务端:

public class Server {
    public static void main(String[] args) throws IOException {
        /*
        1.创建服务器端的Socket对象(ServerSocket)
             ServerSocket(int port)
        2.监听客户端连接,返回一个Socket对象
             Socket accept()
        3.获取输入流,读数据,并把数据显示在控制台
             InputStream getInputStream()
        4.释放资源
             void close()
        */

        //1.创建服务器端的Socket对象(ServerSocket)
        ServerSocket ss = new ServerSocket(10000);

        //2.监听客户端连接,返回一个Socket对象
        Socket socket = ss.accept();

        //3.获取输入流,读数据,并把数据显示在控制台
        InputStream is = socket.getInputStream();
        //使用转换流将字节输入流转换成字符输入流(也可以在转换成字符缓冲输入流)
        InputStreamReader isr = new InputStreamReader(is);
        int b;
        while ((b = isr.read()) != -1) {
            System.out.print((char) b);
        }

        //4.释放资源
        socket.close();
        ss.close();


    }
}

运行结果2:

三次握手:

四次挥手:

参考模型
OSI参考模型:

        

应用层
表示层
会话层
传输层
网络层
数据链路层
物理层
TCP/IP参考模型:
应用层
传输层
网络层
物理+数据链路层

练习题

练习一:

        多发多收

                客户端:多次发送数据,发送886时结束

                服务端:接收多次数据,并打印

代码演示:
客户端:
public class Client {
    public static void main(String[] args) throws IOException {
        /*
        多发多收
            客户端:多次发送数据,发送886时结束
            服务端:接收多次数据,并打印
         */

        //1.创建Socket对象
        Socket socket = new Socket("127.0.0.1",10001);

        //2.获取输出流
        OutputStream os = socket.getOutputStream();
        Scanner sc = new Scanner(System.in);

        while (true) {
            System.out.println("请输入您要发送的内容");
            String message = sc.nextLine();
            if("886".equals(message)) {
                break;
            }
            os.write(message.getBytes());
        }

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

    }
}
服务端:
public class Server {
    public static void main(String[] args) throws IOException {

        //1.创建ServerSocket对象
        ServerSocket ss = new ServerSocket(10001);

        //2.监听客户端连接,返回Socket对象
        Socket socket = ss.accept();

        //3.获取输入流
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        int b;
        while ((b = br.read()) != -1) {
            System.out.print((char) b);
        }

        //4.释放资源
        socket.close();
        ss.close();

    }
}
运行结果:
客户端:

服务端:

练习二:

        接收和反馈

                客户端:发送一条数据,接收服务端反馈的消息并打印

                服务端:接收数据并打印,再给客户端反馈消息

代码演示:
客户端:
public class Client {
    public static void main(String[] args) throws IOException {
        /*
        接收和反馈
            客户端:发送一条数据,接收服务端反馈的消息并打印
            服务端:接收数据并打印,再给客户端反馈消息
         */

        //1.创建Socket对象
        Socket socket = new Socket("127.0.0.1",10001);

        //2.获取输出流
        //发送一条数据
        OutputStream os = socket.getOutputStream();
        os.write("你好".getBytes());
        //终止输出流,终止服务端的read方法
        socket.shutdownOutput();


        //接收服务端反馈的消息
        InputStreamReader isr = new InputStreamReader(socket.getInputStream());
        int b;
        while ((b = isr.read()) != -1) {
            System.out.print((char) b);
        }

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

    }
}
服务端:
public class Server {
    public static void main(String[] args) throws IOException {

        //1.创建ServerSocket对象
        ServerSocket ss = new ServerSocket(10001);

        //2.监听客户端连接,返回Socket对象
        Socket socket = ss.accept();

        //3.获取输入流
        InputStreamReader isr = new InputStreamReader(socket.getInputStream());
        int b;
        //注意:read方法会从连接通道中读取数据
        //但是,需要有一个结束标记,此时的循环才会停止
        //否则,程序就会一直停在read方法这里,等待读取下面的数据
        while ((b = isr.read()) != -1) {
            System.out.print((char) b);
        }
        //输出反馈消息
        OutputStream os = socket.getOutputStream();
        os.write("你也好".getBytes());

        //4.释放资源
        socket.close();
        ss.close();

    }
}
运行结果:
客户端:

服务端:

练习三:

        上传文件

                客户端:将本地文件上传到服务器。接收服务器的反馈

                服务器:接收客户端上传的文件,上传完毕之后给出反馈

代码演示:
客户端:
public class Client {
    public static void main(String[] args) throws IOException {
        /*
        上传文件
            客户端:将本地文件上传到服务器。接收服务器的反馈
            服务器:接收客户端上传的文件,上传完毕之后给出反馈
         */

        //创建Socket对象
        Socket socket = new Socket("127.0.0.1",10001);

        //获取输出流
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());

        //读取文件
        BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream("C:\\Users\\Han\\IdeaProjects\\mysocketnet\\clientdir\\img.png"));
        byte[] bytes = new byte[1024];
        int len;
        while((len = bis.read(bytes)) != -1) {
            //写出文件
            bos.write(bytes,0,len);
        }
        bis.close();
        bos.flush();

        //终止输出流
        socket.shutdownOutput();

        //接收反馈
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String feedback = br.readLine();
        System.out.println(feedback);

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


    }
}
服务端:
public class Server {
    public static void main(String[] args) throws IOException {

        //创建ServerSocket对象
        ServerSocket ss = new ServerSocket(10001);

        //监听客户端连接,返回Socket对象
        Socket socket = ss.accept();

        //获取输入流
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\mysocketnet\\serverdir\\img.png"));
        byte[] bytes = new byte[1024];
        int len;
        while((len = bis.read(bytes)) != -1) {
            //存储文件
            bos.write(bytes,0,len);
        }
        bos.flush();
        bos.close();

        //反馈已接收文件信息
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write("已接收文件");
        bw.flush();

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

    }
}
运行结果:
客户端:

且图片正常复制

练习四:

        解决上一题文件名重复问题

代码演示:
客户端:

        没有变化

public class Client {
    public static void main(String[] args) throws IOException {
        /*
        解决上一题文件名重复问题
         */

        //创建Socket对象
        Socket socket = new Socket("127.0.0.1",10001);

        //获取输出流
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());

        //读取文件
        BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream("C:\\Users\\Han\\IdeaProjects\\mysocketnet\\clientdir\\img.png"));
        byte[] bytes = new byte[1024];
        int len;
        while((len = bis.read(bytes)) != -1) {
            //写出文件
            bos.write(bytes,0,len);
        }
        bis.close();
        bos.flush();

        //终止输出流
        socket.shutdownOutput();

        //接收反馈
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String feedback = br.readLine();
        System.out.println(feedback);

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


    }
}
服务端:

        新添加用UUID类来生成随机唯一名字

public class Server {
    public static void main(String[] args) throws IOException {

        //创建ServerSocket对象
        ServerSocket ss = new ServerSocket(10001);

        //监听客户端连接,返回Socket对象
        Socket socket = ss.accept();

        //获取输入流
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());

        //利用UUID获取随机文件名称
        String name = UUID.randomUUID().toString().replace("-", "");

        //定义输出流输出到文件
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\mysocketnet\\serverdir\\" + name + ".png"));
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1) {
            //存储文件
            bos.write(bytes, 0, len);
        }
        bos.flush();
        bos.close();

        //反馈已接收文件信息
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write("已接收文件");
        bw.flush();

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

    }
}
运行结果:
客户端:

且图片正常复制,运行两次后结果:

练习五:

        上传文件(多线程版)

                想要服务器不停止,能接收很多用户上传的图片。

                该怎么做? 提示:可以用多线程

代码演示:
客户端:
public class Client {
    public static void main(String[] args) throws IOException {
        /*
        上传文件(多线程版)
            想要服务器不停止,能接收很多用户上传的图片。
            该怎么做?
            提示:可以用多线程
         */

        //创建Socket对象
        Socket socket = new Socket("127.0.0.1",10001);

        //获取输出流
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());

        //读取文件
        BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream("C:\\Users\\Han\\IdeaProjects\\mysocketnet\\clientdir\\img.png"));
        byte[] bytes = new byte[1024];
        int len;
        while((len = bis.read(bytes)) != -1) {
            //写出文件
            bos.write(bytes,0,len);
        }
        bis.close();
        bos.flush();

        //终止输出流
        socket.shutdownOutput();

        //接收反馈
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String feedback = br.readLine();
        System.out.println(feedback);

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


    }
}
服务端:
public class Server {
    public static void main(String[] args) throws IOException {

        //创建ServerSocket对象
        ServerSocket ss = new ServerSocket(10001);

        while (true) {
            //监听客户端连接,返回Socket对象
            Socket socket = ss.accept();

            //创建线程
            MyRunnable mr = new MyRunnable(socket);
            Thread thread = new Thread(mr);
            thread.start();

        }

    }
}
MyRunnable类:
public class MyRunnable implements Runnable {

    Socket socket;

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


    @Override
    public void run() {
        try {
            //获取输入流
            BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());

            //利用UUID获取随机文件名称
            String name = UUID.randomUUID().toString().replace("-", "");

            //定义输出流输出到文件
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\mysocketnet\\serverdir\\" + name + ".png"));
            byte[] bytes = new byte[1024];
            int len;
            while ((len = bis.read(bytes)) != -1) {
                //存储文件
                bos.write(bytes, 0, len);
            }
            bos.flush();
            bos.close();

            //反馈已接收文件信息
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("已接收文件");
            bw.flush();


        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //释放资源
            if(socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
运行结果:
客户端:

且图片正常复制

练习六:

        上传文件(线程池优化)

        频繁创建线程并销毁非常浪费系统资源,所以需要用线程池优化

代码演示:
客户端:
public class Client {
    public static void main(String[] args) throws IOException {
        /*
        上传文件(线程池优化)
            频繁创建线程并销毁非常浪费系统资源,所以需要用线程池优化
         */

        //创建Socket对象
        Socket socket = new Socket("127.0.0.1",10001);

        //获取输出流
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());

        //读取文件
        BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream("C:\\Users\\Han\\IdeaProjects\\mysocketnet\\clientdir\\img.png"));
        byte[] bytes = new byte[1024];
        int len;
        while((len = bis.read(bytes)) != -1) {
            //写出文件
            bos.write(bytes,0,len);
        }
        bis.close();
        bos.flush();

        //终止输出流
        socket.shutdownOutput();

        //接收反馈
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String feedback = br.readLine();
        System.out.println(feedback);

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


    }
}
服务端:
public class Server {
    public static void main(String[] args) throws IOException {

        //创建ServerSocket对象
        ServerSocket ss = new ServerSocket(10001);

        //创建线程池对象
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                3,//核心线程数
                16,//总线程数
                60,//空闲时间
                TimeUnit.SECONDS,//空闲时间单位
                new ArrayBlockingQueue<>(2),//队列长度
                Executors.defaultThreadFactory(),//线程工厂
                new ThreadPoolExecutor.AbortPolicy()//拒绝策略
        );

        while (true) {
            //监听客户端连接,返回Socket对象
            Socket socket = ss.accept();

            //交给线程池
            pool.submit(new MyRunnable(socket));


        }

    }
}
MyRunnable类:
public class MyRunnable implements Runnable {

    Socket socket;

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


    @Override
    public void run() {
        try {
            //获取输入流
            BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());

            //利用UUID获取随机文件名称
            String name = UUID.randomUUID().toString().replace("-", "");

            //定义输出流输出到文件
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:\\Users\\Han\\IdeaProjects\\mysocketnet\\serverdir\\" + name + ".png"));
            byte[] bytes = new byte[1024];
            int len;
            while ((len = bis.read(bytes)) != -1) {
                //存储文件
                bos.write(bytes, 0, len);
            }
            bos.flush();
            bos.close();

            //反馈已接收文件信息
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("已接收文件");
            bw.flush();


        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //释放资源
            if(socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
运行结果:
客户端:

且图片正常复制

练习七:

        BS(接收浏览器的消息并打印)

                客户端:不需要写

                服务器:接收数据并打印

代码演示:
服务端:
public class Server {
    public static void main(String[] args) throws IOException {
        /*
        BS(接收浏览器的消息并打印)
            客户端:不需要写
            服务器:接收数据并打印
         */

        //1.创建ServerSocket对象
        ServerSocket ss = new ServerSocket(10001);

        //2.监听客户端连接,返回Socket对象
        Socket socket = ss.accept();

        //3.获取输入流
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        int b;
        while ((b = br.read()) != -1) {
            System.out.print((char) b);
        }

        //4.释放资源
        socket.close();
        ss.close();


    }
}

        开启服务端

打开浏览器,在网址搜索栏上输入127.0.0.1:10001(服务端启动的端口号)

运行结果:

大作业:

        控制台版聊天室

代码演示:

Client类:
public class Client {
    public static void main(String[] args) throws IOException {
        /*服务器已经连接成功
                ==============欢迎来到聊天室================
        1登录
        2注册
        请输入您的选择:*/
        //创建Socket对象

        Socket socket = new Socket("127.0.0.1",10001);

        ClientRunnable cr = new ClientRunnable(socket);

        Scanner sc = new Scanner(System.in);

        System.out.println("服务器已经连接成功");
        while (true) {
            System.out.println("==============欢迎来到聊天室================");
            System.out.println("1登录");
            System.out.println("2注册");
            System.out.print("请输入您的选择:");
            //读取用户输入信息
            String select = sc.nextLine();
            if (select.equals("1")) {
                System.out.println("点击了登录");
                if (loginEvent(socket)) {
                    //创建线程进入聊天室聊天
                    Thread thread = new Thread(cr);
                    thread.start();
                    //开始聊天
                    while(true) {
                        System.out.println("请输入想说的话:");
                        String message = sc.nextLine();
                        //传给服务端
                        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                        bw.write(message);
                        bw.newLine();
                        bw.flush();
                    }
                }
            } else if (select.equals("2")) {
                System.out.println("点击了注册");
                if (registerEvent()) {
                    break;
                }
            } else {
                System.out.println("没有这个选项,请重新输入:");
            }
        }

    }


    //注册事件
    private static boolean registerEvent() throws IOException {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = sc.nextLine();
        System.out.println("请输入密码:");
        String password = sc.nextLine();
        System.out.println("请再次输入密码:");
        String passwordAgain = sc.nextLine();
        //两次输入的密码不一致
        if (!(password.equals(passwordAgain))) {
            System.out.println("两次输入的密码不一致");
            return false;
        }
        //用户名或密码格式有误
        if (!(usernameFormatVerify(username) && passwordFormatVerify(password))) {
            System.out.println("用户名或密码格式有误");
            return false;
        }
        //查看用户名是否已存在
        BufferedReader br = new BufferedReader(
                new FileReader("C:\\Users\\Han\\IdeaProjects\\mychatroom\\src\\com\\han\\chatroom\\usersinfo"));
        ArrayList<String> list = new ArrayList<>();
        String line;
        while ((line = br.readLine()) != null) {
            list.add(line);
        }
        br.close();
        for (String str : list) {
            String us = str.split("=")[0];
            if (username.equals(us)) {
                System.out.println("用户名已存在");
                return false;
            }
        }
        //将新用户名和密码存入文件
        BufferedWriter bw = new BufferedWriter(
                new FileWriter("C:\\Users\\Han\\IdeaProjects\\mychatroom\\src\\com\\han\\chatroom\\usersinfo", true));
        //username=zhangsan&password=123
        bw.newLine();
        bw.write(username + "=" + password);
        bw.flush();
        bw.close();
        System.out.println("注册成功");
        return true;
    }

    //登录事件
    private static boolean loginEvent(Socket socket) throws IOException {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入您的用户名:");
        String username = sc.nextLine();
        System.out.println("请输入您的密码:");
        String password = sc.nextLine();
        StringBuffer sb = new StringBuffer();
        //以下面这种形式将用户名密码传输给服务器
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        //username=zhangsan&password=123
        if (usernameFormatVerify(username) && passwordFormatVerify(password)) {
            sb.append("username=").append(username).append("&password=").append(password);
            //传输给服务端
            //获取输出流
            bw.write(sb.toString());
            bw.newLine();
            bw.flush();
            //接收输入流并打印
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String result = br.readLine();
            System.out.println(result);

            if("用户名密码正确,登录成功".equals(result)) {
                return true;
            }
        }
        return false;
    }

    //验证用户名格式
    public static boolean usernameFormatVerify(String username) {
        //用户名要唯一,长度:6~18位,纯字母,不能有数字或其他符号。
        int len = username.length();
        //长度:6~18位
        if (len < 6 || len > 18) {
            System.out.println("用户名长度应为6~18位");
            return false;
        }
        //纯字母
        for (int i = 0; i < len; i++) {
            char ch = username.charAt(i);
            if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) {
                System.out.println("用户名应为纯字母,不能有数字或其他符号");
                return false;
            }
        }
        return true;
    }

    //验证密码格式
    public static boolean passwordFormatVerify(String password) {
        //密码长度3~8位。第一位必须是小写或者大小的字母,后面必须是纯数字。
        int len = password.length();
        //长度3~8位
        if (len < 3 || len > 8) {
            System.out.println("密码长度应为3~8位");
            return false;
        }
        //第一位必须是小写或者大小的字母,后面必须是纯数字
        char start = password.charAt(0);
        if (!((start >= 'a' && start <= 'z') || (start >= 'A' && start <= 'Z'))) {
            System.out.println("密码第一位必须是小写或者大小的字母,后面必须是纯数字");
            return false;
        }
        char[] arr = new char[len - 1];
        password.getChars(1, len, arr, 0);
        for (char ch : arr) {
            if (!(ch >= '0' && ch <= '9')) {
                System.out.println("密码第一位必须是小写或者大小的字母,后面必须是纯数字");
                return false;
            }
        }
        return true;
    }
}
CilientRunnable类:
public class ClientRunnable implements Runnable {

    Socket socket;

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

    @Override
    public void run() {

        //获取输入流
        try {
            while (true) {
                BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String message = br.readLine();
                System.out.println(message);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }
}
Server类:
public class Server {

    //创建集合管理登录成功的Socket对象
    static ArrayList<Socket> socketList = new ArrayList<>();


    public static void main(String[] args) throws IOException {

        //创建ServerSocket对象
        ServerSocket ss = new ServerSocket(10001);


        while (true) {
            //监听客户端连接,获得Socket对象
            Socket socket = ss.accept();

            //只要来一个客户端就创建一个线程
            ServerRunnable sr = new ServerRunnable(socketList,socket);
            Thread thread = new Thread(sr);
            thread.start();
        }

    }


}
ServerRunnable类:
public class ServerRunnable implements Runnable{

    ArrayList<Socket> socketList;
    Socket socket;

    public ServerRunnable(ArrayList<Socket> socketList, Socket socket) {
        this.socketList = socketList;
        this.socket = socket;
    }

    @Override
    public void run() {
        String rebackStr;

        try {
            //获取输入流
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String str = br.readLine();
            //username=zhangsan&password=123
            String username = str.split("&")[0].split("=")[1];
            String password = str.split("&")[1].split("=")[1];

            //判断用户名密码是否正确
            //读取文件中的用户名密码
            HashMap<String,String> hm = new HashMap<>();
            BufferedReader br2 = new BufferedReader(
                    new FileReader("C:\\Users\\Han\\IdeaProjects\\mychatroom\\src\\com\\han\\chatroom\\usersinfo"));
            String line;
            while((line = br2.readLine()) != null) {
                String[] arr = line.split("=");
                hm.put(arr[0],arr[1]);
            }
            //判断加回写提示
            rebackStr = judge(hm,username,password);
            //回写
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write(rebackStr);
            bw.newLine();
            bw.flush();

            if("用户名密码正确,登录成功".equals(rebackStr)) {
                //登录成功了
                socketList.add(socket);
                while (true) {
                    //读取数据
                    String message = username + "发来消息:" + br.readLine();
                    System.out.println(message);
                    //群发消息
                    for (Socket socket : socketList) {
                        BufferedWriter bw2 = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                        bw2.write(message);
                        bw2.newLine();
                        bw2.flush();
                    }
                }

            } else {
                //登录失败
                System.out.println("登录失败");
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static String judge(HashMap<String,String> hm, String username, String password) {
        int flag = 0;
        Set<Map.Entry<String, String>> entries = hm.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            if(entry.getKey().equals(username)) {
                //有相同用户名
                flag++;
                if(entry.getValue().equals(password)) {
                    return "用户名密码正确,登录成功";
                }
            }
        }
        if(flag == 0) {
            return "用户名不存在,登录失败";
        } else {
            return "密码错误,登录失败";
        }
    }
}

运行结果:

服务端:

客户端1:

客户端2:

客户端3:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值