当前位置: 首页 > article >正文

Java-网络编程基础

目录

一、网络编程

1.网络编程概述

2.电脑之间数据传输需要的条件(网络编程三要素)

(1)IP

(2)端口

(3)协议

①UDP协议

②TCP协议

3.常用命令

(1)IP相关

4.InetAddress类

5.UDP通信程序

(1)发送数据

(2)接收数据

(3)练习

(4)UDP的三种通信方式

(5)组播代码实现

①.组播发送

②组播接收(主要写区别了,大体一致)

(6)UDP广播代码实现

6.TCP通信程序

(1)发送数据

(2)接收数据

(3)三次握手

(4)四次挥手

(5)练习

(6)练习02

(7)服务端优化

①可多次接收客户端

②UUID类

③多线程版

⑤多线程版优化-线程池

(8)问题【待解决】


一、网络编程

1.网络编程概述

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

2.电脑之间数据传输需要的条件(网络编程三要素)

  • 确认接收端在网络中的位置-------------------------------------------------IP地址(设备在网络中的地址,是唯一的标识)

  • 确定接收端中某接收输数据软件的入口----------------------------------端口号(应用程序在设备中唯一的标识)

  • 确定网络中传输数据的规则--------------------------------------------------协议(数据在网络中传输的规则,常见的协议有UDP协议和TCP协议)

(1)IP

全称”互联网协议地址,也称IP地址。是分配给上网设备的数字标签。常见的IP分类为:ipv4和ipv6,

  • ipv4:32bit----------------------------->11000000 10101000 00000001 01000010-------------------------->为了好读使用点分十进制表示法------------------->192.168.1.66

  • ipv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,而IPv4的模式下IP的总数是有限的。采用128位地址长度,分成8组------------>位数太多不表示了---------> 为了好读使用冒分十六进制表示法----------------------->2001:0DB8:0000:0023:0008:0800:200C:417A----------------->省略前面的0------------>2001:DB8:0:23:8:800:200C:417A

ipv6特殊情况:如果计算出的16进制表示形式中间有多个连续的0------------------>FF01:0:0:0:0:0:0:1101----------------------->0位压缩表示法---------->FF01::1101

(2)端口

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

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

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

(3)协议

计算机网络中,连接和通信的规则被称为网络通信协议

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

  • 面向无连接通信协议

特点

  • 速度快

  • 有大小限制,一次最多发送64K

  • 不安全,易丢失数据

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

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

特点

  • 速度慢

  • 没有大小限制

  • 数据安全

3.常用命令

(1)IP相关
  • ipconfig:查看本机IP地址

  • ping IP地址:检查网络是否通畅

特殊IP地址127.0.0.1

是回送地址也称本地回环地址,可以代表本机的IP地址,一般用来测试使用

4.InetAddress类

为了方便我们对IP地址的获取和操作,Java提供了一个类InetAddress供我们使用

主要方法

方法名说明
static InetAddress getByName(String host)根据提供的主机名和IP地址创建InetAddress
String getHostName()获取此IP地址的主机名
String getHostAddress()返回IP地址字符串
public class InetAddressDemo {public static void main(String[] args) throws UnknownHostException {
​//获取InetAddress对象,可以传递主机名或IP地址InetAddress address = InetAddress.getByName("DESKTOP-03DC35L");
​//获得主机名final String hostName = address.getHostName();//获得IP地址final String hostAddress = address.getHostAddress();System.out.println(hostName);System.out.println(hostAddress);}
}

5.UDP通信程序

(1)发送数据
  • 找发送端------------------------创建发送端的DatagramSocket对象

  • 封装信息------------------------创建数据,并把数据打包(DatagramPacket)

  • 发送端发送信息---------------调用DatagramSocket对象的方法发送数据

  • 关闭发送端----------------------释放资源

代码实现:

public class SendingClient {public static void main(String[] args) throws IOException {//创建发送端DatagramSocket datagramSocket = new DatagramSocket();
​byte[] bytes = "发送消息".getBytes(StandardCharsets.UTF_8);InetAddress inetAddress = InetAddress.getByName("127.0.0.1");int port = 10000;//打包消息DatagramPacket datagramPacket = datagramPacket(bytes, bytes.length, inetAddress, port);
​//发送消息datagramSocket.send(datagramPacket);
​//释放资源datagramSocket.close();
​
​}
​/*** 消息打包* @param bytes 数据* @param length 数据大小,全部则为bytes.length* @param inetAddress 接收端* @param port 接收端的端口号*/private static DatagramPacket datagramPacket(byte[] bytes, int length, InetAddress inetAddress,int port) {//打包消息return new DatagramPacket(bytes,length,inetAddress,port);;}
}
(2)接收数据
  • 找接收端----------------------------------------------------------------创建接收端的DatagramSocket对象

