Java的 网络编程

什么是网络编程

在网络通信协议下,不同计算机上运行的程序之间进行的数据传输就是网络编程

  • 应用场景:
    即使通信,网游对战,金融证券,国际贸易,邮件等等
    不管是什么场景,都是计算机和计算机之间通过网络进行数据传输

java中可以使用java.net包下的技术轻松开发出常见的网络应用程序

  • 常见的软件架构
    1.C/S:client/server 客户端/服务器
    这种需要用户本地下载客户端程序,在远程有一个服务器端程序
    例如:qq,steam等等

    2.B/S:browser/server 浏览器/服务器
    这种只需要一个浏览器,用户可以通过不同的网址去访问不同的服务器
    例如:4399,淘宝等

  • B/S架构的优缺点:
    1.不需要开发客户端,只需要页面+服务端
    2.用户不需要下载,打开浏览器就能使用
    3.如果应用过大,用户的体验就会收到影响

  • C/S架构的优缺点
    1.画面可以做到非常精美,用户体验好
    2.需要开发客户端,也需要开发服务端
    3.用户下载和更新的时候太麻烦

  • 小结
    在这里插入图片描述

网络编程三要素

1.确定对方电脑在互联网上的地址:也就是IP
2.确定接受数据的软件:也就是端口号
3.确定网络传输的规则:也就是协议

所以:IP,端口号,协议就是网络编程的三要素

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

  • 端口号:
    应用程序在设备中唯一的标识

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

  • 小结
    在这里插入图片描述

IP

IP全称:Internet Protocol,是互联网协议地址,也称Ip地址
是分配给上网设备的数字标签

通俗理解:上网设备(手机,电脑等)在网络中的地址,是唯一的

常见的IP分为:IPv4,IPv6

IPv4

全称:Internet Protocol version 4,互联网通信协议第四版
采用32位地址长度,分成4组,是32位二进制,但是由于不好记忆,所以用点分十进制表示法,把8bit分为一组,将二进制转换为10进制,然后每组之间用点区分,每组的十进制范围是0到255
在这里插入图片描述

ipv4的弊端:
总共不到有43亿个ip

IPv6

全称:Internet Protocol version 6,互联网通信协议第六版
由于互联网的蓬勃发展,IP地址的需求量愈来愈大,而IPv4的ip地址的总数是有限的,所以有了ipv6
采用128位的地址长度,分为8组

可以给地球上每一粒沙子都赋予ip

采用冒分十六进制表示法
在这里插入图片描述

目前ipv6用的不多

  • 小结:
    在这里插入图片描述

ipv4的细节

  • ipv4的地址分类形式:
    ipv4地址可以分为两类:
    一类是公网使用(万维网使用)
    还有一类是私有地址(局域网使用)

192.168开头的就是局域网ip,范围为192.168.0.0–192.168.255.255,专门为组织机构内部使用,以此来节省ip

特殊IP地址:127.0.0.1,也可以是localhost:是回送地址也称本地回环地址,也称本机IP(自己电脑的IP),永远只会寻找当前所在本机

练习的时候ip地址就写:127.0.0.1

常用的CMD命令
1.ipconfig:查看本机IP地址
2.ping:检查网络是否连通

  • 小结
    在这里插入图片描述

InetAddress的使用

此类表示互联网协议 (IP) 地址。 
IP 地址是 IP 使用的 32 位或 128 位无符号数字,
它是一种低级协议,UDP 和 TCP 协议都是在它的基础上构建的。
IP 地址的体系结构是由 RFC 790:Assigned Numbers、
 RFC 1918:Address Allocation for Private Internets、
 RFC 2365:Administratively Scoped IP Multicast 
 和 RFC 2373:IP Version 6 Addressing Architecture 定义的。
 InetAddress 的实例包含 IP 地址,
 还可能包含相应的主机名(取决于它是否用主机名构造或者是否已执行反向主机名解析)。

这个类有两个子类,Inet4Address和Inet6Address分别表示ipv4和ipv6

