java基础入门-23-【网络编程】

news2024/11/24 0:08:35

java基础入门-23-【网络编程】

  • 32、网络编程
    • 1.什么是网络编程
    • 2.网络编程三要素
      • 1.1 IP
      • 1.2 总结
      • 1.3 IPV4的地址分类形式
      • 1.4 常见的CMD命令
      • 1.5 InetAddress类的使用
      • 1.6 端口和协议
    • 2.UDP通信程序
      • 2.1 UDP发送数据
      • 2.2UDP接收数据
      • 2.3UDP通信程序练习
      • 2.4UDP三种通讯方式
      • 2.5UDP组播实现
      • 2.6UDP广播实现
    • 3. TCP通信程序
      • 3.1 TCP 发送数据
      • 3.2TCP 接收数据
      • 3.3 TCP程序练习(传输中文)
    • 4. 综合练习
      • 练习一:多发多收
      • 练习二:接收并反馈
      • 练习三:上传练习(TCP协议)
      • 练习四:TCP通信练习----上传文件(文件名重复问题)
      • 练习五:服务器改写为多线程
      • 练习六:线程池改进
      • 练习七:TCP通信练习7---BS(接收浏览器的消息并打印)
    • 5:TCP通信练习8---网络编程(课后大作业)【聊天室】
      • 5.1 项目名称
      • 5.2 使用到的知识点
      • 5.3 准备工作
      • 5.4 需求描述
      • 5.5 其他要求:
      • 5.6 代码实现
    • endl

32、网络编程

1.什么是网络编程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
**在这里插入图片描述**
在这里插入图片描述

  • 计算机网络

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

  • 网络编程

    在网络通信协议下,不同计算机上运行的程序,可以进行数据传输

2.网络编程三要素

在这里插入图片描述
在这里插入图片描述

  • IP地址

    要想让网络中的计算机能够互相通信,必须为每台计算机指定一个标识号,通过这个标识号来指定要接收数据的计算机和识别发送的计算机,而IP地址就是这个标识号。也就是设备的标识

  • 端口

    网络的通信,本质上是两个应用程序的通信。每台计算机都有很多的应用程序,那么在网络通信时,如何区分这些应用程序呢?如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的应用程序了。也就是应用程序的标识

  • 协议

    通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。常见的协议有UDP协议和TCP协议

1.1 IP

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
IP地址:是网络中设备的唯一标识

  • IP地址分为两大类
    • IPv4:是给每个连接在网络上的主机分配一个32bit地址。按照TCP/IP规定,IP地址用二进制来表示,每个IP地址长32bit,也就是4个字节。例如一个采用二进制形式的IP地址是“11000000 10101000 00000001 01000010”,这么长的地址,处理起来也太费劲了。为了方便使用,IP地址经常被写成十进制的形式,中间使用符号“.”分隔不同的字节。于是,上面的IP地址可以表示为“192.168.1.66”。IP地址的这种表示法叫做“点分十进制表示法”,这显然比1和0容易记忆得多
    • IPv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发紧张。为了扩大地址空间,通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进制数,这样就解决了网络地址资源数量不够的问题
  • DOS常用命令:
    • ipconfig:查看本机IP地址
    • ping IP地址:检查网络是否连通
  • 特殊IP地址:
    • 127.0.0.1:是回送地址,可以代表本机地址,一般用来测试使用

1.2 总结

在这里插入图片描述

1.3 IPV4的地址分类形式

在这里插入图片描述
在这里插入图片描述

1.4 常见的CMD命令

在这里插入图片描述
在这里插入图片描述

1.5 InetAddress类的使用

InetAddress:此类表示Internet协议(IP)地址

  • 相关方法

    方法名说明
    static InetAddress getByName(String host)确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址
    String getHostName()获取此IP地址的主机名
    String getHostAddress()返回文本显示中的IP地址字符串
  • 代码演示

    public class InetAddressDemo {
        public static void main(String[] args) throws UnknownHostException {
    		//InetAddress address = InetAddress.getByName("itcast");
            InetAddress address = InetAddress.getByName("192.168.1.66");
    
            //public String getHostName():获取此IP地址的主机名
            String name = address.getHostName();
            //public String getHostAddress():返回文本显示中的IP地址字符串
            String ip = address.getHostAddress();
    
            System.out.println("主机名:" + name);
            System.out.println("IP地址:" + ip);
        }
    }
    

1.6 端口和协议

  • 端口

    • 设备上应用程序的唯一标识
  • 端口号

    • 用两个字节表示的整数,它的取值范围是0~ 65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败
  • 协议

    • 计算机网络中,连接和通信的规则被称为网络通信协议
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
  • UDP协议

    • 用户数据报协议(User Datagram Protocol)
    • UDP是面向无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
    • 由于使用UDP协议消耗系统资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输
    • 例如视频会议通常采用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议
  • TCP协议

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

    • TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”

    • 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠

      第一次握手,客户端向服务器端发出连接请求,等待服务器确认

      第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求

      第三次握手,客户端再次向服务器端发送确认信息,确认连接

    • 完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛。例如上传文件、下载文件、浏览网页等

2.UDP通信程序

2.1 UDP发送数据

在这里插入图片描述

  • Java中的UDP通信

    • UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是这两个Socket只是发送,接收数据的对象,因此对于基于UDP协议的通信双方而言,没有所谓的客户端和服务器的概念
    • Java提供了DatagramSocket类作为基于UDP协议的Socket
  • 构造方法

    方法名说明
    DatagramSocket()创建数据报套接字并将其绑定到本机地址上的任何可用端口
    DatagramPacket(byte[] buf,int len,InetAddress add,int port)创建数据包,发送长度为len的数据包到指定主机的指定端口
  • 相关方法

    方法名说明
    void send(DatagramPacket p)发送数据报包
    void close()关闭数据报套接字
    void receive(DatagramPacket p)从此套接字接受数据报包
  • 发送数据的步骤

    • 创建发送端的Socket对象(DatagramSocket)
    • 创建数据,并把数据打包
    • 调用DatagramSocket对象的方法发送数据
    • 关闭发送端
  • 代码演示

    public class SendDemo {
        public static void main(String[] args) throws IOException {
            //创建发送端的Socket对象(DatagramSocket)
            // DatagramSocket() 构造数据报套接字并将其绑定到本地主机上的任何可用端口
            DatagramSocket ds = new DatagramSocket();
    
            //创建数据,并把数据打包
            //DatagramPacket(byte[] buf, int length, InetAddress address, int port)
            //构造一个数据包,发送长度为 length的数据包到指定主机上的指定端口号。
            byte[] bys = "hello,udp,我来了".getBytes();
    
            DatagramPacket dp = new DatagramPacket(bys,bys.length,InetAddress.getByName("127.0.0.1"),10086);
    
            //调用DatagramSocket对象的方法发送数据
            //void send(DatagramPacket p) 从此套接字发送数据报包
            ds.send(dp);
    
            //关闭发送端
            //void close() 关闭此数据报套接字
            ds.close();
        }
    }
    
