当前位置: 首页 > 图灵资讯 > 技术篇> java网络编程

java网络编程

来源:图灵教育
时间:2023-05-29 14:05:11

1. 网络编程入门1.1 网络编程概述

  • 计算机网络是指在网络操作系统、网络管理软件和网络通信协议的管理和协调下,通过通信线连接具有不同地理位置独立功能的多台计算机及其外部设备,实现资源共享和信息传递的计算机系统
  • 在网络通信协议下,不同计算机上运行的网络编程程序可以传输数据
1.2 网络编程三要素
  • IP地址要想让网络中的计算机相互通信,必须为每台计算机指定一个标识号,指定要接收数据的计算机和识别发送的计算机,IP地址就是这个标识号。也就是设备的标识
  • 端口网络的通信本质上是两个应用程序的通信。每台计算机都有很多应用程序,那么在网络通信中如何区分这些应用程序呢?如果IP地址可以识别网络中唯一的设备,那么端口号可以识别设备中唯一的应用程序。也就是说,应用程序的识别
  • 该协议可以通过计算机网络连接多台计算机。同一网络中的计算机在连接和通信时需要遵守一定的规则,就像道路上的汽车必须遵守交通规则一样。在计算机网络中,这些连接和通信规则被称为网络通信协议。它统一规定了数据传输格式、传输速率和传输步骤,通信双方必须同时遵守才能完成数据交换。常见的协议包括UDP协议和TCP协议
1.3 IP地址

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

  • IP地址分为两类
  • IPv4:将32bit地址分配给每个连接到网络的主机。根据TCP/IP的规定,IP地址用二进制表示,每个IP地址长32bit,即4个字节。例如,采用二进制形式的IP地址是“1.1万万 10101000 00000001 01000010,这么长的地址,处理起来太费力了。为方便使用,IP地址常写成十进制,中间使用符号“.将不同的字节分开。因此,上述IP地址可以表示为“192.168.1.66”。IP地址的表达称为“点分十进制表达”,显然比1和0更容易记住
  • IPv6:随着互联网的蓬勃发展,对知识产权地址的需求越来越大,但网络地址资源有限,使得知识产权的分配越来越紧张。为了扩大地址空间,通过IPV6重新定义地址空间,使用128个地址长度,每16个字节分为8组16进制数,解决了网络地址资源数量不足的问题
  • 常用的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.11 UDP发送数据
  • UDP通信在Java中
  • UDP协议是一种不可靠的网络协议。它在通信的两端建立了一个Socket对象,但这两个Socket只是发送和接收数据的对象。因此,基于UDP协议的通信双方没有所谓的客户端和服务器概念
  • Java提供基于UDP协议的SocketdatagramSocket类
  • 构造方法

方法名

说明

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)

为接收长度为len的数据包创建DatagramPacket

  • 相关方法

方法名

说明

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.2555);        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发送数据
  • TCP通信在Java中
  • 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. 最后一步是断开连接,确保连接通过四个挥手协议终止
  • 握手三次,挥手四次
  • 三次握手
  • 四次挥手
  • 示例代码

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();        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.创建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. 综合练习练习1:多发多收

需求:

客户端:多次发送数据:

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

代码示例:

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.创建对象绑定100000端口        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();    }}

练习3:上传练习(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();    }}

练习4:重复文件名

```java

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

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