这个类没有对外提供构造方法,不能直接new,而是要通过静态方法getByName获取对象,getByName的底层就会你用的是ipv4还是ipv6,判断完后就会根据你用的ip返回相应的子类对象

package InetTest;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class testOne {
    public static void main(String[] args) throws UnknownHostException {
        /*
        *  static InetAddress getByName(String host) 确定主机名称的IP地址,主机名称可以是机器名称,也可以是ip地址
        *  String getHostName() 获取此 IP 地址的主机名。
        *  String getHostAddress() 返回 IP 地址字符串(以文本表现形式)。
        * */
        //获取InetAddress的对象
        //这里InetAddress的对象就是ip的对象,也可以看作是电脑的对象
        //我这里的对象就是我的电脑的对象
        InetAddress address=InetAddress.getByName("Sanchi-Artifact");
        System.out.println(address);
        //*  String getHostAddress() 返回 IP 地址字符串(以文本表现形式)。
        String ip = address.getHostAddress();
        System.out.println(ip);
        //*  String getHostName() 获取此 IP 地址的主机名。
        String name = address.getHostName();
        System.out.println(name);
        
        //Sanchi-Artifact/10.200.25.214
        //10.200.25.214
        //Sanchi-Artifact
    }
}

端口号

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

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

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

端口就可以理解成电脑往外发出数据的出口或者接受数据的入口
可以理解成在电脑上一个一个的出口或入口,例如:电脑A的微信绑定的是65533这个端口,电脑B的微信也绑定的是端口65533,那么数据就从电脑A的65533出去,再从电脑B的65533进去。当然,如果电脑B的微信绑定的是1025端口,那么数据就从电脑A的65533出去,再从电脑B的1025进去。
在这里插入图片描述

协议

计算机网络中,链接和通信的规则被称为网络通信协议
有两个模型用来数据传输,
OSI参考模型和TCP/IP参考模型(或TCP/IP协议)
在这里插入图片描述
OSI参考模型和TCP/IP参考模型(或TCP/IP协议)的差别
在这里插入图片描述
OSI参考模型
在这里插入图片描述
TCP/IP参考模型(或TCP/IP协议)
在这里插入图片描述
上图中的HTTP,FTP,TCP,ICMP等等都是协议

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

    UDP是面向无连接的通信协议
    速度快,有大小限制,一次最多发送64k,数据不安全,易丢失数据

面向无连接:在发送信息时不会判断电脑之间是否连接成功,直接发送数据,若已连接就会把数据成功发送,若未连接就不会成功发送数据(也会发送,但是不会成功),

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

    TCP协议是面向连接的通信协议
    速度慢,没有大小限制,数据安全
    面向链接:在发送信息时会判断电脑之间是否连接成功,链接成功才会发送信息,不成功不会发送信息

  • UDP的应用场景:
    在线会议,视频通话,语音通话,在线视频等

  • TCP的应用场景
    下载软件,发送文字信息,发送邮件等