public class SendMessageDemo {
    public static void main(String[] args) throws IOException {
        //发送数据

        //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();
    }
}

2.2UDP接收数据

在这里插入图片描述

  • 接收数据的步骤

    • 创建接收端的Socket对象(DatagramSocket)
    • 创建一个数据包,用于接收数据
    • 调用DatagramSocket对象的方法接收数据
    • 解析数据包,并把数据在控制台显示
    • 关闭接收端
  • 构造方法

    方法名说明
    DatagramPacket(byte[] buf, int len)创建一个DatagramPacket用于接收长度为len的数据包
  • 相关方法

    方法名说明
    byte[] getData()返回数据缓冲区
    int getLength()返回要发送的数据的长度或接收的数据的长度
  • 示例代码

    public class ReceiveDemo {
        public static void main(String[] args) throws IOException {
          	//创建接收端的Socket对象(DatagramSocket)
          	DatagramSocket ds = new DatagramSocket(12345);
    
          	//创建一个数据包,用于接收数据
          	byte[] bys = new byte[1024];
          	DatagramPacket dp = new DatagramPacket(bys, bys.length);
    
          	//调用DatagramSocket对象的方法接收数据
          	ds.receive(dp);
    
          	//解析数据包,并把数据在控制台显示
          	System.out.println("数据是:" + new String(dp.getData(), 0,dp.getLength()));
            }
        }
    }
    
public class ReceiveMessageDemo {
    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);

        //该方法是阻塞的
        //该程序执行到这一步的时候,会在这里死等
        //等发送端发送消息
        System.out.println(11111);
        ds.receive(dp);
        System.out.println(22222);

        //3.解析数据包
        byte[] data = dp.getData();
        int len = dp.getLength();
        InetAddress address = dp.getAddress();
        int port = dp.getPort();
        SocketAddress socketAddress = dp.getSocketAddress();

        System.out.println("接收到数据" + new String(data, 0, len));
        System.out.println("该数据是从" + address + "这台电脑中的" + port + "这个端口发出的");
        System.out.println("socketAddress = " + socketAddress);

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

2.3UDP通信程序练习

在这里插入图片描述

  • 案例需求

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

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

  • 代码实现

    /*
        UDP发送数据:
            数据来自于键盘录入,直到输入的数据是886,发送数据结束
     */
    public class SendDemo {
        public static void main(String[] args) throws IOException {
            //创建发送端的Socket对象(DatagramSocket)
            DatagramSocket ds = new DatagramSocket();
            //键盘录入数据
            Scanner sc = new Scanner(System.in);
            while (true) {
              	String s = sc.nextLine();
                //输入的数据是886,发送数据结束
                if ("886".equals(s)) {
                    break;
                }
                //创建数据,并把数据打包
                byte[] bys = s.getBytes();
                DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.1.66"), 12345);
    
                //调用DatagramSocket对象的方法发送数据
                ds.send(dp);
            }
            //关闭发送端
            ds.close();
        }
    }
    
    /*
        UDP接收数据:
            因为接收端不知道发送端什么时候停止发送,故采用死循环接收
     */
    public class ReceiveDemo {
        public static void main(String[] args) throws IOException {
            //创建接收端的Socket对象(DatagramSocket)
            DatagramSocket ds = new DatagramSocket(12345);
            while (true) {
                //创建一个数据包,用于接收数据
                byte[] bys = new byte[1024];
                DatagramPacket dp = new DatagramPacket(bys, bys.length);
                //调用DatagramSocket对象的方法接收数据
                ds.receive(dp);
                //解析数据包,并把数据在控制台显示
                System.out.println("数据是:" + new String(dp.getData(), 0, dp.getLength()));
            }
            //关闭接收端
    //        ds.close();
        }
    }
    
public class SendMessageDemo {
    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("请输入您要说的话:");
            String str = sc.nextLine();
            if ("886".equals(str)){
                break;
            }
            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();
    }
}
public class ReceiveMessageDemo {
    public static void main(String[] args) throws IOException {
        /*
        按照下面的要求实现程序:
            UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束
            UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收
         */

        //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.解析数据包
            byte[] data = dp.getData();
            int len = dp.getLength();
            String ip = dp.getAddress().getHostAddress();
            String name = dp.getAddress().getHostName();

            //4.打印数据
            System.out.println("ip为:" + ip + ",主机名为:" + name + "的人,发送了数据:" + new String(data, 0, len));
        }

    }
}

2.4UDP三种通讯方式

  • 单播

    单播用于两个主机之间的端对端通信

  • 组播

    组播用于对一组特定的主机进行通信

  • 广播

    广播用于一个主机对整个局域网上所有主机上的数据通信
    在这里插入图片描述

