[AppleScript] 纯文本查看 复制代码
public class TCPClient {
public static void main(String[] args) throws IOException {
// 1. 创建客户端Socket对象, 指定要连接的服务端的IP地址和端口号
// 创建Socket对象时, 会自动去连接服务端, 如果服务端没有启动, 或IP地址或端口错误, 则抛异常: java.net.ConnectException: Connection refused: connect
Socket socket = new Socket("127.0.0.1", 8888); // 127.0.0.1 表示自己电脑的IP地址
// 2. 获取网络字节输出流对象
OutputStream os = socket.getOutputStream();
// 3. 通过网络字节输出流对象发送(写)数据
os.write("你好服务器".getBytes()); // 待做: 接收服务器返回的数据 // 释放资源(关闭Socket同时会关闭流对象)
socket.close();
}
}
[AppleScript] 纯文本查看 复制代码
// TCP协议的服务端
public class TCPServer {
public static void main(String[] args) throws IOException {
// 创建服务端对象, 并指定要使用的端口号
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务端已启动..."); // 调用accept方法来等待客户端连接
Socket clientSocket = serverSocket.accept(); // 通过客户端socket对象获取网络字节输入流, 用来读取数据
InputStream is = clientSocket.getInputStream();
// 通过流读取数据, 一次读一个byte数组, 因为发送的字符串内容少, 所以一个数组读一次就够了
byte[] bytes = new byte[1024];
int len;
len = is.read(bytes);
// 将读取到的字节数组转换为字符串, 读到多少转换多少, 不要全转
String s = new String(bytes, 0, len);
//System.out.println("[服务端收到信息]:来自" + clientSocket.getInetAddress().getHostAddress());
System.out.println("[服务端收到信息]:" + s); // 通过客户端socket对象获取网络字节输出流, 用来发送数据
OutputStream os = clientSocket.getOutputStream();
os.write("收到谢谢!".getBytes()); // 关闭
clientSocket.close();
serverSocket.close();
}
}
[AppleScript] 纯文本查看 复制代码
public class TCPClient {
public static void main(String[] args) throws IOException {
// 1. 创建客户端Socket对象, 指定要连接的服务端的IP地址和端口号
// 创建Socket对象时, 会自动去连接服务端, 如果服务端没有启动, 或IP地址或端口错误, 则抛异常: java.net.ConnectException: Connection refused: connect
Socket socket = new Socket("127.0.0.1", 8888); // 127.0.0.1 表示自己电脑的IP地址
// 2. 获取网络字节输出流对象
OutputStream os = socket.getOutputStream();
// 3. 通过网络字节输出流对象发送(写)数据
os.write("你好服务器".getBytes()); // 获取网络字节输入流对象, 接收服务器返回的数据
InputStream is = socket.getInputStream();
byte[] bytes = new byte[0124];
int len;
len = is.read(bytes);
// 将读取到的字节转换为字符串
String s = new String(bytes, 0, len);
System.out.println("[客户端收到的数据]:" + s); // 释放资源(关闭Socket同时会关闭流对象)
socket.close();
}
}
[AppleScript] 纯文本查看 复制代码
public class FileUploadClient {
public static void main(String[] args) throws IOException{
/*
1. 读取磁盘文件, 发送到服务端
*/
// 创建文件字节输入流对象, 用来读取文件
FileInputStream fis = new FileInputStream("day11\\小岳岳.jpg"); // 指向要上传的图片文件
// 创建客户端Socket对象, 连接服务端的地址
Socket socket = new Socket("127.0.0.1", 9999);
// 获取网络字节输出流, 用来向服务端发送数据
OutputStream os = socket.getOutputStream();
// 边读磁盘文件, 边向服务端发送数据. 一次读写一个byte数组
byte[] bytes = new byte[1024];
int len;
while ((len = fis.read(bytes)) != -1) {
// 读到多少数据, 就写多少
os.write(bytes, 0, len);
} /*
2. 读取服务端返回的信息, 打印出来
*/
// 获取网络字节输入流, 用来读取服务端返回的上传结果
InputStream is = socket.getInputStream();
// 边读服务器返回的数据, 边转换为字符串打印. 一次读一个byte数组
// 因为bytes数组和len上面已经定义过了, 所以我们可以直接使用
while ((len = is.read(bytes)) != -1) { // 注意!! 这里是用网络输入流读, 不要把对象写错
// 读到多少转换多少
String s = new String(bytes, 0, len);
System.out.print(s);
} /*
3. 释放资源
*/
fis.close(); // 自己创建的文件流要关闭
socket.close(); // 网络流通过socket来关闭
}
}
文件上传案例: 服务器端代码实现知识点:
文件上传服务端如何接收文件进行保存, 如何返回响应
总结:
使用InputStream从客户端接收数据
使用FileOutputStream写图片
边收边写
补充:
File file = new File("d:\\upload")
file + "\\1.jpg"
d:\\upload\\1.jpg
5分钟练习: 实现服务端接收图片
需求:
编写文件上传服务器代码, 将上传的文件保存到当前模块下的upload目录中实现步骤:
1.创建一个服务器ServerSocket对象,和系统要指定的端口号
2.使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象
3.使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
4.判断"模块名\\upload"文件夹是否存在,不存在则创建
5.创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
8.使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功"
10.释放资源(FileOutputStream,Socket,ServerSocket)
代码:
// 文件上传服务端
public class FileUploadServer {
public static void main(String[] args) throws IOException {
/*
服务端接收上传数据, 写到服务端的磁盘
*/
// 创建服务端
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("文件上传服务端已启动....");
// 等待客户端连接
Socket clientSocket = serverSocket.accept(); // 创建上传文件的目录
File uploadDir = new File("day11\\upload");
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
// 获取网络字节输入流, 用来读取客户端发来的文件数据
InputStream is = clientSocket.getInputStream();
// 创建文件字节输出流, 用来向服务端磁盘写文件数据
FileOutputStream fos = new FileOutputStream(uploadDir + "\\小岳岳.jpg");
// 一次读写一个字节数组
byte[] bytes = new byte[1024];
int len;
while ((len = is.read(bytes)) != -1) {
// 读多少, 写杜少
fos.write(bytes, 0, len);
} /*
服务端向客户端回写上传成功
*/
clientSocket.getOutputStream().write("上传成功".getBytes()); /*
释放资源
*/
fos.close();
clientSocket.close();
serverSocket.close();
}
}
文件上传案例: 阻塞问题知识点:
Socket流如何发送结束标记
总结:
java.net.Socket类: TCP客户端
void shutdownOutput(): 关闭输出流, 告知服务端数据发送完毕
补充: 5分钟练习: 解决阻塞问题
需求:
修改客户端代码, 在发送完文件数据后, 增加一行代码发送结束标记: socket.shutdownOutput();
重新测试程序
代码: 文件上传案例: 优化(命名, 循环, 多线程)知识点:
目前服务端有以下问题:
1. 上传的图片因为写入文件名相同, 每次都会被覆盖, 如何解决?
2. 上传一个文件服务端就结束了, 如何让服务端不停止, 一直接收文件上传
3. 在同一个线程读大文件可能会比较慢, 能否利用多线程提高程序效率
总结:
解决方案:
1. 上传文件的命名规则: 域名+毫秒值+随机数
2. 将serverSocket.accept()直到最后的操作放入死循环中, 服务器就可以一直接收文件上传
3. 当serverSocket.accept()得到Socket客户端对象后的操作, 全都放在子线程中执行
补充: 5分钟练习: 优化服务端代码
需求:
优化服务端代码, 解决文件重名问题, 无法一直接收问题, 上传效率问题
1. 拼接的文件名, 改为: 域名+毫秒值+随机数 的方式
2. 从serverSocket.accept()开始, 直到最后释放资源的操作代码, 全都放入while(true)死循环中
3. 将serverSocket.accept()之后, 直到最后释放资源的操作代码, 全都放到子线程中执行
代码:
/*
优化服务端:
命名
循环
多线程
*/
// 文件上传服务端
public class FileUploadServer {
public static void main(String[] args) throws IOException {
/*
服务端接收上传数据, 写到服务端的磁盘
*/
// 创建服务端
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("文件上传服务端已启动...."); // 优化2: 循环接收客户端的连接
while (true) {
// 等待客户端连接
Socket clientSocket = serverSocket.accept(); // 优化3: 将赋值文件的耗时任务放在子线程中执行
new Thread(new Runnable() {
@Override
public void run() {
try {
// 创建上传文件的目录
File uploadDir = new File("day11\\upload");
if (!uploadDir.exists()) {
uploadDir.mkdirs();
} // 优化1: 随机命名
String filename = "itheima" + System.currentTimeMillis() + new Random().nextInt(999999) + ".jpg"; // 获取网络字节输入流, 用来读取客户端发来的文件数据
InputStream is = clientSocket.getInputStream();
// 创建文件字节输出流, 用来向服务端磁盘写文件数据
FileOutputStream fos = new FileOutputStream(uploadDir + "\\" + filename);
// 一次读写一个字节数组
byte[] bytes = new byte[1024];
int len;
while ((len = is.read(bytes)) != -1) {
// 读多少, 写杜少
fos.write(bytes, 0, len);
} /*
服务端向客户端回写上传成功
*/
clientSocket.getOutputStream().write("上传成功".getBytes()); /*
释放资源
*/
fos.close();
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
// serverSocket.close();
}
}
使用线程池版本:
/*
优化服务端:
命名
循环
多线程
使用线程池
*/
// 文件上传服务端
public class FileUploadServer {
public static void main(String[] args) throws IOException {
/*
服务端接收上传数据, 写到服务端的磁盘
*/
// 创建服务端
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("文件上传服务端已启动...."); // 创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(5); // 优化2: 循环接收客户端的连接
while (true) {
// 等待客户端连接
Socket clientSocket = serverSocket.accept(); // 优化3: 将赋值文件的耗时任务放在子线程中执行
executorService.submit(new Runnable() {
@Override
public void run() {
try {
// 创建上传文件的目录
File uploadDir = new File("day11\\upload");
if (!uploadDir.exists()) {
uploadDir.mkdirs();
} // 优化1: 随机命名
String filename = "itheima" + System.currentTimeMillis() + new Random().nextInt(999999) + ".jpg"; // 获取网络字节输入流, 用来读取客户端发来的文件数据
InputStream is = clientSocket.getInputStream();
// 创建文件字节输出流, 用来向服务端磁盘写文件数据
FileOutputStream fos = new FileOutputStream(uploadDir + "\\" + filename);
// 一次读写一个字节数组
byte[] bytes = new byte[1024];
int len;
while ((len = is.read(bytes)) != -1) {
// 读多少, 写杜少
fos.write(bytes, 0, len);
} /*
服务端向客户端回写上传成功
*/
clientSocket.getOutputStream().write("上传成功".getBytes()); /*
释放资源
*/
fos.close();
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
// serverSocket.close();
}
}