  • 创建一个容器,用来封装接收的数据--------------------------创建容器,用于接收数据

  • 接收数据,并将数据放入到上一步创建的容器中----------调用DatagramSocket的方法接收数据并将数据放入箱子中

  • 从容器中获取数据---------------------------------------------------解析数据

  • 关闭接收端------------------------------------------------------------释放资源

代码实现:

public class ReceivingClient {public static void main(String[] args) throws IOException {//创建接收端,传入端口号,//无参数则为从随机端口接收数据DatagramSocket receive = new DatagramSocket(10000);//创建新的容器,用于接收数据//DatagramPacket(byte[] buf, int length)byte[] bytes = new byte[1024];DatagramPacket datagramPacket = getDatagramPacket(bytes);;
​//接收数据receive.receive(datagramPacket);
​//获得数据并解析byte[] data = datagramPacket.getData();int length = datagramPacket.getLength();System.out.println(new String(data,0,length));
​//释放资源receive.close();}
​private static DatagramPacket getDatagramPacket(byte[] bytes){return new DatagramPacket(bytes,bytes.length);}
​
​
}
(3)练习

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

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

发送端:

public class SendClient {public static void main(String[] args) throws IOException {//固定的数据放上面//2.4 接收端ip地址InetAddress inetAddress = InetAddress.getByName("127.0.0.1");//2.5 接收端端口号int port = 10001;
​//面向对象化//1.创建发送对象DatagramSocket send = createSendClient();
​//2.数据准备//2.1 创建Scanner对象Scanner scanner = createScanner();
​while (true){//2.2 输入要发送的消息String message = message(scanner);//2.3 数据处理str转byte[]byte[] bytes = strToBytes(message);//4.封装数据//4.1 创建封装对象DatagramPacket dataPacket = createDataPacket(bytes, bytes.length, inetAddress, port);//5.发送数据send.send(dataPacket);
​if ("886".equals(message)){break;}}
​//6.释放资源send.close();System.out.println("发送端已关闭");
​
​
​
​}
​private static DatagramPacket createDataPacket(byte[] bytes,int length,InetAddress inetAddress,int port) {return new DatagramPacket(bytes,bytes.length,inetAddress,port);}
​private static Scanner createScanner() {return new Scanner(System.in);}
​private static DatagramSocket createSendClient() throws SocketException {//创建发送端DatagramSocket send = new DatagramSocket();return send;}
​private static String message(Scanner scanner){System.out.println("请输入要发送的信息");String s = scanner.nextLine();return s;}
​private static byte[] strToBytes(String message){return message.getBytes();}
​
​
}

接收端:

public class ReceivingClient {public static void main(String[] args) throws IOException {//准备接收端对象,设置端口号DatagramSocket receiveClient = createReceivedClient(10001);
​//准备新容器,用来接收数据//1.接收数据大小DatagramPacket dataBox = getDataBox();
​while (true){//接收数据receiveClient.receive(dataBox);//从容器中拿数据,输出到控制台byte[] data = dataBox.getData();//实际接受到的数据大小int length = dataBox.getLength();
​if ("886".equals(new String(data,0,length))){System.out.println("发送端不再发送消息,接收端准备关闭");break;}else {System.out.println("实际接收到的数据大小:"+length);System.out.println(new String(data,0,length));}}
​
​
​
​//关闭接收端receiveClient.close();System.out.println("接收端已关闭");}
​private static DatagramPacket getDataBox() {byte[] bytes = new byte[1024];DatagramPacket dataBox = new DatagramPacket(bytes,bytes.length);return dataBox;}
​private static DatagramSocket createReceivedClient(int port) throws SocketException {DatagramSocket receiveClient = new DatagramSocket(port);return receiveClient;}
}
(4)UDP的三种通信方式
  • 单播----------------------------------1对1,单发送单接收

  • 组播(ipv4)----------------------1对多,单发送对一组接收

    多播(ipv6)