2.5UDP组播实现

  • 实现步骤

    • 发送端
      1. 创建发送端的Socket对象(DatagramSocket)
      2. 创建数据,并把数据打包(DatagramPacket)
      3. 调用DatagramSocket对象的方法发送数据(在单播中,这里是发给指定IP的电脑但是在组播当中,这里是发给组播地址)
      4. 释放资源
    • 接收端
      1. 创建接收端Socket对象(MulticastSocket)
      2. 创建一个箱子,用于接收数据
      3. 把当前计算机绑定一个组播地址
      4. 将数据接收到箱子中
      5. 解析数据包,并打印数据
      6. 释放资源
  • 代码实现

    // 发送端
    public class ClinetDemo {
        public static void main(String[] args) throws IOException {
            // 1. 创建发送端的Socket对象(DatagramSocket)
            DatagramSocket ds = new DatagramSocket();
            String s = "hello 组播";
            byte[] bytes = s.getBytes();
            InetAddress address = InetAddress.getByName("224.0.1.0");
            int port = 10000;
            // 2. 创建数据,并把数据打包(DatagramPacket)
            DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
            // 3. 调用DatagramSocket对象的方法发送数据(在单播中,这里是发给指定IP的电脑但是在组播当中,这里是发给组播地址)
            ds.send(dp);
            // 4. 释放资源
            ds.close();
        }
    }
    // 接收端
    public class ServerDemo {
        public static void main(String[] args) throws IOException {
            // 1. 创建接收端Socket对象(MulticastSocket)
            MulticastSocket ms = new MulticastSocket(10000);
            // 2. 创建一个箱子,用于接收数据
            DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
            // 3. 把当前计算机绑定一个组播地址,表示添加到这一组中.
            ms.joinGroup(InetAddress.getByName("224.0.1.0"));
            // 4. 将数据接收到箱子中
            ms.receive(dp);
            // 5. 解析数据包,并打印数据
            byte[] data = dp.getData();
            int length = dp.getLength();
            System.out.println(new String(data,0,length));
            // 6. 释放资源
            ms.close();
        }
    }
    
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.创建DatagramPacket数据包对象
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length);

        //4.接收数据
        ms.receive(dp);

        //5.解析数据
        byte[] data = dp.getData();
        int len = dp.getLength();
        String ip = dp.getAddress().getHostAddress();
        String name = dp.getAddress().getHostName();

        System.out.println("ip为:" + ip + ",主机名为:" + name + "的人,发送了数据:" + new String(data, 0, len));

        //6.释放资源
        ms.close();
    }
}
public class SendMessageDemo {
    public static void main(String[] args) throws IOException {
        /*
            组播发送端代码
         */

        //创建MulticastSocket对象
        MulticastSocket ms = new MulticastSocket();

        //创建DatagramPacket对象
        String s = "你好,你好!";
        byte[] bytes = s.getBytes();
        InetAddress address = InetAddress.getByName("224.0.0.1");
        int port = 10000;

        DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);

        //调用MulticastSocket发送数据方法发送数据
        ms.send(dp);

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

2.6UDP广播实现

  • 实现步骤

    • 发送端
      1. 创建发送端Socket对象(DatagramSocket)
      2. 创建存储数据的箱子,将广播地址封装进去
      3. 发送数据
      4. 释放资源
    • 接收端
      1. 创建接收端的Socket对象(DatagramSocket)
      2. 创建一个数据包,用于接收数据
      3. 调用DatagramSocket对象的方法接收数据
      4. 解析数据包,并把数据在控制台显示
      5. 关闭接收端
  • 代码实现

    // 发送端
    public class ClientDemo {
        public static void main(String[] args) throws IOException {
          	// 1. 创建发送端Socket对象(DatagramSocket)
            DatagramSocket ds = new DatagramSocket();
    		// 2. 创建存储数据的箱子,将广播地址封装进去
            String s = "广播 hello";
            byte[] bytes = s.getBytes();
            InetAddress address = InetAddress.getByName("255.255.255.255");
            int port = 10000;
            DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
    		// 3. 发送数据
            ds.send(dp);
    		// 4. 释放资源
            ds.close();
        }
    }
    
    // 接收端
    public class ServerDemo {
        public static void main(String[] args) throws IOException {
            // 1. 创建接收端的Socket对象(DatagramSocket)
            DatagramSocket ds = new DatagramSocket(10000);
            // 2. 创建一个数据包,用于接收数据
            DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
            // 3. 调用DatagramSocket对象的方法接收数据
            ds.receive(dp);
            // 4. 解析数据包,并把数据在控制台显示
            byte[] data = dp.getData();
            int length = dp.getLength();
            System.out.println(new String(data,0,length));
            // 5. 关闭接收端
            ds.close();
        }
    }
    

3. TCP通信程序

在这里插入图片描述
在这里插入图片描述

3.1 TCP 发送数据

  • Java中的TCP通信

    • Java对基于TCP协议的的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信。
    • Java为客户端提供了Socket类,为服务器端提供了ServerSocket类
  • 构造方法

    方法名说明
    Socket(InetAddress address,int port)创建流套接字并将其连接到指定IP指定端口号
    Socket(String host, int port)创建流套接字并将其连接到指定主机上的指定端口号
  • 相关方法