UDP通信程序

  • 发送数据
    使用UDP协议发送数据的具体步骤
    1.创建发送端的DatagramSocket对象
    2.数据打包(DatagramPacket
    3.发送数据
    4.释放资源
    如果类比的话,可以类比成发送快递
    在这里插入图片描述
package InetTest;

import java.io.IOException;
import java.net.*;

public class testTwo {
    public static void main(String[] args) throws IOException {
        //1.创建发送端的DatagramSocket对象(快递公司)
        //注意:
        //1.1
        //这里创建DatagramSocket对象时可以绑定端口,
        //以后就是通过这个端口往外发送数据
        //1.2
        //如果创建DatagramSocket对象时是空参构造,那么就会在所以可以用的端口中随机一个使用
        //如果是带参构造,那么参数就是指定的端口
        DatagramSocket ds=new DatagramSocket();

        //2.打包数据(``DatagramPacket``)
        //在打包数据时有许多参数
        //2.1具体数据
        //这一部分是要发送的数据,是字节数组的形式
        String str="欧肖赶紧欧电脑";
        byte[] bytes = str.getBytes();
        //2.2要发送的数据长度
        //第二个参数就是要发送的数据长度
        int length = bytes.length;
        //2.3ip地址
        //指定要向哪台电脑或哪个ip地址发送数据
        InetAddress ia=InetAddress.getByName("127.0.0.1");
        //2.4指定端口号
        //要往哪个端口号去发送数据
        int port=10086;
        //然后创建DatagramPacket对象就是打包数据
        DatagramPacket dp=new DatagramPacket(bytes,length,ia,port);
        //发送数据
        ds.send(dp);

        //释放数据
        ds.close();
    }
}
  • 接收数据
    使用UDP协议接收数据的具体步骤
    1.创建接收端的DatagramSocket对象
    2.接收打包好的数据
    3.解析数据包
    4.释放资源
    如果类比的话,可以类比成收到快递
    在这里插入图片描述
package InetTest;

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

public class testThree {
    public static void main(String[] args) throws IOException {
        //1.创建接收端的``DatagramSocket``对象(快递公司)
        //注意
        //在接收时,一定要绑定端口,
        // 且绑定的端口一定要跟发送时打包数据的端口保持一致
        //例如,上一个程序发送数据,数据包指定的端口是10086,那么,这次接收数据的端口就要是10086
        DatagramSocket ds=new DatagramSocket(10086);

        //接收数据包
        //也是DatagramPacket对象,只不过不需要那么多参数,只要存储数据的数组和数据的长度就行了
        byte[]bytes=new byte[1024];
        //这里的bytes.length表示最大能接收的数据长度
        DatagramPacket dp=new DatagramPacket(bytes,bytes.length);
        //receive接收数据
        //该方法是阻塞的,程序在运行到这一步的时候,会在这里的等数据
        //直到发送端发送信息
        ds.receive(dp);

        //解析数据包
        //getData,得到数据
        byte[]date=dp.getData();
        //getLength,得到数据长度
        int length = dp.getLength();
        //getAddress,得到数据ip地址
        InetAddress address = dp.getAddress();
        //getPort,得到发送数据的端口号
        int port = dp.getPort();

        System.out.println("数据:"+new String(date,0,length));
        System.out.println("数据长度:"+length);
        System.out.println("数据ip地址:"+address);
        System.out.println("数据发出的端口号:"+port);

        ds.close();
        //数据:欧肖赶紧欧电脑
        //数据长度:21
        //数据ip地址:/127.0.0.1
        //数据发出的端口号:55686
    }
}

注意:要先运行接收端,在运行发送端

练习:聊天室

//接收数据
package InetTest;

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class testFour {
    public static void main(String[] args) throws IOException {
        DatagramSocket ds=new DatagramSocket();
        Scanner sc=new Scanner(System.in);
        while (true) {
            System.out.println("请输入");
            String str = sc.nextLine();
            byte[] bytes = str.getBytes();
            InetAddress ia=InetAddress.getByName("127.0.0.1");
            if(str.equals("886")){
                break;
            }
            int port=10086;

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

            ds.send(dp);
        }
        ds.close();
    }
}

//发送数据
package InetTest;

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

public class testFive {
    public static void main(String[] args) throws IOException {
        DatagramSocket ds=new DatagramSocket(10086);
        byte[]bytes=new byte[1024];
        DatagramPacket dp=new DatagramPacket(bytes,bytes.length);
        while (true) {
            ds.receive(dp);

            String name = dp.getAddress().getHostName();
            String ip = dp.getAddress().getHostAddress();
            InetAddress address = dp.getAddress();
            int port = dp.getPort();
            byte[] data = dp.getData();
            int length = dp.getLength();
            System.out.println(address+"--one");
            System.out.println(port+"--two");
            System.out.println(address+"--three");
            System.out.println(address+"--four");
        }
    }
}

UDP的三种通信方式

分别为:单播,组播和广播

  • 单播:
    就是一对一,一台电脑只给另一台电脑发送数据
    在这里插入图片描述

  • 组播
    一台电脑可以给一组电脑发送信息
    在这里插入图片描述

  • 广播
    一台电脑可以给局域网的所有电脑发送数据
    在这里插入图片描述

  • UDP的三种通信方式(代码实现)
    单播:我们以前的代码就是单播

    组播:组播地址:224.0.0.0--239.255.255.255其中,224.0.0.0到224.0.0.255为预留的组播地址

    广播:广播地址:255.255.255.255


//         组播端接收代码
 package InetTest;

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

public class testSeven {
    public static void main(String[] args) throws IOException {
        /*
        *   组播端接收代码
        * */

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

        //2.将当前本机,添加到224.0.0.1这个组播ip地址中
        InetAddress address = InetAddress.getByName("224.0.0.1");
        ms.joinGroup(address);

        //2.创建DatagramPacket对象
        byte[]bytes=new byte[1024];
        DatagramPacket dp=new DatagramPacket(bytes,bytes.length);

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

        //5.解析数据
        byte[] data = dp.getData();
        String ip = dp.getAddress().getHostAddress();
        String name = dp.getAddress().getHostName();
        int length = dp.getLength();
        System.out.println("ip:"+ip+",name:"+name+",data:"+new String(data,0,length));

        ms.close();
    }
}


//        组播发送端代码
package InetTest;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.nio.channels.MulticastChannel;

public class testSix {
    public static void main(String[] args) throws IOException {
        /*
        *   组播发送端代码
        * */

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

        //2.创建DatagramPacket对象
        String str="你是好人";
        byte[]bytes=str.getBytes();
        //注意,这里发送组播时ip地址是组播的地址
        InetAddress address = InetAddress.getByName("224.0.0.1");
        int port=10086;
        DatagramPacket dp=new DatagramPacket(bytes,bytes.length,address,port);

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

上述代码中的接收端代码可以有多个,发送端发送一个,与发送端对应的多个接收端都可以接收到信息
同理,广播也是这样,只不过广播是局域网内所有的电脑都能接收到消息

TCP通信程序

TCP通信协议是一种可靠的网络协议,他在通信的两端各建立一个Socket对象
在通信之前一定要确保链接(连接)已经建立,若没有连接,无法发送数据
通过Socket产生IO流来进行网络通信
在这里插入图片描述
这里TCP通信是通过IO流实现的数据传输,所以,要获取输入流和输出流
上图是客户端往服务器发送数据,所以,客户端到服务器是输出流,服务器读取数据是输入流

  • 客户端传输数据的步骤:
    1.创建客户端的Socket(Socket)对象与指定服务器连接
    Socket(String host,int port)
    2.获取输出流,写数据
    OutputStream getOutputStream()
    3.释放资源
    void close()

  • 服务器接收数据的步骤
    1.创建服务器端的Socket(ServerSocket)对象
    ServerSocket(int port)
    2.监听客户端连接,连接成功后返回一个Socket对象
    Socket accept()
    3.获取输入流,读取数据,并把数据显示在控制台
    InputStream getInputStream()
    4.释放资源
    void close()

package InetTest;

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

public class TCPtest {
    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();

        //3.写出数据
        os.write("你好?".getBytes());

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


package InetTest;

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

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

        //创建对象ServerSocket
        //这里绑定的端口需要与发送数据绑定的端口一致
        ServerSocket ss=new ServerSocket(10000);

        //监听客户端链接
        //这里就会判断信息的发出端和接收端是否链接,若没有链接的话,程序就会卡在这一步
        //若链接,返回值就是客户端的链接对象
        Socket socket = ss.accept();

        //从链接通道(客户端的链接对象)中获取输入流读取数据
        InputStream is = socket.getInputStream();
        int b;
        while ((b=is.read())!=-1){
            System.out.println((char) b);
        }

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

注意:上面的代码只能发送英文消息,发送中文消息会出现乱码

乱码出现的原因:由于ieda使用的是UTF-8,一个中文对应三个字节,在读取的时候是一个一个字节来进行读取,由于三个字节表示一个中文,所以,就出现了乱码

解决方法,在读取数据时使用字符流,用转换流将字节流转成字符流

package InetTest;

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

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

        //创建对象ServerSocket
        //这里绑定的端口需要与发送数据绑定的端口一致
        ServerSocket ss=new ServerSocket(10000);

        //监听客户端链接
        //这里就会判断信息的发出端和接收端是否链接,若没有链接的话,程序就会卡在这一步
        //若链接,返回值就是链接通道对象
        Socket socket = ss.accept();

        //从链接通道中获取输入流读取数据
        InputStream is = socket.getInputStream();
        InputStreamReader isr=new InputStreamReader(is);//这里是改动部位
       
        int b;
        while ((b=isr.read())!=-1){
            System.out.println((char) b);
        }

        //释放资源
        socket.close();
        ss.close();
    }
}
  • 代码细节:
    在用TCP发送或接收数据的时候,要先打开服务器端,再打开客户端

    在服务器端监听客户端的连接,一旦连接成功,就会返回一个连接通道对象,可以通过这个连接通道对象来获取客户端发送的信息(客户端信息的输出流和服务端信息的输入流都是通过来连接通道对象获取,这些IO流都建立在连接通道里面,所以在释放资源时可以不用关IO流,直接关闭连接通道)

    在这个连接的底层,有一个三次握手协议保证连接的建立

    在连接的底层还有一个四次挥手协议,可以利用这个协议断开连接,并且确保连接通道里面的数据已经处理完毕了

三次握手和四次挥手协议

  • 三次握手协议
    确保连接建立
    1.客户端向服务器发出请求,等待服务器确认
    2.服务器向客户端返回响应,告诉客户端收到请求
    3.客户端再次向服务器发出确认消息,连接建立
    在这里插入图片描述

  • 四次挥手协议
    确保连接断开,且数据处理完毕
    1.客户端向服务器发出取消连接请求
    2.服务器向客户端返回一个响应,表示收到客户端取消请求,
    3.服务器将最后的数据处理完毕,然后服务器向客户端发出确认取消消息
    4.客户端再次发送确认消息,连接取消

综合练习

在这里插入图片描述

package InetTwoTest;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

//客户端
public class testOneClient {
    public static void main(String[] args) throws IOException {
        Socket socket=new Socket("127.0.0.1",10000);

        OutputStream outputStream = socket.getOutputStream();
        Scanner sc=new Scanner(System.in);
        while (true) {
            System.out.println("请输入要说的话");
            String str = sc.nextLine();
            if(str.equals("886")){
                break;
            }
            outputStream.write(str.getBytes());
        }

        socket.close();
    }
}

package InetTwoTest;

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


//服务端
public class testOneServer {
    public static void main(String[] args) throws IOException {
        ServerSocket ss=new ServerSocket(10000);

        Socket accept = ss.accept();

        InputStreamReader isr=new InputStreamReader(accept.getInputStream());

        int b;
        while ((b=isr.read())!=-1){
            System.out.println((char) b);
        }

        accept.close();
    }
}

在这里插入图片描述

package InetTwoTest;

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

//客户端
public class testTwoClient {
    public static void main(String[] args) throws IOException {
        Socket socket=new Socket("127.0.0.1",10086);

        OutputStream os = socket.getOutputStream();
        String s="你好,尼格";
        os.write(s.getBytes());

        //这里数据发送完后,需要一个结束标记才能让服务器端的输入流结束读取,
        //进而进行数据的反馈
        socket.shutdownOutput();

        InputStreamReader isr=new InputStreamReader(socket.getInputStream());
        int b;
        while ((b=isr.read())!=-1){
            System.out.print((char) b);
        }

        socket.close();
    }
}


package InetTwoTest;

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

package InetTwoTest;

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

//服务端
public class testTwoServer {
    public static void main(String[] args) throws IOException {
        ServerSocket ss=new ServerSocket(10086);

        Socket accept = ss.accept();

        InputStreamReader isr=new InputStreamReader(accept.getInputStream());

        int b;
        //这里read方法会从通道读取数据,但是需要一个结束标记此处的循环才会停止,
        // 否则程序就会一直停在read方法这里
        while ((b=isr.read())!=-1){
            System.out.print((char)b);
        }

        OutputStream os = accept.getOutputStream();

        String s="你全家都是尼格";
        os.write(s.getBytes());

        accept.close();
    }
}

在这里插入图片描述

package InetTwoTest;

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


//客户端
public class testThreeC {
    public static void main(String[] args) throws IOException {
        Socket socket=new Socket("127.0.0.1",10000);

        FileInputStream fis=new FileInputStream("D:\\我的世界\\Client\\voiceOfShape.webp");
        BufferedInputStream bis=new BufferedInputStream(fis);

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

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

        socket.close();
    }
}


package InetTwoTest;

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


//服务器端
public class testThreeS {
    public static void main(String[] args) throws IOException {
        ServerSocket ss=new ServerSocket(10000);

        Socket accept = ss.accept();
        BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\我的世界\\Server\\a.webp"));

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

        BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
        bw.write("上传成功");
        bw.newLine();
        bw.flush();

        accept.close();
        ss.close();
    }
}
  • 文件名重复问题
    如果使用上述代码,那么上传的所有文件的文件名都是重复的
    所以要改进
    java中专门有一个类来解决这个问题,
    UUID:通用唯一标识符类

可以用类名加randomUUID来获取UUID的对象
UUID.randomUUID

由于用UUID生成的类名有-,可以用toString.replace(“-”," ");来替换-为空格

sout(UUID.randomUUID.toString.replace("-"," "))

改进的代码

package InetTwoTest;

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


//服务器端
public class testThreeS {
    public static void main(String[] args) throws IOException {
        ServerSocket ss=new ServerSocket(10000);

        Socket accept = ss.accept();
        BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
        String name = UUID.randomUUID().toString().replace("-", " ");
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\我的世界\\Server\\"+name+".webp"));

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

        BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
        bw.write("上传成功");
        bw.newLine();
        bw.flush();

        accept.close();
        ss.close();
    }
}

在这里插入图片描述

package InetTwoTest;

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


//服务器端
public class testThreeS {
    public static void main(String[] args) throws IOException {
        ServerSocket ss=new ServerSocket(10000);


        while (true) {
            //等待连接
            Socket accept = ss.accept();
            //每有一个连接就开启一条线程
            new Thread(new MyRunnable(accept)).start();
        }

    }
}

//MyRunnable
package InetTwoTest;

import java.io.*;
import java.net.Socket;
import java.util.UUID;

public class MyRunnable implements Runnable{
    Socket accept;
    public MyRunnable(Socket socket) {
        this.accept=socket;
    }

    @Override
    public void run() {
        try {
            BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
            String name = UUID.randomUUID().toString().replace("-", " ");
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\我的世界\\Server\\"+name+".webp"));

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

            BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
            bw.write("上传成功");
            bw.newLine();
            bw.flush();

            accept.close();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(accept!=null){
                try {
                    accept.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在这里插入图片描述

package InetTwoTest;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


//服务器端
public class testThreeS {
    public static void main(String[] args) throws IOException {
        ServerSocket ss=new ServerSocket(10000);

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

        while (true) {
            //等待连接
            Socket accept = ss.accept();
//            //每有一个连接就开启一条线程
//            new Thread(new MyRunnable(accept)).start();
            pool.submit(new MyRunnable(accept));
        }

    }
}

在这里插入图片描述
客户端不需要写,只需要在浏览器中输入对应的服务器和端口号,(服务器和端口号之间用:隔开),就可以了

GET / HTTP/1.1
Host: 127.0.0.1:10000
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Microsoft Edge";v="113", "Chromium";v="113", "Not-A.Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.50
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6


进程已结束,退出代码0
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值