网络编程

1. 网络编程入门

1.1 网络编程概述

  • 计算机网络

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

  • 网络编程

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

1.2 网络编程三要素

  • IP地址

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

  • 端口

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

  • 协议

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

1.3 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.4 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("itheima");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.5 端口和协议

  • 端口

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

    • 用两个字节表示的整数,它的取值范围是065535。其中,01023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用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();}
    }
    

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

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

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

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.1TCP发送数据

  • 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",10000);//2.可以从连接通道中获取输出流OutputStream os = socket.getOutputStream();//写出数据os.write("aaa".getBytes());//3.释放资源os.close();socket.close();}
    }
    

3.2TCP接收数据

  • 构造方法

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

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

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

    • 三次握手

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TGN1yaf8-1693290826405)(/img/07_TCP%E4%B8%89%E6%AC%A1%E6%8F%A1%E6%89%8B.png)]

    • 四次挥手

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xKLkTu55-1693290826406)(img/08_TCP%E5%9B%9B%E6%AC%A1%E6%8C%A5%E6%89%8B.png)]

  • 示例代码

    public class Server {public static void main(String[] args) throws IOException {//TCP协议,接收数据//1.创建对象ServerSockerServerSocket ss = new ServerSocket(10000);//2.监听客户端的链接Socket socket = ss.accept();//3.从连接通道中获取输入流读取数据InputStream is = socket.getInputStream();int b;while ((b = is.read()) != -1){System.out.println((char) b);}//4.释放资源socket.close();ss.close();}
    }
    

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

发送端:

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.创建对象ServerSockerServerSocket 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();}
    }
    

练习三:上传练习(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",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());BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("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();}
    }
    