    方法名说明
    InputStream getInputStream()返回此套接字的输入流
    OutputStream getOutputStream()返回此套接字的输出流
  • 示例代码

public class Client {
    public static void main(String[] args) throws IOException {
        //TCP协议,发送数据

        //1.创建Socket对象
        //细节:在创建对象的同时会连上服务端
        //     如果连接不上,代码会报错
        Socket socket = new Socket("127.0.0.1", 1000);

        //2.可以从连接通道中获取输出流
        OutputStream os = socket.getOutputStream();
        //写出数据
        os.write("你好,你好?".getBytes());

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

3.2TCP 接收数据

  • 构造方法

    方法名说明
    ServletSocket(int port)创建绑定到指定端口的服务器套接字
  • 相关方法

    方法名说明
    Socket accept()监听要连接到此的套接字并接受它
  • 注意事项

    1. accept方法是阻塞的,作用就是等待客户端连接
    2. 客户端创建对象并连接服务器,此时是通过三次握手协议,保证跟服务器之间的连接
    3. 针对客户端来讲,是往外写的,所以是输出流
      针对服务器来讲,是往里读的,所以是输入流
    4. read方法也是阻塞的
    5. 客户端在关流的时候,还多了一个往服务器写结束标记的动作
    6. 最后一步断开连接,通过四次挥手协议保证连接终止
  • 三次握手和四次挥手

    • 三次握手

      在这里插入图片描述

    • 四次挥手
      在这里插入图片描述

  • 示例代码

public class Server {
    public static void main(String[] args) throws IOException {
        //TCP协议,接收数据

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

        //2.监听客户端的连接
        Socket socket = ss.accept();

        //3.从连接通道中获取输入流读取数据
        InputStream is = socket.getInputStream();
        int b;
        while ((b = is.read()) != -1) {
            System.out.print((char) b);
        }
        System.out.println();

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

3.3 TCP程序练习(传输中文)

发送端:

public class Client {
    public static void main(String[] args) throws IOException {
        //TCP协议,发送数据

        //1.创建Socket对象
        //细节:在创建对象的同时会连接服务端
        //      如果连接不上,代码会报错
        Socket socket = new Socket("127.0.0.1",10000);


        //2.可以从连接通道中获取输出流
        OutputStream os = socket.getOutputStream();
        //写出数据
        os.write("你好你好".getBytes());//12字节

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

    }
}

接收端:

public class Server {
    public static void main(String[] args) throws IOException {
        //TCP协议,接收数据

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

        //2.监听客户端的链接
        Socket socket = ss.accept();

        //3.从连接通道中获取输入流读取数据
        InputStream is = socket.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(isr);

        // 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();

    }
}

在这里插入图片描述

4. 综合练习

练习一:多发多收

需求:

​ 客户端:多次发送数据

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

代码示例:

public class Client {
    public static void main(String[] args) throws IOException {
        //客户端:多次发送数据
        //服务器:接收多次接收数据,并打印

        //1. 创建Socket对象并连接服务端
        Socket socket = new Socket("127.0.0.1",10000);

        //2.写出数据
        Scanner sc = new Scanner(System.in);
        OutputStream os = socket.getOutputStream();

        while (true) {
            System.out.println("请输入您要发送的信息");
            String str = sc.nextLine();
            if("886".equals(str)){
                break;
            }
            os.write(str.getBytes());
        }
        //3.释放资源
        socket.close();
    }
}
public class Server {
    public static void main(String[] args) throws IOException {
        //客户端:多次发送数据
        //服务器:接收多次接收数据,并打印

        //1.创建对象绑定10000端口
        ServerSocket ss = new ServerSocket(10000);

        //2.等待客户端来连接
        Socket socket = ss.accept();

        //3.读取数据
        InputStreamReader isr = new InputStreamReader(socket.getInputStream());
        int b;
        while ((b = isr.read()) != -1){
            System.out.print((char)b);
        }

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

练习二:接收并反馈

  • 案例需求

    客户端:发送数据,接受服务器反馈

    服务器:收到消息后给出反馈

  • 案例分析

    • 客户端创建对象,使用输出流输出数据
    • 服务端创建对象,使用输入流接受数据
    • 服务端使用输出流给出反馈数据
    • 客户端使用输入流接受反馈数据
  • 代码实现

    // 客户端
    public class ClientDemo {
        public static void main(String[] args) throws IOException {
            Socket socket = new Socket("127.0.0.1",10000);
    
            OutputStream os = socket.getOutputStream();
            os.write("hello".getBytes());
           // os.close();如果在这里关流,会导致整个socket都无法使用
            socket.shutdownOutput();//仅仅关闭输出流.并写一个结束标记,对socket没有任何影响
            
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String line;
            while((line = br.readLine())!=null){
                System.out.println(line);
            }
            br.close();
            os.close();
            socket.close();
        }
    }
    // 服务器
    public class ServerDemo {
        public static void main(String[] args) throws IOException {
            ServerSocket ss = new ServerSocket(10000);
    
            Socket accept = ss.accept();
    
            InputStream is = accept.getInputStream();
            int b;
            while((b = is.read())!=-1){
                System.out.println((char) b);
            }
    
            System.out.println("看看我执行了吗?");
    
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
            bw.write("你谁啊?");
            bw.newLine();
            bw.flush();
    
            bw.close();
            is.close();
            accept.close();
            ss.close();
        }
    }
    
// 客户端
public class Client {
    public static void main(String[] args) throws IOException {
        //客户端:发送一条数据,接受服务器反馈的消息并打印
        //服务器:收到数据并打印,再给客户端反馈消息

        //1.创建Socket对象并连接服务器
        Socket socket = new Socket("127.0.0.1", 1000);

        //2.写出数据
        String str = "见到你很高兴";
        OutputStream os = socket.getOutputStream();
        os.write(str.getBytes());

        //写一个结束标记
        // os.close();如果在这里关流,会导致整个socket都无法使用
        socket.shutdownOutput();//仅仅关闭输出流.并写一个结束标记,对socket没有任何影响

        //3.接收服务端回写的数据
        InputStream is = socket.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        int b;
        while ((b = isr.read()) != -1) {
            System.out.print((char) b);
        }

        //释放资源
        os.close();
        socket.close();
    }
}
// 服务器
public class Server {
    public static void main(String[] args) throws IOException {
        //客户端:发送一条数据,接受服务器反馈的消息并打印
        //服务器:收到数据并打印,再给客户端反馈消息

        //1.创建对象并绑定1000端口
        ServerSocket ss = new ServerSocket(1000);

        //2.等待客户端连接
        Socket socket = ss.accept();

        //3.socket中获取输入流读取数据
        InputStream is = socket.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        int b;
        //细节:
        //read方法会从通道中读取数据
        //但是,需要一个结束标记,此处的循环才会停止
        //否则,程序就会一直停在read方法这里,等待读取下面的数据
        while ((b = isr.read()) != -1) {
            System.out.print((char) b);
        }

        //4.回写数据
        String str = "到底有多开心";
        OutputStream os = socket.getOutputStream();
        os.write(str.getBytes());

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

在这里插入图片描述

练习三:上传练习(TCP协议)

在这里插入图片描述

  • 案例需求

    客户端:数据来自于本地文件,接收服务器反馈

    服务器:接收到的数据写入本地文件,给出反馈

  • 案例分析

    • 创建客户端对象,创建输入流对象指向文件,每读一次数据就给服务器输出一次数据,输出结束后使用shutdownOutput()方法告知服务端传输结束
    • 创建服务器对象,创建输出流对象指向文件,每接受一次数据就使用输出流输出到文件中,传输结束后。使用输出流给客户端反馈信息
    • 客户端接受服务端的回馈信息
  • 相关方法

    方法名说明
    void shutdownInput()将此套接字的输入流放置在“流的末尾”
    void shutdownOutput()禁止用此套接字的输出流
  • 代码实现

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

        //1.创建Socket对象,并连接服务器
        Socket socket = new Socket("127.0.0.1", 1000);

        //2.读取本地文件中的数据,并写到服务器当中
        //缓冲流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day30-mysocketnet\\clientdir\\a.jpg"));
        OutputStream os = socket.getOutputStream();
        BufferedOutputStream bos = new BufferedOutputStream(os);
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }

        //网服务器写出结束标记
        socket.shutdownOutput();

        //3.接收服务器的回写数据
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String line = br.readLine();
        System.out.println("line = " + line);

        //4.释放资源
        socket.close();
    }
}
public class Server {
    public static void main(String[] args) throws IOException {
        //客户端:将本地文件上传到服务器,接收服务器的反馈。
        //服务器:接收客户端上传的文件,上传完毕之后给出反馈。

        //1.创建ServerSocket对象,并绑定端口1000
        ServerSocket ss = new ServerSocket(1000);

        //2.等待客户端来连接
        Socket socket = ss.accept();

        //3.读取数据并保存到本地文件中
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day30-mysocketnet\\serverdir\\a.jpg"));
        int len;
        byte[] bytes = new byte[1024];
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }
        bos.close();

        //4.回写数据
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write("上传成功");
        bw.newLine();//换行
        bw.flush();

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

练习四:TCP通信练习----上传文件(文件名重复问题)

 ```java

public class UUIDTest {
public static void main(String[] args) {
String str = UUID.randomUUID().toString().replace(“-”, “”);
System.out.println(str);//9f15b8c356c54f55bfcb0ee3023fce8a
}
}
```

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


        //1. 创建Socket对象,并连接服务器
        Socket socket = new Socket("127.0.0.1",10000);

        //2.读取本地文件中的数据,并写到服务器当中
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("mysocketnet\\clientdir\\a.jpg"));
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1){
            bos.write(bytes,0,len);
        }

        //往服务器写出结束标记
        socket.shutdownOutput();


        //3.接收服务器的回写数据
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String line = br.readLine();
        System.out.println(line);


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

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


        //1.创建对象并绑定端口
        ServerSocket ss = new ServerSocket(10000);

        //2.等待客户端来连接
        Socket socket = ss.accept();

        //3.读取数据并保存到本地文件中
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        String name = UUID.randomUUID().toString().replace("-", "");
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("mysocketnet\\serverdir\\" + name + ".jpg"));
        int len;
        byte[] bytes = new byte[1024];
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }
        bos.close();
        
        //4.回写数据
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write("上传成功");
        bw.newLine();
        bw.flush();

        //5.释放资源
        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",10000);

        //2.读取本地文件中的数据,并写到服务器当中
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("mysocketnet\\clientdir\\a.jpg"));
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1){
            bos.write(bytes,0,len);
        }