  • 广播----------------------------------1对所有,单发送对所有接收

(5)组播代码实现

组播地址:224.0.0.0~239.255.25.255

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

①.组播发送

和上边发送数据步骤基本一致,但发送数据略有不同

在单播中,是发给指定IP的电脑,但是在组播当中,是发给组播地址

public class SendClient {public static void main(String[] args) throws IOException {//创建对象DatagramSocket datagramSocket = new DatagramSocket();//数据准备String s = "组播测试";byte[] bytes = s.getBytes();InetAddress inetAddress = InetAddress.getByName("224.0.1.0");//封装数据DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length,inetAddress,10002);
​datagramSocket.send(datagramPacket);
​datagramSocket.close();}
}
②组播接收(主要写区别了,大体一致)
  • 组播接收端创建的是new MulticastSocket(10086);

  • 在创建容器和接收数据之间,添加一步:把当前电脑添加到这个组中

public class ReserveClient {public static void main(String[] args) throws IOException {MulticastSocket multicastSocket = new MulticastSocket(10002);
​byte[] bytes = new byte[1024];DatagramPacket packet = new DatagramPacket(bytes,bytes.length);
​//添加电脑到组中multicastSocket.joinGroup(InetAddress.getByName("224.0.1.0"));
​multicastSocket.receive(packet);
​byte[] data = packet.getData();int length = packet.getLength();System.out.println(new String(data,0,length));
​multicastSocket.close();}
}
(6)UDP广播代码实现

发送

  • 在单播中,是发给指定IP的电脑,但是在广播当中,是发给广播地址

  • 广播地址:255.255.255.255

public class SendClient {public static void main(String[] args) throws IOException {DatagramSocket datagramSocket = new DatagramSocket();String message = "广播测试";final byte[] bytes = message.getBytes();InetAddress inetAddress = InetAddress.getByName("255.255.255.255");DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length,inetAddress,10002);datagramSocket.send(datagramPacket);
​datagramSocket.close();}
}

接收

  • 跟接收一模一样,啥都不用改

6.TCP通信程序

TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象。通信之前要保证连接已经建立

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

(1)发送数据
  • 创建客户端的socket对象Socket(String host,int port),与指定服务端连接

  • 获得输出流,写数据

  • 释放资源

public class SendClient {public static void main(String[] args) throws IOException {//创建一个Socket对象,两个参数//服务器ip地址//端口号Socket socket = new Socket("127.0.0.1",10003);
​//获取输出流,写数据OutputStream outputStream = socket.getOutputStream();OutputStreamWriter osw = new OutputStreamWriter(outputStream);
​//写数据String s = "tcp发送消息";osw.write(s);//释放资源socket.close();}
}
(2)接收数据
  • 创建服务端的socket对象ServerSocket(int port)

  • 监听客户端连接,返回一个socket对象,accept()等待连接,死等(阻塞)

  • 获得输入流,读取数据,并把数据显示或解析

  • 释放资源

public class ServiceClient {public static void main(String[] args) throws IOException {//创建服务端的socket对象ServerSocketServerSocket serverSocket = new ServerSocket(10003);//等待客户端连接,连接后会返回一个socket对象Socket accept = serverSocket.accept();
​//获取输入流,读取数据InputStream inputStream = accept.getInputStream();InputStreamReader isr = new InputStreamReader(inputStream);
​int n;while ((n = isr.read()) != -1){System.out.print((char) n);}//释放资源isr.close();serverSocket.close();
​
​
​}
}

注意点:

  • accept()方法是阻塞的,作用就是等待客户端连接

  • 客户端创建对象运行后会连接服务器,如果服务器没有启动则会报ConnectException异常

  • 与服务器连接是通过三次握手协议保证的

  • 对客户端来讲,是往外写的,所以是输出流

  • 对服务端来讲,是往里读的,所以是输入流

  • IO流中read()方法也是阻塞的

  • 客户端在关流的时候,还有一个往服务器写结束标记符的动作

  • 最后与服务端断开连接,是通过四次挥手协议保证连接终止的

(3)三次握手
  • 第一次:客户端向服务器发出连接请求,等待服务器确认

  • 第二次:服务器向客户端返回一个响应,告诉客户端收到了请求

  • 第三次:客户端再次发出请求确认信息,连接建立

(4)四次挥手
  • 第一次:客户端向服务端发出取消连接的请求

  • 第二次:服务器向客户端返回一个响应,表示收到客户端请求

  • 第三次:服务器会将最后的数据进行处理,处理完成后服务器会向客户端发出确认取消的消息

