目录
1、网络编程引言
回顾单机的IO传输,如何多个PC之间数据传输?利用网络在多个PC之间数据传输。就是Socket编程。
什么是计算机网络
是指特定的物理线路,将分散,独立的计算机或者相关设备连接起来,并通过共用的协议进行数据传送,实现资源共享.
1.1 IP地址
当一台计算机要与另一台计算机通信时,需要知道另外一台计算机的地址.互联网协议(Internet Protocol ,IP)可以用来唯一的标识互联网上的计算机.IP地址由4段用点隔开的0~255的十进制数组成例如,192.168.10. 2 .
IP地址是网络中用于区分不同计算机的数字标识,由32个二进制位组成.并将其分成4组,每组8位.32位的IP地址由于用二进制表示不便于记忆,因而采用称为点分十进制的方法表示。
例如: 192.168.1.1
目前IP协议的版本号是4(IPv4)下一个版本是IPv6。IPv4采用32位(bit)地址长度,只有大约43亿个地址(2^32)。 IPv6具有更大的地址空间,IPv6中IP地址的长度为128位,即最大地址个数为2^128。
IP地址就好像电话号码:有了某人的电话号码,你就能与他通话了。同样,有了某台主机的IP地址,你就能与这台主机通信了。
1.2 域名
由于ip地址是数字,不容易记忆.所以就将他们映射为域名(domain name)
例如: www.aaaa.cn 在互联网中有叫做域名服务器(Domain Name Server DNS)的服务器,它可以把这个域名转换为Ip地址.然后根据这个Ip地址实现通信.
在命令行中使用:
ipconfig可以查询自己的IP.
ping 尝试连接某ip地址
特殊Ip地址
127.0.0.1 本地回路地址主机名:localhost,ping这个地址可以测试网卡是否可用
1.3 端口号
计算机端口(port) 是计算机与外界通信交流的出口。计算机通过端口区分Internet的各种服务.
计算机上的每一个程序都会对应一个端口号.例如使用计算机可以进行E-Mail(邮件) WWW(浏览器) FTP(文件传输)操作. 这些程序都需要通过互联网进行数据的传输.计算机是通过端口进行的判断数据的归属,不同的程序有不同的端口.
端口是有范围限制的,端口号只有整数0-65535。
(1)公认端口(wellkonwn ports) 范围是0-1023 系统保留端口。
(2)注册端口(registered ports) 范围1024-49151松散绑定一些服务,虽
然有一些服务绑定这些端口,这些端口还可以应用于其他服务。
(3)动态/私有端口(dynamic and/)范围49152-65535 动态分配是指当一个系统进程或应用程序进程需要网络通信时,它向主机申请一个端口,主机从可用的端口号中分配 一个供它使用。当这个进程关闭时,同时也就释放了所占用的端口号。
所以, 如果程序中需要指定端口,那么尽量使用1024以上的, 1024以下基本都被系统程序占用了.
互联网协议是在互联网中从一台计算机向另一台计算机传输数据的规则,数据是以包的形式封装的。有两个和互联网一起使用的协议是UDP和TCP.
1.4 UDP
User datagram protocol (用户数据报协议) 特点: 无连接、不可靠、速度快 将数据及源和目的封装成数据包中,不需要建立连接。每个数据报的大小在限制在64k内 例如: UDP协议不能保证传输没有丢失 视频通话,即时通信,IP电话 (VoIP)电话 |
1.4 TCP
Transmission control protocol (传输控制协议) 特点: 面向连接、可靠、效率稍低 通过三次握手,建立连接,形成传输数据的通道。在连接中进行大数据量传输 例如: 因为Tcp协议能够发现丢失的传输数据并重新发送,所以适合文件传输,接收邮件 |
2、Java程序设计的网络编程
Java是通过Socket机制实现网络间的数据通信的.
Socket就是为网络服务提供的一种机制。通信的两端都有Socket。网络通信其实就是Socket间的通信。数据在两个Socket间通过IO传输。
2.1 InetAddress
既然要使用网络编程,那么Ip地址是需要经常使用的,Java提供了一个类来表示IP地址。
java.net.InetAddress类是Java的IP地址封装类,它不需要用户了解如何实现IP地址的细节。
InetAddress类没有构造方法,要创建该类的实例对象,可以通过该类的静态方法获得该对象
方法:
public static InetAddress getLocalHost() 获得本机的InetAddress对象,当查找不到本地机器的地址 时,发生UnknownHostException异常。 public static InetAddress getByName (String host) 该方法获得由host指定的InetAddress对象,host是计算机的域名(例如 gz.aaaa.cn),其作用跟IP地址一样,只不过域名标识计算机比IP标识计算机更易于记忆。如果找不到主机会发生UnknownHostException异常。 public static InetAddress[] getAllByName(String host) 使用getAllByName方法可以从DNS上得到域名对应的所有的IP.这个方法返回一个InetAddress类型的数组出错了同样会抛出UnknownException异常 |
测试方法:
2.1.1 获取本机IP地址
// 获取本机 private static void tsstLocalHost() throws UnknownHostException { // 获取本机Ip地址 InetAddress localHost = InetAddress.getLocalHost(); // 通过InetAddress对象,方法实现操作 System.out.println(localHost); // 获取Ip 十进制 String hostAddress = localHost.getHostAddress(); System.out.println(hostAddress); // 获取主机名 String hostName = localHost.getHostName(); System.out.println(hostName); // ip字节表示形式 byte[] address = localHost.getAddress(); System.out.println(Arrays.toString(address)); // Java 的字节数是有符号的,能存-128~ 127. System.out.println(Integer.toBinaryString(192)); System.out.println(Integer.toBinaryString(-64)); } |
2.1.2 根据域名获取Ip
获取www.baidu.con 域名的Ip地址。
获取www.baidu.com 域名的所有IP地址。
// 获取baidu的ip地址 private static void testBaidu() throws UnknownHostException { // 获取www.baidu.com Ip地址 String host = "www.baidu.com"; // InetAddress byName = InetAddress.getByName(host); // String hostAddress = byName.getHostAddress(); // System.out.println(hostAddress); // 61.135.169.125 // 获取www.baidu.com InetAddress[] allBynameBaidu = InetAddress.getAllByName(host); for (InetAddress i : allBynameBaidu) { System.out.println(i.getHostAddress()); } } |
2.2 Java中的UDP
需要学习使用的类:
DatagramSocket
DatagramPacket
需要建立发送端,接收端。
建立数据包。将数据存储在数据包中.
调用Socket的发送接收方法。
关闭Socket。
发送端与接收端是两个独立的运行程序。
2.2.1 UDP发送
第一步:创建Socket
需要创建Socket, 发送端不需要指定ip地址和端口, 使用本机地址发送, 会自动找到未使用的端口。
需要使用DatagramSocket此类表示用来发送和接收数据报包的套接字。
java.lang.Object java.net.DatagramSocket |
可以通过构造函数创建该Socket对象
DatagramSocket socket = new DatagramSocket(); |
第二步:创建数据包
发送时需要创建数据包如何创建数据包?使用DatagramPacket
java.lang.Object java.net.DatagramPacket 此类表示数据报包。 |
创建数据包时需要通过构造函数指定发送的数据(字节数组),数据长度(数组长度),接受方的IP地址(InteAddress类),接受方端口号(port)。
构造函数:
DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。 |
第三步:发送数据
有了Socket 有了数据,如何发送数据包?使用Socket的send方法将数据包发送出去
从此套接字发送数据报包。 |
第四步:关闭Socket
使用Socket的close方法关闭。
void close() 关闭此数据报套接字。 |
注意: 在发送端,要在数据包对象中明确目的地IP及端口。
2.2.2 UDP接收
第一步:需要创建Socket,
接收时必须指定端口号.
DatagramSocket socket = new DatagramSocket(8888); |
第二步:创建数据包,
接收时也需要创建数据包, 用来存储数据. 需要一个字节数组.
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024); 接收数据 |
第三步:接收数据
使用DatagramSocket 的receive方法接收数据.该方法需要指定数据包.
socket.receive(packet); |
第四步: 从数据包中获取数据
byte[] data = packet.getData(); |
第五步:获取数据长度
int len = packet.getLength(); |
第六步:获取发送端ip地址
packet.getInetAddress().getHostAddress(); |
第七步:获取发送端端口号
packet.getPort(); |
第八步:关闭socket
socket.close(); |
注意: 在接收端,要指定监听的端口。
2.2.3 案例一:
发送数据:使用UDP将一串数据发送出去,并使用UDP接收.要求获取到发送者的Ip地址端口号.并将信息显示出来.
public class UDPSend { public static void main(String[] args) throws SocketException, UnknownHostException, IOException { System.out.println("UDP发送端启动,准备发送数据"); // 创建Socket, DatagramSocket socket = new DatagramSocket(); // 创建数据包 String data = "你好我是UDP"; InetAddress ip = InetAddress.getByName("192.168.10.252"); DatagramPacket packet = new DatagramPacket(data.getBytes(), data.getBytes().length, ip, 50000); // 发送数据 socket.send(packet); // 关闭socket socket.close(); System.out.println("udp发送端数据发送完毕"); } } |
接收数据
public class UdpReceive { public static void main(String[] args) throws SocketException, IOException { System.out.println("这是Udp接收端,已经启动,等待接收"); // 创建Socket 接收端必须指定端口号 DatagramSocket socket = new DatagramSocket(50000); // 创建接受的数据包 byte[] byt = new byte[1024]; DatagramPacket packet = new DatagramPacket(byt, byt.length); // 接收 socket.receive(packet); // 获取发送方ip InetAddress address = packet.getAddress(); String hostAddress = address.getHostAddress(); // 获取发送方端口号 int port = packet.getPort(); // 获取数据 byte[] data = packet.getData(); // 获取数据长度 int dataLen = packet.getLength(); System.out.println("IP:" + hostAddress + "端口号:" + port + "发送了: " + new String(data, 0, dataLen)); socket.close(); System.out.println("接收端接受完毕..."); } } |
2.2.4 异常处理:
发送端:
public class UDPSend { public static void main(String[] args) { System.out.println("UDP发送端启动,准备发送数据"); // 创建Socket, DatagramSocket socket = null; try { socket = new DatagramSocket(); // 创建数据包 String data = "你好我是UDP"; InetAddress ip = InetAddress.getByName("255.255.255.255"); DatagramPacket packet = new DatagramPacket(data.getBytes(), data.getBytes().length, ip, 50000); // 发送数据 socket.send(packet); } catch (IOException e) { e.printStackTrace(); } finally { // 关闭socket socket.close(); } System.out.println("udp发送端数据发送完毕"); } } |
接收端:
public class UdpReceive { public static void main(String[] args) { System.out.println("这是Udp接收端,已经启动,等待接收"); // 创建Socket 接收端必须指定端口号 DatagramSocket socket = null; try { socket = new DatagramSocket(50000); // 创建接受的数据包 byte[] byt = new byte[1024]; DatagramPacket packet = new DatagramPacket(byt, byt.length); // 接收 socket.receive(packet); // 获取发送方ip InetAddress address = packet.getAddress(); String hostAddress = address.getHostAddress(); // 获取发送方端口号 int port = packet.getPort(); // 获取数据 byte[] data = packet.getData(); // 获取数据长度 int dataLen = packet.getLength(); System.out.println("IP:" + hostAddress + "端口号:" + port + "发送了: " + new String(data, 0, dataLen)); } catch (IOException e) { e.printStackTrace(); } finally { socket.close(); } System.out.println("接收端接受完毕..."); } } |
2.2.5 案例二:
循环发送接收
通过控制台录入用户信息,使用UDP 发送出去,另外一个UDP进行接收发送端可以持续发送信息,接收端可以持续接收信息.当发送端输入bye 结束.
发送方
public class TestUdpSend { public static void main(String[] args) { // 发送端 System.out.println("这是UDP发送端,即将发送数据"); DatagramSocket socket = null; // 创建Socket try { socket = new DatagramSocket(); BufferedReader br = new BufferedReader(new InputStreamReader( System.in)); while (true) { System.out.println("请输入发送信息:"); String message = br.readLine(); if (!"bye".equals(message)) { break; } // 准备数据包,封装数据 DatagramPacket packet = new DatagramPacket(message.getBytes(), message.getBytes().length, InetAddress.getByName("127.0.0.1"), 50000); // 发送数据 socket.send(packet); } } catch (IOException e) { e.printStackTrace(); } finally { // 关闭关闭socke socket.close(); } System.out.println("发送端数据数据发送完毕"); } } |
接收方
public class TestUdpReverse { public static void main(String[] args) { System.out.println("这是UDP接收端,准备接收数据"); // 创建Socket,指定端口 DatagramSocket socket = null; try { socket = new DatagramSocket(50000); // 创建数组 byte[] byt = new byte[1024]; // 创建packet DatagramPacket packet = new DatagramPacket(byt, byt.length); while (true) { // 接收数据 socket.receive(packet); // 获取发送发ip String ip = packet.getAddress().getHostAddress(); // 获取发送方端口 int port = packet.getPort(); // 获取数据长度 int dataLen = packet.getLength(); // 获取数据 byte[] data = packet.getData(); // 转为字符串 String mess = new String(data, 0, dataLen); System.out.println("发送方:" + ip + " 端口:" + port + " 发送了:" + mess); if ("bye".equals(mess)) { break; } } } catch (IOException e) { e.printStackTrace(); } finally { socket.close(); } } } |
2.3 Java的TCP
面向连接, 数据安全, 区分服务器端和客户端.
TCP分为Socket(客户端)和ServerSocket(服务端)
需要分别建立客户端和服务器端
客户端和服务端建立连接后,通过Socket中的IO流进行数据的传输
需要关闭关闭socket
同样,客户端与服务器端是两个独立的应用程序。
2.3.1 TCP客户端
第一步:创建客户端Socket
需要指定连接到服务器的地址和端口号, 并尝试连接
客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。
Socket socket = new Socket("192.168.1.220", 8888); |
第二步:连接成功获取输入输出流
连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通getInputStream(),getOutputStream()获取即可。
socket.getInputStream(); socket.getOuputStream(); |
第三步: 将数据写出到服务端
使用字节输出流的write() 方法 |
第四步:关闭socket
调用close方法 |
连接成功之后获取输入输出流
socket.getInputStream();
socket.getOuputStream();
获取流之后就可以通过输入输出流发送和读取数据了, 客户端的输入流连接服务端输出流, 客户端输出流连接服务端输入流
客户端案例: public class TcpClient { public static void main(String[] args) throws IOException, IOException { System.out.println("客户端启动..."); // 创建客户端 Socket socket = new Socket("127.0.0.1", 50000); // 与服务端建立连接,获取输入输出流 InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); // 将数据写出到服务端 System.out.println("客户端发送数据..."); out.write("Tcp,你好我是客户端...".getBytes()); // 关闭socket out.close(); } } |
2.3.2 TCP服务端:
第一步: 创建服务端
ServerSocket, 需要指定端口号. 客户端连接的就是这个端口.
创建ServerSocket
ServerSocket serverSocket = new ServierSocket(8888); |
第二步:和客户端建立连接
通过accept方法
Socket accept() 侦听并接受到此套接字的连接。 |
该方法会侦听是否有客户端连接,如果有建立连接,并获取客户端的Socket
也就是说服务端创建之后可以获取客户端连接, 返回一个Socket对象, 这个Socket就是和客户端连接的Socket
Socket socket = serverSocket.accept(); |
第三步: 接受客户端的数据,获取客户端的数据
服务端获取这个socket的输入输出流, 就可以和客户端发送接收数据了socket.getInputStream(); socket.getOutputStream(); |
第五步:获取客户端的ip地址和端口号
使用服务端获取的Socket 获取ip地址和端口.
InetAddress getInetAddress() 返回套接字连接的地址。 |
int getPort() 返回此套接字连接到的远程端口。 |
第四步:关闭客户端和服务端
在服务端中分别调用close方法. |
2.3.3 案例一:
客户端向服务端发送数据,服务端获取并进信息打印在控制台.
public class TcpServer { public static void main(String[] args) throws IOException { System.out.println("服务端启动:"); // 服务端,监听端口 ServerSocket server = new ServerSocket(50000); // 使用acept进入侦听状态,获取客户端数据 Socket socket = server.accept(); // 获取输入流输出流 InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); // 获取客户端ip,端口 InetAddress inetAddress = socket.getInetAddress(); String ip = inetAddress.getHostAddress(); int port = socket.getPort(); // 获取客户端数据 byte[] byt = new byte[1024]; System.out.println("服务端接受数据:"); int len = in.read(byt); String mess = new String(byt, 0, len); System.out.println("该客户端:" + ip + ":" + port + "发送了:" + mess); // 关闭客户端 socket.close(); // 关闭服务端 server.close(); } } |
2.3.4 案例二
客户端和服务端实现交互
客户端给服务端发送信息,并接收服务端的回馈信息。
例如:
首先客户端向服务器通话
客户端:你好吗?服务器 |
服务器接到信息
服务器:收到客户端消息 客户端说:你好吗?服务器 |
服务器回馈信息
服务器:收到客户端消息 客户端说:你好吗? 服务器说:我很好 |
客户端接到服务器的信息
客户端:你好吗?服务器 客户端: 收到服务器消息 服务器说: 我很好 |
客户端:
public class TcpClient { public static void main(String[] args) throws IOException, IOException { System.out.println("客户端启动..."); // 创建客户端 Socket socket = new Socket("127.0.0.1", 50000); // 与服务端建立连接,获取输入输出流 InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); BufferedReader br = new BufferedReader(new InputStreamReader( System.in)); byte[] byt = new byte[1024]; while (true) { // 将数据写出到服务端 System.out.println("客户端发送数据...");
System.out.println("你说:"); String mess = br.readLine(); System.out.println("你告诉了服务端:"); out.write(mess.getBytes()); // 获取服务端的回话 int len = in.read(byt); System.out.print("服务端告诉我了:"); System.out.println(new String(byt, 0, len)); if ("bye".equals(mess)) { break; } } // 关闭socket out.close(); } } |
服务端:
public class TcpServer { public static void main(String[] args) throws IOException { System.out.println("服务端启动:"); // 服务端,监听端口 ServerSocket server = new ServerSocket(50000); // 使用acept进入侦听状态,获取客户端数据 Socket socket = server.accept(); // 获取输入流输出流 InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); // 获取客户端ip,端口 InetAddress inetAddress = socket.getInetAddress(); String ip = inetAddress.getHostAddress(); int port = socket.getPort(); byte[] byt = new byte[1024]; BufferedReader br = new BufferedReader(new InputStreamReader( System.in)); while (true) { // 获取客户端数据 System.out.println("服务端接收数据:"); int len = in.read(byt); String mess = new String(byt, 0, len); System.out.println("客户端:" + ip + ":" + port + "告诉了服务端:" + mess); // 向客户端回话. System.out.println("你要告诉客户端:"); String str = br.readLine(); out.write(str.getBytes()); if ("bye".equals(mess)) { break; } } // 关闭客户端 socket.close(); // 关闭服务端 server.close(); } } |
2.3.5 案例三:
服务器可以客户端的连接.客户端通过键盘录入数据,发送到服务端,服务端接收到数据后,转换成大写在返回给客户端。
需求:
服务器可以客户端的连接.客户端通过键盘录入数据,发送到服务端,服务端接收到数据后,转换成大写在返回给客户端。
客户端:
public class Cilent { public static void main(String[] args) throws UnknownHostException, IOException { System.out.println("客户端启动..."); Socket socket = new Socket("127.0.0.1", 50000); InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); System.out.println("请录入:"); String mess = br.readLine(); out.write(mess.getBytes()); System.out.println("-----------获取服务器的回馈--------------"); byte[] byt = new byte[1024]; int len = in.read(byt); System.out.println(new String(byt, 0, len)); socket.close(); } } |
服务端:
public class Cilent { public static void main(String[] args) throws UnknownHostException, IOException { System.out.println("客户端启动..."); Socket socket = new Socket("127.0.0.1", 50000); InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); System.out.println("请录入:"); String mess = br.readLine(); out.write(mess.getBytes()); System.out.println("-----------获取服务器的回馈--------------"); byte[] byt = new byte[1024]; int len = in.read(byt); System.out.println(new String(byt, 0, len)); socket.close(); } } |
2.3.6 多线程服务器
服务器一般是为多个客户端同时服务的, 当每个客户端连接到服务器时, 可以开一条单独的线程处理这个连接.
服务器端:
public class Server { public static void main(String[] args) throws IOException { System.out.println("服务器启动..."); ServerSocket server = new ServerSocket(50000); while (true) { Socket socket = server.accept(); ServerRunnable serverRunnable = new ServerRunnable(socket); Thread t1 = new Thread(serverRunnable); t1.start(); } } } class ServerRunnable implements Runnable { Socket socket; ServerRunnable(Socket socket) { this.socket = socket; } @Override public void run() { try { InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); byte[] byt = new byte[1024]; int len = in.read(byt); String str = new String(byt, 0, len); System.out.println(str); System.out.println("------------服务器转大写--------------"); String upperCase = str.toUpperCase(); System.out.println(upperCase); // 转大写,写回给客户端 out.write(upperCase.getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } |
2.3.7 上传文件
分析:
先启动服务器,再启动客户端.客户端和服务端需要建立连接,服务端向客户端反馈信息.告知客户端”连接成功,请上传文件路径:
客户端:
一: 客户端需要检测文件是否存在,是否是文件.
二: 客户端将文件名,和文件的长度发给服务器,服务器以此判断文件是否存在,文件大小用以验证文件是否上传完毕.
三: 客户端根据服务器端的反馈信息,如果服务器存在该文件,上传结束,不存在开始上传.
四: 客户端开始上传, 使用字节输入流,将文件加载到输入流中,读取输入流,通过socket的输出流写到服务器.
五: 服务器:
根据客户传送的信息,判断文件是否存在如果存在不再上传,需要将结果反馈给客户端.
如果不存在,新建字节输出流,将客户端传送的数据,写到服务器中.判断服务器文件的长度和客户端文件的长度是否一致.一致上传完毕.
客户端:
public class Client { public static void main(String[] args) throws IOException { System.out.println("客户端启动"); Socket socket = new Socket("127.0.0.1", 5000); InputStream ips = socket.getInputStream(); OutputStream ops = socket.getOutputStream(); // 1 ops.write("客户端请求上传文件".getBytes()); // 4 byte[] byt = new byte[1024]; int len = 0; len = ips.read(byt); String mess = new String(byt, 0, len); System.out.println(mess); // 获取上传文件 File file = getFile(); // 获取上传文件名 mess = file.getName(); System.out.println("文件名:" + mess); // 5 ops.write(mess.getBytes()); // 7上传文件大小 ops.write(String.valueOf(file.length()).getBytes()); // 10 len = ips.read(byt); mess = new String(byt, 0, len); if ("文件存在".equals(mess)) { System.out.println("文件存在,无需上传"); return; } // 11 上传 FileInputStream fis = new FileInputStream(file); while ((len = fis.read(byt)) != -1) { ops.write(byt, 0, len); } System.out.println("文件上传完毕"); len = ips.read(byt); System.out.println("服务器说:" + new String(byt, 0, len)); ops.close(); socket.close(); } private static File getFile() { // 用户输入文件路径 Scanner sc = new Scanner(System.in); while (true) { System.out.println("请输入文件的路径名:"); String nextLine = sc.nextLine(); // 创建File File file = new File(nextLine); if (!file.exists()) { System.out.println("文件不存在"); continue; } if (file.isDirectory()) { System.out.println("不支持目录"); continue; } return file; } } } } } |
服务端:
public class Server { public static void main(String[] args) throws IOException { System.out.println("服务端启动"); ServerSocket server = new ServerSocket(5000); Socket client = server.accept(); InputStream ips = client.getInputStream(); OutputStream ops = client.getOutputStream(); // 2 byte[] byt = new byte[1024]; int len = -1; len = ips.read(byt); System.out.println(new String(byt, 0, len)); // 3 ops.write("接收请求,请上传".getBytes()); // 5 len = ips.read(byt); String fileName = new String(byt, 0, len); System.out.println("文件名:" + fileName); // 根据文件名创建File对象, File file = new File("d:\\", fileName); // 8获取文件长度 len = ips.read(byt); long length = Long.parseLong(new String(byt, 0, len)); // 9判断文件是否存在 if (file.exists() && file.length() == length) { System.out.println("文件已经存在"); ops.write("文件存在".getBytes()); return; } else { ops.write("文件不存在".getBytes()); } // 12接收 FileOutputStream fos = new FileOutputStream(file); while ((len = ips.read(byt)) != -1) { fos.write(byt, 0, len); fos.flush(); if (file.length() == length) { System.out.println("文件上传完毕..."); ops.write("文件接收完毕".getBytes()); break; } } fos.close(); client.close(); server.close(); } } |
注意: 服务器中的写出文件时,如果客户端socket没有关闭,那么读不到文件末尾,需要强制结束(判断文件长度是否一致)