        //往服务器写出结束标记
        socket.shutdownOutput();


        //3.接收服务器的回写数据
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String line = br.readLine();
        System.out.println(line);


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

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


        //1.创建对象并绑定端口
        ServerSocket ss = new ServerSocket(10000);

        while (true) {
            //2.等待客户端来连接
            Socket socket = ss.accept();

            //开启一条线程
            //一个用户就对应服务端的一条线程
            new Thread(new MyRunnable(socket)).start();
        }

    }
}


public class MyRunnable implements Runnable{

    Socket socket;

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

    @Override
    public void run() {
        try {
            //3.读取数据并保存到本地文件中
            BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
            String name = UUID.randomUUID().toString().replace("-", "");
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("mysocketnet\\serverdir\\" + name + ".jpg"));
            int len;
            byte[] bytes = new byte[1024];
            while ((len = bis.read(bytes)) != -1) {
                bos.write(bytes, 0, len);
            }
            bos.close();
            
            //4.回写数据
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("上传成功");
            bw.newLine();
            bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //5.释放资源
           if(socket != null){
               try {
                   socket.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
        }
    }
}

练习六:线程池改进

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


        //1. 创建Socket对象,并连接服务器
        Socket socket = new Socket("127.0.0.1",10000);

        //2.读取本地文件中的数据,并写到服务器当中
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("mysocketnet\\clientdir\\a.jpg"));
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1){
            bos.write(bytes,0,len);
        }

        //往服务器写出结束标记
        socket.shutdownOutput();


        //3.接收服务器的回写数据
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String line = br.readLine();
        System.out.println(line);


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

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


        //创建线程池对象
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                3,//核心线程数量
                16,//线程池总大小
                60,//空闲时间
                TimeUnit.SECONDS,//空闲时间(单位)
                new ArrayBlockingQueue<>(2),//队列
                Executors.defaultThreadFactory(),//线程工厂,让线程池如何创建线程对象
                new ThreadPoolExecutor.AbortPolicy()//阻塞队列
        );



        //1.创建对象并绑定端口
        ServerSocket ss = new ServerSocket(10000);

        while (true) {
            //2.等待客户端来连接
            Socket socket = ss.accept();

            //开启一条线程
            //一个用户就对应服务端的一条线程
            //new Thread(new MyRunnable(socket)).start();
            pool.submit(new MyRunnable(socket));
        }

    }
}
public class MyRunnable implements Runnable{

    Socket socket;

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

    @Override
    public void run() {
        try {
            //3.读取数据并保存到本地文件中
            BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
            String name = UUID.randomUUID().toString().replace("-", "");
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("mysocketnet\\serverdir\\" + name + ".jpg"));
            int len;
            byte[] bytes = new byte[1024];
            while ((len = bis.read(bytes)) != -1) {
                bos.write(bytes, 0, len);
            }
            bos.close();
            //4.回写数据
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("上传成功");
            bw.newLine();
            bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //5.释放资源
           if(socket != null){
               try {
                   socket.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
        }
    }
}

练习七:TCP通信练习7—BS(接收浏览器的消息并打印)

客户端:不需要写。

服务器:接收数据并打印。

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