  • 第四次:客户端再次发送确认取消连接的消息,服务器取消连接

(5)练习

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

服务器:接收数据,给出反馈

public class SendClient {public static void main(String[] args) throws IOException {Socket socket = new Socket("127.0.0.1",10005);
​OutputStream outputStream = socket.getOutputStream();OutputStreamWriter osw = new OutputStreamWriter(outputStream);
​osw.write("客户端请求:hello world");
​//需要写一个结束标记//注意:不能把关流操作放到这,关流完成后会导致整个socket关闭//shutdownOutput() 仅仅关闭输出流,并写一个结束标记osw.flush();socket.shutdownOutput();
​InputStreamReader isr = new InputStreamReader(socket.getInputStream());int n;while ((n = isr.read()) != -1){System.out.print((char) n);}
​
​
​isr.close();osw.close();socket.close();
​}
}
public class ServerClient {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(10005);
​Socket accept = serverSocket.accept();
​InputStream inputStream = accept.getInputStream();InputStreamReader isr = new InputStreamReader(inputStream);int n;while ((n = isr.read()) != -1){System.out.print((char) n);}
​OutputStream outputStream = accept.getOutputStream();outputStream.write("服务端响应:你是谁?".getBytes());
​
​outputStream.close();isr.close();accept.close();serverSocket.close();}}
(6)练习02

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

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

public class SendClient {public static void main(String[] args) throws IOException {
​//创建socket对象//传入地址和端口号Socket socket = new Socket("127.0.0.1",10005);
​//优化输入流的变量byte[] bytes = new byte[1024];int length;
​//创建一个File对象,获取文件名和文件路径File file = new File("src\\tcp\\socket_tcp03_2\\music");//创建一个ArrayList数组,准备保存文件名ArrayList<String> fileNameList = new ArrayList<>();//查看该目录下所有文件File[] files = file.listFiles();if (files.length != 0){for (File listFile : files) {fileNameList.add(listFile.getName());}}
​//创建一个字节输入流,准备从本地文件中读取一个作为数据写出//传入文件路径FileInputStream fis = new FileInputStream(new File(file, fileNameList.get(0)));//优化输入流BufferedInputStream bis = new BufferedInputStream(fis);//创建一个集合,用来保存读取到的数据ArrayList<byte[]> dataList = new ArrayList<>();
​
​while ((length = bis.read(bytes)) != -1){dataList.add(Arrays.copyOfRange(bytes,0,length));}
​
​//获取输出流,准备写出数据OutputStream os = socket.getOutputStream();BufferedOutputStream bos = new BufferedOutputStream(os);for (byte[] bytes1 : dataList) {bos.write(bytes1);bos.flush();}//不传输数据后,单独关闭socket的输出流socket.shutdownOutput();
​//获取输入流,准备接收服务端的反馈InputStream is = socket.getInputStream();bis = new BufferedInputStream(is);while ((length = bis.read(bytes))!= -1){System.out.print(new String(bytes,0,length));}socket.close();
​}
​
}

Server类,用来实现服务端的具体操作

public class Server {
​/*** 遍历数组并写出* @param listBytes 保存数据的集合* @param osw 输出流* @throws IOException 会抛出IO异常*/public  void traverseAndWrite(ArrayList<byte[]> listBytes, BufferedOutputStream osw) throws IOException {for (byte[] listByte : listBytes) {osw.write(listByte);}}
​/*** 输出流优化,缓冲字节输出流* @param fos 需要优化的字节流* @return 缓冲字节输出流对象*/public  BufferedOutputStream getBufferedOutputStream(FileOutputStream fos) {return new BufferedOutputStream(fos);}
​/*** 创建输出流* @param file 要写出的文件(路径+文件名)* @return 返回字节输出流对象* @throws FileNotFoundException 抛出FileNotFoundException异常*/public  FileOutputStream getFileOutputStream(File file) throws FileNotFoundException {return new FileOutputStream(file);}
​/*** 把内容写入到哪* @param folder 目录(目标文件夹)* @param fileName 文件名(目标文件名)* @param nameSuffix 文件后缀名(格式.jpg/.mp3等)* @return 返回File对象*/public  File getFile(File folder, String fileName,String nameSuffix) {return new File(folder, fileName +nameSuffix);}
​/*** 获取唯一文件名* @return 返回唯一文件名(String)*/public  String getFileName() {return UUID.randomUUID().toString().replace("-","");}
​/*** 检查目标文件夹是否存在* @param folder 目标文件夹*/public  void inspectFolder(File folder) {if (!folder.exists()){if (folder.mkdir()){System.out.println("文件夹创建成功");}}}
​/*** 设置写出路径* @param path 目标位置* @return  返回File对象*/public  File getFolder(String path) {return new File(path);}
​/*** 读取数据* @param bis 缓冲字节输入流对象* @param listBytes 存储数据的集合* @throws IOException 会抛出IOException*/public  void readData(BufferedInputStream bis, ArrayList<byte[]> listBytes) throws IOException {byte[] bytes = new byte[1024];int length;while ((length = bis.read(bytes)) != -1){listBytes.add(Arrays.copyOfRange(bytes,0,length));}}
​/*** 创建集合* @return ArrayList<byte[]>*/public  ArrayList<byte[]> getArrayList() {return new ArrayList<>();}
​/*** 优化输入流* @param inputStream 字节输入流* @return 返回缓冲字节输入流对象*/public  BufferedInputStream getBufferedInputStream(InputStream inputStream) {return new BufferedInputStream(inputStream);}
​/*** 获取服务端Socket的输入流* @param serverSocket 服务端的socket对象* @return 返回服务端Socket的字节输入流对象* @throws IOException 会抛出IOException*/public  InputStream getInputStream(Socket serverSocket) throws IOException {return serverSocket.getInputStream();}
​/*** 获取服务端Socket对象* @param ss ServerSocket* @return 返回服务端的socket对象* @throws IOException 会抛出IOException*/public Socket getServerSocket(ServerSocket ss) throws IOException {return ss.accept();}
}

服务端:

public class ServerClient {public static void main(String[] args) throws IOException {
​Server server = new Server();
​//创建ServerSocket对象,设置好端口号ServerSocket ss = new ServerSocket(10005);
​
​//获取服务端的socket对象Socket serverSocket = server.getServerSocket(ss);
​//获取输入流,准备读取数据InputStream inputStream = server.getInputStream(serverSocket);
​//输入流优化BufferedInputStream bis = server.getBufferedInputStream(inputStream);
​//创建一个集合(可选)保存接收到的数据ArrayList<byte[]> listBytes = server.getArrayList();
​//读取数据并保存到listBytes集合中server.readData(bis, listBytes);
​//设置写出路径String s = "src\\tcp\\socket_tcp03_2\\serverClient_file";File folder = server.getFolder(s);
​//检查文件夹是否存在server.inspectFolder(folder);
​//获取唯一文件名final String fileName = server.getFileName();
​//设置文件后缀名String nameSuffix = ".mp3";
​//在设置好的路径下创建名为【fileName】的空文件File file = server.getFile(folder, fileName,nameSuffix);
​//创建输出流,准备写出数据FileOutputStream fos = server.getFileOutputStream(file);//优化输出流BufferedOutputStream osw = server.getBufferedOutputStream(fos);//遍历集合并写出数据server.traverseAndWrite(listBytes, osw);
​//获取输出流,准备给反馈OutputStream os = serverSocket.getOutputStream();//优化BufferedOutputStream bos = new BufferedOutputStream(os);//写反馈bos.write("保存成功".getBytes());bos.flush();
​
​//释放资源serverSocket.close();ss.close();
​}
}

(7)服务端优化
①可多次接收客户端

使用循环虽然可以让服务器处理多个客户端请求。但是无法同时跟多个客户端进行通信。

while (true){//获取服务端的socket对象Socket serverSocket = getServerSocket(ss);
​//获取输入流,准备读取数据InputStream inputStream = getInputStream(serverSocket);
​//输入流优化BufferedInputStream bis = getBufferedInputStream(inputStream);
​//创建一个集合(可选)保存接收到的数据ArrayList<byte[]> listBytes = getArrayList();
​//读取数据并保存到listBytes集合中readData(bis, listBytes);
​//设置写出路径String s = "src\\tcp\\socket_tcp03\\serverClient_file";File folder = getFolder(s);
​//检查文件夹是否存在inspectFolder(folder);
​//获取唯一文件名final String fileName = getFileName();
​//设置文件后缀名String nameSuffix = ".mp3";
​//在设置好的路径下创建名为【fileName】的空文件File file = getFile(folder, fileName,nameSuffix);
​//创建输出流,准备写出数据FileOutputStream fos = getFileOutputStream(file);//优化输出流BufferedOutputStream osw = getBufferedOutputStream(fos);//遍历集合并写出数据traverseAndWrite(listBytes, osw);
​//获取输出流,准备给反馈OutputStream os = serverSocket.getOutputStream();//优化BufferedOutputStream bos = new BufferedOutputStream(os);//写反馈bos.write("保存成功".getBytes());
​
​//释放资源serverSocket.close();}

②UUID类

主要方法

方法名说明
static UUID randomUUID()静态工厂检索一个类型4《伪随机生成》的UUID。
String toString()返回表示此 UUIDString对象
③多线程版

使用多线程虽然可以让服务器同时处理多个客户端请求。但是资源消耗太大。

public class ServerClient {public static void main(String[] args) throws IOException {//创建ServerSocket对象,设置好端口号ServerSocket ss = new ServerSocket(10005);while (true){Socket accept = ss.accept();ThreadSocket threadSocket = new ThreadSocket(accept);new Thread(threadSocket).start();}}
}
public class ThreadSocket implements Runnable{Socket socket;
​public ThreadSocket(Socket socket) {this.socket = socket;}
​@Overridepublic void run() {//获取输入流,准备读取数据InputStream inputStream = null;try {inputStream = getInputStream(socket);} catch (IOException e) {e.printStackTrace();}
​//输入流优化BufferedInputStream bis = getBufferedInputStream(inputStream);
​//创建一个集合(可选)保存接收到的数据ArrayList<byte[]> listBytes = getArrayList();
​//读取数据并保存到listBytes集合中try {readData(bis, listBytes);} catch (IOException e) {e.printStackTrace();}
​//设置写出路径String s = "src\\tcp\\socket_tcp04\\serverClient_file";File folder = getFolder(s);
​//检查文件夹是否存在inspectFolder(folder);
​//获取唯一文件名final String fileName = getFileName();
​//设置文件后缀名String nameSuffix = ".mp3";
​//在设置好的路径下创建名为【fileName】的空文件File file = getFile(folder, fileName,nameSuffix);
​//创建输出流,准备写出数据FileOutputStream fos = null;try {fos = getFileOutputStream(file);} catch (FileNotFoundException e) {e.printStackTrace();}//优化输出流BufferedOutputStream osw = getBufferedOutputStream(fos);//遍历集合并写出数据try {traverseAndWrite(listBytes, osw);} catch (IOException e) {e.printStackTrace();}
​//获取输出流,准备给反馈OutputStream os = null;try {os = socket.getOutputStream();} catch (IOException e) {e.printStackTrace();}//优化BufferedOutputStream bos = null;if (os != null){bos = new BufferedOutputStream(os);}
​//写反馈try {if (bos!=null){bos.write("保存成功".getBytes());bos.flush();}
​} catch (IOException e) {e.printStackTrace();}
​
​//释放资源try {socket.close();} catch (IOException e) {e.printStackTrace();}
​}
​/*** 遍历数组并写出* @param listBytes 保存数据的集合* @param osw 输出流* @throws IOException 会抛出IO异常*/private  void traverseAndWrite(ArrayList<byte[]> listBytes, BufferedOutputStream osw) throws IOException {for (byte[] listByte : listBytes) {osw.write(listByte);osw.flush();}}
​/*** 输出流优化,缓冲字节输出流* @param fos 需要优化的字节流* @return 缓冲字节输出流对象*/private  BufferedOutputStream getBufferedOutputStream(FileOutputStream fos) {return new BufferedOutputStream(fos);}
​/*** 创建输出流* @param file 要写出的文件(路径+文件名)* @return 返回字节输出流对象* @throws FileNotFoundException 抛出FileNotFoundException异常*/private  FileOutputStream getFileOutputStream(File file) throws FileNotFoundException {return new FileOutputStream(file);}
​/*** 把内容写入到哪* @param folder 目录(目标文件夹)* @param fileName 文件名(目标文件名)* @param nameSuffix 文件后缀名(格式.jpg/.mp3等)* @return 返回File对象*/private  File getFile(File folder, String fileName,String nameSuffix) {return new File(folder, fileName +nameSuffix);}
​/*** 获取唯一文件名* @return 返回唯一文件名(String)*/private  String getFileName() {return UUID.randomUUID().toString().replace("-","");}
​/*** 检查目标文件夹是否存在* @param folder 目标文件夹*/private  void inspectFolder(File folder) {if (!folder.exists()){if (folder.mkdir()){System.out.println("文件夹创建成功");}}}
​/*** 设置写出路径* @param path 目标位置* @return  返回File对象*/private  File getFolder(String path) {return new File(path);}
​/*** 读取数据* @param bis 缓冲字节输入流对象* @param listBytes 存储数据的集合* @throws IOException 会抛出IOException*/private  void readData(BufferedInputStream bis, ArrayList<byte[]> listBytes) throws IOException {byte[] bytes = new byte[1024];int length;while ((length = bis.read(bytes)) != -1){listBytes.add(Arrays.copyOfRange(bytes,0,length));}}
​/*** 创建集合* @return ArrayList<byte[]>*/private  ArrayList<byte[]> getArrayList() {return new ArrayList<>();}
​/*** 优化输入流* @param inputStream 字节输入流* @return 返回缓冲字节输入流对象*/private  BufferedInputStream getBufferedInputStream(InputStream inputStream) {return new BufferedInputStream(inputStream);}
​/*** 获取服务端Socket的输入流* @param serverSocket 服务端的socket对象* @return 返回服务端Socket的字节输入流对象* @throws IOException 会抛出IOException*/private  InputStream getInputStream(Socket serverSocket) throws IOException {return serverSocket.getInputStream();}
​
}
⑤多线程版优化-线程池
public class ServerClient {public static void main(String[] args) throws IOException {//创建ServerSocket对象,设置好端口号ServerSocket ss = new ServerSocket(10005);//创建线程池
​ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(//核心线程数量5,//线程池的总数量10,//临时线程空闲时间:多长时间没有任务就销毁60,//空闲时间单位TimeUnit.MINUTES,//阻塞队列,允许多少个线程排队new ArrayBlockingQueue<>(5),//创建线程的方式Executors.defaultThreadFactory(),//任务拒绝策略new ThreadPoolExecutor.AbortPolicy());
​while (true){Socket accept = ss.accept();ThreadSocket threadSocket = new ThreadSocket(accept);poolExecutor.submit(threadSocket);final int activeCount = poolExecutor.getActiveCount();System.out.println(activeCount);}
​}
​
​
}

public class ThreadSocket implements Runnable{Socket socket;
​public ThreadSocket(Socket socket) {this.socket = socket;}
​@Overridepublic void run() {//获取输入流,准备读取数据InputStream inputStream = null;try {inputStream = getInputStream(socket);} catch (IOException e) {e.printStackTrace();}
​//输入流优化BufferedInputStream bis = getBufferedInputStream(inputStream);
​//创建一个集合(可选)保存接收到的数据ArrayList<byte[]> listBytes = getArrayList();
​//读取数据并保存到listBytes集合中try {readData(bis, listBytes);} catch (IOException e) {e.printStackTrace();}
​//设置写出路径String s = "src\\tcp\\socket_tcp04\\serverClient_file";File folder = getFolder(s);
​//检查文件夹是否存在inspectFolder(folder);
​//获取唯一文件名final String fileName = getFileName();
​//设置文件后缀名String nameSuffix = ".mp3";
​//在设置好的路径下创建名为【fileName】的空文件File file = getFile(folder, fileName,nameSuffix);
​//创建输出流,准备写出数据FileOutputStream fos = null;try {fos = getFileOutputStream(file);} catch (FileNotFoundException e) {e.printStackTrace();}//优化输出流BufferedOutputStream osw = getBufferedOutputStream(fos);//遍历集合并写出数据try {traverseAndWrite(listBytes, osw);} catch (IOException e) {e.printStackTrace();}
​//获取输出流,准备给反馈OutputStream os = null;try {os = socket.getOutputStream();} catch (IOException e) {e.printStackTrace();}//优化BufferedOutputStream bos = null;if (os != null){bos = new BufferedOutputStream(os);}
​//写反馈try {if (bos!=null){bos.write("保存成功".getBytes());bos.flush();}
​} catch (IOException e) {e.printStackTrace();}
​
​//释放资源try {socket.close();} catch (IOException e) {e.printStackTrace();}
​}
​/*** 遍历数组并写出* @param listBytes 保存数据的集合* @param osw 输出流* @throws IOException 会抛出IO异常*/private  void traverseAndWrite(ArrayList<byte[]> listBytes, BufferedOutputStream osw) throws IOException {for (byte[] listByte : listBytes) {osw.write(listByte);osw.flush();}}
​/*** 输出流优化,缓冲字节输出流* @param fos 需要优化的字节流* @return 缓冲字节输出流对象*/private  BufferedOutputStream getBufferedOutputStream(FileOutputStream fos) {return new BufferedOutputStream(fos);}
​/*** 创建输出流* @param file 要写出的文件(路径+文件名)* @return 返回字节输出流对象* @throws FileNotFoundException 抛出FileNotFoundException异常*/private  FileOutputStream getFileOutputStream(File file) throws FileNotFoundException {return new FileOutputStream(file);}
​/*** 把内容写入到哪* @param folder 目录(目标文件夹)* @param fileName 文件名(目标文件名)* @param nameSuffix 文件后缀名(格式.jpg/.mp3等)* @return 返回File对象*/private  File getFile(File folder, String fileName,String nameSuffix) {return new File(folder, fileName +nameSuffix);}
​/*** 获取唯一文件名* @return 返回唯一文件名(String)*/private  String getFileName() {return UUID.randomUUID().toString().replace("-","");}
​/*** 检查目标文件夹是否存在* @param folder 目标文件夹*/private  void inspectFolder(File folder) {if (!folder.exists()){if (folder.mkdir()){System.out.println("文件夹创建成功");}}}
​/*** 设置写出路径* @param path 目标位置* @return  返回File对象*/private  File getFolder(String path) {return new File(path);}
​/*** 读取数据* @param bis 缓冲字节输入流对象* @param listBytes 存储数据的集合* @throws IOException 会抛出IOException*/private  void readData(BufferedInputStream bis, ArrayList<byte[]> listBytes) throws IOException {byte[] bytes = new byte[1024];int length;while ((length = bis.read(bytes)) != -1){listBytes.add(Arrays.copyOfRange(bytes,0,length));}}
​/*** 创建集合* @return ArrayList<byte[]>*/private  ArrayList<byte[]> getArrayList() {return new ArrayList<>();}
​/*** 优化输入流* @param inputStream 字节输入流* @return 返回缓冲字节输入流对象*/private  BufferedInputStream getBufferedInputStream(InputStream inputStream) {return new BufferedInputStream(inputStream);}
​/*** 获取服务端Socket的输入流* @param serverSocket 服务端的socket对象* @return 返回服务端Socket的字节输入流对象* @throws IOException 会抛出IOException*/private  InputStream getInputStream(Socket serverSocket) throws IOException {return serverSocket.getInputStream();}
​
}
(8)问题【待解决】

虽然实现了多线程和重复接收的问题,但在测试的时候还有个问题,记录一下

文件写好之后,由于服务端的ServerSocket没有关闭,在服务端读取完数据并写出后,其状态是被java虚拟机使用的,故此文件是无法删除的,但能正常访问~

如图:

我传了三个文件

然后我去文件夹看一下状态

无法删除

但是可以正常听

关闭服务器

文件会正常

http://www.lryc.cn/news/2413171.html

相关文章:

  • 旅游网站设计
  • Python爬虫从入门到精通:(45)JS逆向:空中网逆向分析:js混淆_Python涛哥
  • IntelliJ IDEA 2016.2激活方法汇总
  • 程序设计竞赛推荐两本实用书给你
  • cuteftp8.3序列号
  • 使用jsmooth将jar文件转换成可在无java的环境中运行的exe文件
  • 禁漫排行榜周目公告版_华泰证券手机版下载-华泰证券涨乐财富通手机版下载V7.5.0安卓版...
  • 影音先锋云服务器,影音先锋云服务器
  • ——Diary5(Java面向对象2——多态,内部类)
  • 转载:一些射频相关的网站
  • Activity Monitor使用说明
  • Python调试器-Pdb的简介及调试命令
  • 跨时空的相遇~为她制作一个专属的QQ空间相册(附源代码)
  • 【BLOCK】Oracle 块管理常用SQL
  • 北京师范大学珠海分校论坛 http://www.bnubbs.net
  • Springboot计算机毕业设计乐途网站的设计与实现ck8f1
  • 从头到尾彻底理解KMP(2014年8月22日版)
  • 计算机论文投稿指南
  • 扫雷游戏C语言代码实现——万字长文超详细,手把手教你实现,新手也能学会
  • opencv学习篇(3)snake轮廓检测
  • 自学Java最简单快速的学习路线图,快速从入门到精通
  • winxp IIS安装,一看就明白!
  • 零基础Python速成学习计划(详细)
  • 英特尔MeeGo: Intel开放软件平台MeeGo 应用总数过万
  • Visual Studio图形调试器详细使用教程(基于DirectX11)
  • kali web渗透
  • uart、串口、COM口、USB口,ttl,rs232,rs485这几个是什么关系?
  • 证书和域名的关系?
  • 如何用asp.net制作网站
  • 创建属于你的SDK!