练习四:文件名重复

 ```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;}@Overridepublic 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;}@Overridepublic 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();}}}}
}

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

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

相关文章

ChatGPT AIGC 完成二八分析柏拉图的制作案例

我们先让ChatGPT来总结一下二八分析柏拉图的好处与优点 同样ChatGPT 也可以帮我们来实现柏拉图的制作。 效果如下: 这样的按年份进行选择的柏拉图使用前端可视化的技术就可以实现。 如HTML,JS,Echarts等,但是代码可以让ChatGPT来做,生成。 在ChatGPT中给它一个Prompt …

html5——前端笔记

html 一、html51.1、理解html结构1.2、h1 - h6 (标题标签)1.3、p (段落和换行标签)1.4、br 换行标签1.5、文本格式化1.6、div 和 span 标签1.7、img 图像标签1.8、a 超链接标签1.9、table表格标签1.9.1、表格标签1.9.2、表格结构标签1.9.3、合并单元格 1.10、列表1.10.1、ul无序…

Android studio实现水平进度条

原文 ProgressBar 用于显示某个耗时操作完成的百分比的组件称为进度条。ProgressBar默认产生圆形进度条。 实现效果图&#xff1a; MainActivity import android.os.Bundle; import android.view.View; import android.app.Activity; import android.widget.Button; import…

Python:多变量赋值

相关文章 Python专栏https://blog.csdn.net/weixin_45791458/category_12403403.html?spm1001.2014.3001.5482 Python中的赋值语句可以同时对多个变量进行对象绑定&#xff08;赋值&#xff09;&#xff0c;既可以是多变量链式赋值&#xff0c;也可以是多变量平行赋值&#x…

部署Spring Boot项目

上传jar包 之前在新建Spring Boot项目[1]使用mvn install的方式&#xff0c;已经构建出jar包。 通过scp或rz/sz&#xff0c;将该jar包上传到服务器 执行java -jar hello-0.0.1-SNAPSHOT.jar,发生如下报错&#xff1a; Exception in thread "main" java.lang.Unsuppo…

(笔记五)利用opencv进行图像几何转换

参考网站&#xff1a;https://docs.opencv.org/4.1.1/da/d6e/tutorial_py_geometric_transformations.html &#xff08;1&#xff09;读取原始图像和标记图像 import cv2 as cv import numpy as np from matplotlib import pyplot as pltpath r"D:\data\flower.jpg&qu…

Redis-监听过期key-JAVA实现方案

一、创建监听配置类 RedisListenerConfig。 import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.d…

图文详解PhPStudy安装教程

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 官方下载 请在PhPStudy官方网站下载安装文件&#xff0c;官方链接如下&#xff1a;https://m.xp.cn/linux.html&#xff1b;图示如下&#xff1a; 请下载PhPStudy安装文件…

QML与C++的交互操作

QML旨在通过C 代码轻松扩展。Qt QML模块中的类使QML对象能够从C 加载和操作&#xff0c;QML引擎与Qt元对象系统集成的本质使得C 功能可以直接从QML调用。这允许开发混合应用程序&#xff0c;这些应用程序是通过混合使用QML&#xff0c;JavaScript和C 代码实现的。除了从QML访问…

WebGIS的一些学习笔记

一、简述计算机网络的Internet 概念、网络类型分类、基本特征和功用是什么 计算机网络的Internet 概念 计算机网络是地理上分散的多台独立自主的计算机遵循约定的通讯协议&#xff0c;通过软、硬件互连以实现交互通信、资源共享、信息交换、协同工作以及在线处理等功能的系统…

LabVIEW液压支架控制系统的使用与各种配置的预测模型的比较分析

LabVIEW液压支架控制系统的使用与各种配置的预测模型的比较分析 模型预测控制在工业中应用广泛。这种方法的优点之一是在求解最优控制问题时能够明确考虑对输入和输出状态施加的约束。控制对象模型用于有限时间范围内最优控制的实时计算。所使用的数学设备允许从具有单输入和单…

12 mysql char/varchar 的数据存储

前言 这里主要是 由于之前的一个 datetime 存储的时间 导致的问题的衍生出来的探究 探究的主要内容为 int 类类型的存储, 浮点类类型的存储, char 类类型的存储, blob 类类型的存储, enum/json/set/bit 类类型的存储 本文主要 的相关内容是 char 类类型的相关数据的存储 …

电子邮件服务器

目录 一、相关知识 二、邮件服务器种类 三、邮件传输协议 四、DNS中的MX记录 五、电子邮件系统工作原理 六、配置文件相关参数 七、邮件服务器配置案例 7.1设置用户别名邮箱 7.2空壳邮件服务器 一、相关知识 1、电子邮箱系统三个组成部分 MUA&#xff08;telnet&#xff09;:邮…

JSX底层渲染机制

JSX底层渲染机制 一,.步骤 1.把我们写的jsx语法编译为虚拟DOM【virtualDOM】 虚拟DOM对象&#xff1a;框架自己内部构建的一套对象体系&#xff08;对象的相关成员都是React内部绑定的&#xff09;&#xff0c;基于这些属性描述出我们所构建视图中的DOM接的相关特征 1基于ba…

通讯软件014——分分钟学会Matrikon HDA Explorer

本文介绍如何使用Matrikon HDA Explorer工具软件进行OPC HDA通讯调试。相关软件可登录网信智汇&#xff08;wangxinzhihui.com&#xff09;下载。 1、连接OPC HDA Server数据源“Kepware.KEPServerEX HAD.V6”。 2、添加标签&#xff1a;右键点击“Kepware.KEPServerEX HAD.V6”…

Oracle-day6:over()函数

目录 一、over()开窗函数 二、无参over()的使用 三、over(partition by 列名) 四、over(order by 列名 asc/desc) 五、over(partition by 列名 order by 列名 asc|desc) 六、练习&#xff08;笔试&#xff09; 一、over()开窗函数 拓展:数据库的版本 oracle:8i 9i 10g …

信息化发展16

计算机网络 从网络的作用范围可将网络类别划分为个人局域网&#xff08; Per sona l Area Net work,PAN) &#xff1e;局域网C Local Area Net work, LAN ) &#xff1e; 城域网&#xff08; Metropoli tan Areaetwork , MAN &#xff09; 、广域网&#xff08; Wide Area Net…

王道考研数据结构

文章目录 C 环境准备官方文档环境准备在线运行VSCode 环境报错解决 绪论线性表顺序表链表错题 栈、队列和数组栈队列栈的应用之中缀转后缀特殊矩阵用数组压缩存储错题 串模式匹配之暴力和KMP 树与二叉树二叉树树和森林哈夫曼树和哈夫曼编码并查集错题 图图的基本概念图的存储及…

Python 新版本有75个内置函数,你不会不知道吧

目录 Python 内置函数 前言 属性分类 模块 module 29. format() 35. help() 对象 object 17. copyright() 18. credits() 26. exit() 44. license() 59. quit() 类 class 08. bool() 10. bytearray() 11. bytes() 14. classmethod() 16. complex() 20. dict…

记一次postgres导致cpu100%

周末想打两把训练赛&#xff0c;没想到朋友发来一个截图 我&#xff1a;嗯&#xff1f;&#xff1f;wtf 于是我上服务器看了一下日志&#xff0c;诶我超&#xff0c;还真的 查看进程详情 [rootiZ7xv7q4im4c48qen2do2bZ project]# pstree -tp postgres memory(904475)─┬─…