        //1.创建ServerSocket对象,并绑定1000端口
        ServerSocket ss = new ServerSocket(1000);

        //2.等待客户端来连接
        Socket socket = ss.accept();

        //3.读取数据
        InputStreamReader isr = new InputStreamReader(socket.getInputStream());
        int b;
        while ((b = isr.read()) != -1) {
            System.out.print((char) b);
        }

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

在这里插入图片描述

5:TCP通信练习8—网络编程(课后大作业)【聊天室】

5.1 项目名称

​ 利用TCP协议,做一个带有登录,注册的无界面,控制版的多人聊天室。

5.2 使用到的知识点

​ 循环,判断,集合,IO,多线程,网络编程等

5.3 准备工作

在当前模块下新建txt文件,文件中保存正确的用户名和密码

文件内容如下:

//左边是用户名
//右边是密码
zhangsan=123
lisi=1234
wangwu=12345

5.4 需求描述

① 客户端启动之后,需要连接服务端,并出现以下提示:

服务器已经连接成功
==============欢迎来到黑马聊天室================
1登录
2注册
请输入您的选择:

②选择登录之后,出现以下提示:

服务器已经连接成功
==============欢迎来到黑马聊天室================
1登录
2注册
请输入您的选择:
1
请输入用户名

③需要输入用户名和密码,输入完毕,没有按回车时,效果如下:

服务器已经连接成功
==============欢迎来到黑马聊天室================
1登录
2注册
请输入您的选择:
1
请输入用户名
zhangsan
请输入密码
123

④按下回车,提交给服务器验证

服务器会结合txt文件中的用户名和密码进行判断

根据不同情况,服务器回写三种判断提示:

服务器回写第一种提示:登录成功
服务器回写第二种提示:密码有误  
服务器回写第三种提示:用户名不存在

⑤客户端接收服务端回写的数据,根据三种情况进行不同的处理方案

登录成功的情况, 可以开始聊天,出现以下提示:

服务器已经连接成功
==============欢迎来到黑马聊天室================
1登录
2注册
请输入您的选择:
1
请输入用户名
zhangsan
请输入密码
123
1
登录成功,开始聊天
请输入您要说的话

密码错误的情况,需要重新输入,出现以下提示:

服务器已经连接成功
==============欢迎来到黑马聊天室================
1登录
2注册
请输入您的选择:
1
请输入用户名
zhangsan
请输入密码
aaa
密码输入错误
==============欢迎来到黑马聊天室================
1登录
2注册
请输入您的选择:

用户名不存在的情况,需要重新输入,出现以下提示:

服务器已经连接成功
==============欢迎来到黑马聊天室================
1登录
2注册
请输入您的选择:
1
请输入用户名
zhaoliu
请输入密码
123456
用户名不存在
==============欢迎来到黑马聊天室================
1登录
2注册
请输入您的选择:

⑥如果成功登录,就可以开始聊天,此时的聊天是群聊,一个人发消息给服务端,服务端接收到之后需要群发给所有人

提示:

​ 此时不能用广播地址,因为广播是UDP独有的

​ 服务端可以将所有用户的Socket对象存储到一个集合中

​ 当需要群发消息时,可以遍历集合发给所有的用户

​ 此时的服务端,相当于做了一个消息的转发

转发核心思想如下图所示:

在这里插入图片描述

5.5 其他要求:

用户名和密码要求:

要求1:用户名要唯一,长度:6~18位,纯字母,不能有数字或其他符号。

要求2:密码长度3~8位。第一位必须是小写或者大小的字母,后面必须是纯数字。

客户端:

拥有登录、注册、聊天功能。

① 当客户端启动之后,要求让用户选择是登录操作还是注册操作,需要循环。

  • 如果是登录操作,就输入用户名和密码,以下面的格式发送给服务端

    username=zhangsan&password=123

  • 如果是注册操作,就输入用户名和密码,以下面的格式发送给服务端

    username=zhangsan&password=123

② 登录成功之后,直接开始聊天。

服务端:

① 先读取本地文件中所有的正确用户信息。

② 当有客户端来链接的时候,就开启一条线程。

③ 在线程里面判断当前用户是登录操作还是注册操作。

④ 登录,校验用户名和密码是否正确

⑤ 注册,校验用户名是否唯一,校验用户名和密码的格式是否正确

⑥ 如果登录成功,开始聊天

⑦ 如果注册成功,将用户信息写到本地,开始聊天

控制台版的聊天室
在这里插入图片描述
在这里插入图片描述

5.6 代码实现

  • Client.java
public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 1000);
        System.out.println("服务器已经连接成功");

        //先把流获取出来
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

        while (true) {
            System.out.println("===========欢迎来到聊天室=================");
            System.out.println("1:登录");
            System.out.println("2:注册");
            System.out.println("请输入您的选择:");
            Scanner sc = new Scanner(System.in);
            String choose = sc.nextLine();
            switch (choose) {
                case "1":
                    login(socket);
                    break;
                case "2":
                    System.out.println("用户选择了注册");
                    break;
                default:
                    System.out.println("没有这个选项");
                    break;
            }
        }

    }


    public static void login(Socket socket) throws IOException {
        //获取输出流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        //往客户端写出用户名和密码
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = sc.nextLine();
        System.out.println("请输入密码:");
        String password = sc.nextLine();

        //拼接
        StringBuilder sb = new StringBuilder();
        //username=zhangsan&password=123
        sb.append("username=").append(username).append("&password=").append(password);

        //第一个写的是执行登录操作
        bw.write("login");
        bw.newLine();
        bw.flush();
        //第二个写的是用户名和密码的信息
        //往服务器写出用户名和密码
        bw.write(sb.toString());
        bw.newLine();
        bw.flush();

        //接收数据
        //获取输入流
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String message = br.readLine();
        System.out.println("message = " + message);

        //状态码 1:登录成功  2:密码有误  3:用户名不存在
        if ("1".equals(message)) {
            System.out.println("登录成功,开始聊天");
            //开一条单独的线程,专门用来接收服务端发送过来的聊天记录
            new Thread(new ClientMyRunnable(socket)).start();

            //模拟一个while(){}表示正在聊天
            //while (true){
            //System.out.println("聊天Client");
            //}
            //开始聊天
            talk2All(bw);
        } else if ("2".equals(message)) {
            System.out.println("密码输入错误");
        } else if ("3".equals(message)) {
            System.out.println("用户名不存在");
        }

    }

    //往服务器写出消息
    private static void talk2All(BufferedWriter bw) throws IOException {
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入您要说的话:");
            String message = sc.nextLine();
            //把聊天内容写给服务器
            bw.write(message);
            bw.newLine();
            bw.flush();

        }
    }

}

class ClientMyRunnable implements Runnable {
    Socket socket;

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

    @Override
    public void run() {
        //循环,循环,重复的接收
        while (true) {
            try {
                //接收服务器发送过来的聊天记录
                BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String msg = br.readLine();
                System.out.println(msg);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}
  • Server.java
public class Server {

    static ArrayList<Socket> list = new ArrayList<>();

    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(1000);

        //1.把本地文件中正确的用户名和密码获取到
        Properties prop = new Properties();
        FileInputStream fis = new FileInputStream("..\\day30-sockethomework\\servicedir\\userinfo.txt");
        prop.load(fis);
        fis.close();

        //2.接收客户端的数据进行判断
        //只要来了一个客户端,就开一条线程处理
        while (true) {
            Socket socket = ss.accept();
            System.out.println("有客户端来连接");
            new Thread(new MyRunnable(socket, prop)).start();
        }
    }
}

class MyRunnable implements Runnable {
    Socket socket;
    Properties prop;

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

    @Override
    public void run() {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while (true) {
                //第一次读的是操作
                String choose = br.readLine();
                switch (choose) {
                    case "login":
                        login(br);
                        break;
                    case "register":
                        System.out.println("用户选择了注册操作");
                        break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //获取用户登录时,传递过来的信息
    //并进行判断
    public void login(BufferedReader br) throws IOException {
        System.out.println("------------------------------------------------");
        System.out.println("用户选择了登录操作");
        String userinfo = br.readLine();
        //username=zhangsan&password=123
        System.out.println("userinfo : " + userinfo);
        String[] userInfoArr = userinfo.split("&");
        String usernameInput = userInfoArr[0].split("=")[1];
        String passwordInput = userInfoArr[1].split("=")[1];
        System.out.println("用户输入的用户名为:" + usernameInput);
        System.out.println("用户输入的密码为:" + passwordInput);

        if (prop.containsKey(usernameInput)) {
            //如果用户名存在,继续判断密码
            //String rightPassword = prop.get(usernameInput) + "";
            String rightPassword = (String) prop.get(usernameInput);

            if (rightPassword.equals(passwordInput)) {
                //提示用户登录成功,可以开始聊天
                //writeMessage2Client("登录成功,可以开始聊天");
                writeMessage2Client("1");

                System.out.println("登录成功,开始聊天");
                //登录成功的时候,就需要把客户端的连接对象Socket保存起来
                Server.list.add(socket);

                //模拟一个while(){}表示正在聊天
                //while (true){
                //System.out.println("聊天Server");
                //}

                //接收客户端发送过来的消息,并打印在控制台
                talk2All(br, usernameInput);

            } else {
                //密码输入有误
                //writeMessage2Client("密码输入有误");
                writeMessage2Client("2");
            }

        } else {
            //如果用户名不存在,直接回写
            //writeMessage2Client("用户名不存在,请先注册");
            writeMessage2Client("3");
        }

    }

    private void talk2All(BufferedReader br, String username) throws IOException {
        while (true) {
            String message = br.readLine();
            System.out.println(username + "发送过来消息:" + message);

            //群发
            for (Socket s : Server.list) {
                //s依次表示每一个客户端的连接对象
                writeMessage2Client(s, username + "发送过来消息:" + message);
            }
        }
    }

    public void writeMessage2Client(String message) throws IOException {
        //获取输出流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write(message);
        bw.newLine();
        bw.flush();
    }

    public void writeMessage2Client(Socket socket, String message) throws IOException {
        //获取输出流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write(message);
        bw.newLine();
        bw.flush();
    }

}

在这里插入图片描述

endl

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1327231.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

more的详细用法

概要&#xff1a; Linux中more的功能是分页显示文件内容 空格键显示下一屏(页)&#xff0c;回车键Enter显示下一行&#xff0c;q键退出 本篇所用系统是Ubuntu22.04 一、more filename more后面跟的是文件名&#xff0c;分页显示文件内容 二、more < filename more从…

在MacOS上Qt配置OpenCV并进行测试

一.Qt环境准备 上一篇博客我讲了如何下载配置OpenCV库&#xff0c;但是在Qt5.15.2使用OpenCV库时&#xff0c;出现了一个问题就是我下载的Qt5.15.2是x86架构的&#xff0c;不能对OpenCV库进行链接&#xff0c;而OpenCV库是arm架构的 直接使用Qt5.15.2编译链接OpenCV库链接头文件…

Leetcode—75.颜色分类【中等】

2023每日刷题&#xff08;六十五&#xff09; Leetcode—75.颜色分类 实现代码 class Solution { public:void sortColors(vector<int>& nums) {int red 0, white 0, blue 0;for(auto num: nums) {if(num 0) {red;} else if(num 1) {white;} else {blue;}}for…

1. 创建型模式 - 工厂方法模式

亦称&#xff1a; 虚拟构造函数、Virtual Constructor、Factory Method 意图 工厂方法模式是一种创建型设计模式&#xff0c; 其在父类中提供一个创建对象的方法&#xff0c; 允许子类决定实例化对象的类型。 问题 假设你正在开发一款物流管理应用。 最初版本只能处理卡车运输…

PnetLab[网络虚拟化实验平台]下载地址

Pnet是一款分组网络仿真工具实验室和EVE-NG类似&#xff0c;由于官方提供的下载地址对我大国内域网络并不友好&#xff0c;特地将资源搬运至此。 PNETLAB-5.1.2.ova 下载 访问码&#xff1a; h2or 5.3.11升级包 下载 访问码&#xff1a; quu9 PNET_4.2.10.ova 下载 访问码&…

【C语言】指针详解(二)

目录 1.指针变量类型的意义 1.1指针的解引用 1.2指针 - 整数 1.3void*指针 2.const修饰指针 2.1const修饰变量 2.2const修饰指针变量 1.指针变量类型的意义 1.1指针的解引用 指针变量的大小和类型无关&#xff0c;只要是指针变量&#xff0c;在同一个平台下&#xff0…

技术人的年终总结报告,请笑纳

背景 年底了&#xff0c;部门间&#xff0c;小组间不可避免的需要写年终总结报告。我相信很多朋友这件事肯定比较反感。认为这些东西都是表面形式&#xff0c;没有任何意义&#xff1b;亦或对于专心搞开发的人&#xff0c;对于这种报告并不擅长&#xff0c;不知道如何下手&…

openGuass:极简版安装

目录 一、openGauss简介 二、初始化安装环境 1.创建安装用户 2.修改文件句柄设置 ​3.修改SEM内核参数 4.关闭防火墙 6.禁用SELINUX 7.安装依赖软件 8.重启服务器 三、安装数据库 1.下载安装包 2.创建安装目录 3.解压安装包 4.执行安装 5.验证安装 四、gsql工具…

stm32DMA

DMA 文章目录 DMADMA简介DMA功能框图DMA请求DMA请求通道 **仲裁器** DMA 数据配置从哪里来到哪里去外设到存储器存储器到外设存储器到存储器 DMA初始化结构体讲解配置DMA TO P(外设)代码例子 DMA简介 DMA(Direct Memory Access)—直接存储器存取&#xff0c;是单片机的一个外设…

TCP/IP 传输层协议

传输层定义了主机应用程序之间端到端的连通性。传输层中最为常见的两个协议分别是传输控制协议TCP&#xff08;Transmission Control Protocol&#xff09;和用户数据包协议UDP&#xff08;User Datagram Protocol&#xff09;。 TCP协议 TCP是一种面向连接的传输层协议&#…

2023 CCF国际AIOps挑战赛决赛暨“大模型时代的AIOps”研讨会成功举办

12月16日&#xff0c;2023 CCF国际AIOps挑战赛决赛暨“大模型时代的AIOps”研讨会于北京西郊宾馆成功举办。活动吸引了来自行业内众多一流专家、学者参与&#xff0c;共同探讨和展示了他们在智能运维领域的最新研究成果和技术应用&#xff0c;为智能运维技术的学术研究、生产实…

【计算机网络】TCP心跳机制、TCP粘包问题

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多计算机网络知识专栏&#xff1a;计算机网络&#x1f525; 给大家跳段…

低代码和纯代码:双向奔赴,共创未来ing……

低代码开发是近年来迅速崛起的软件开发方法&#xff0c;让编写应用程序变得更快、更简单。有人说它是美味的膳食&#xff0c;让开发过程高效而满足&#xff0c;但也有人质疑它是垃圾食品&#xff0c;缺乏定制性与深度。你认为低代码到底是美味的膳食还是垃圾食品呢&#xff0c;…

工业缺陷检测深度学习方法综述——学习笔记(评价:这篇华科大的文章错误百出,学术一点都不严谨,别误人子弟了好吧。。。)

文章目录 摘要1 引言2 问题定义与研究现状2.1 问题定义2.2 问题难点与挑战2.2.1 数据难点2.2.2 任务挑战 2.3 研究概述 3 检测算法3.1 缺陷模式已知3.1.1 传统方法简述3.1.2 深度学习方法 3.2 缺陷模式未知3.2.1 传统方法简述3.2.2 深度学习方法(1) 基于图像相似度的方法(i) 基…

芋道前端框架上线之后发现element-ui的icon图标全部乱码

前言 最近发现线上有人反映图标全部是乱码&#xff0c;登录上去看确实乱码&#xff0c;刷新就好最后一顿搜&#xff0c;发现是sass版本不兼容导致的图标乱码问题 解决办法 1.先把sass升级到1.39.0 2.来到vue.config.js文件配置代码-如果是芋道前端框架不用配置自带 css: {lo…

I.MX6ULL_Linux_驱动篇(48)linux I2C驱动

I2C 是很常用的一个串行通信接口&#xff0c;用于连接各种外设、传感器等器件。本章我们来学习一下如何在 Linux 下开发 I2C 接口器件驱动&#xff0c;重点是学习 Linux 下的 I2C 驱动框架&#xff0c;按照指定的框架去编写 I2C 设备驱动。本章同样以 I.MX6U-ALPHA 开发板上的 …

一些错误的集合

目录 第一章、1.1&#xff09;前端找不到图片1.2&#xff09;1.3&#xff09;1.4&#xff09; 第二章、2.1&#xff09;2.2&#xff09;2.3&#xff09; 第三章、3.1&#xff09;3.2&#xff09;3.3&#xff09; 第四章、4.1&#xff09;4.2&#xff09;4.3&#xff09; 友情提…

ZKP Pasta Curves

Mina book[https://o1-labs.github.io/proof-systems/specs/pasta.html?highlightpasta#pasta-curves]学习笔记 Pasta Curves Pasta Curves is a fascinating innovation in cryptography designed by Zcash. What are the Pasta Curves The Pasta Curves are a pair of e…

走过的2023:在挑战中领悟,在仿徨中成长

转眼间就到了2023年的最后一个月&#xff0c;回顾这短暂而又有意义的一年&#xff0c;可以用12个字总结&#xff1a;在挑战中领悟&#xff0c;在仿徨中成长。这篇文章我会从技术成长、职场生活、读书感悟和个人生活等几个方面&#xff0c;总结一下过去的这一年&#xff0c;梳理…

视觉设计必备!这6款工具让你事半功倍!

视觉设计师是做什么的 不同类型的行业将聘请视觉设计师&#xff0c;工作内容可能涉及设计网站、游戏、电影和其他数字设计项目。通过以下关于视觉设计师的工作描述和薪酬描述&#xff0c;我们可以进一步了解视觉设计师。 视觉设计师职位描述&#xff1a; 遵循品牌指南&#…