1.代码的书写逻辑
client:
* 1.创建Socket对象并连接服务器 * 2.写出数据 * 3.关闭释放
server:
* 1.创建对象绑定端口 * 2.等待客户端连接 * 3.读取数据 * 4.释放资源
2.返回读取完成标志socket.shutdownOutput(),否则程序将停留在read方法中死等,一直等待读取下面的数据。
// read方法从连接通道读取数据 // 但是,循环需要一个结束标志才能在这里停止 // 否则,程序将停留在read方法中,等待读取下面的数据
3.传输图片时,收到的图片显示不全,查看大小也和发送方的图片大小不一致,导致图片底部失真是因为读写的文件内容大小不一致导致的。错误是出在服务器端程序没有注意读写的同步,BufferedOutpuStream是高级流,数据必须刷新才能冲到另一端,执行socket.shutdownOutputStream()时服务器端已发送数据完毕,但shutdownOutputStream()作用仅仅是关闭当前Socket端的输出流并不会让数据冲到Socket另一端中。
方法:在数组读取时,在socket.shutdownOutputStream()前,执行bos.flush(),强行将数据冲到客户端中执行bos.flush(),强行将数据冲到客户端中。
详情:关于Socket通信传输图片底部失真问题_shutdownoutstream_paul亡命天涯的博客-CSDN博客
4.随机生成文件名
String name=UUID.randomUUID().toString().replace("-","");//随机生成文件名
5.实现多客户端时,可将服务端实现代码放进循环,但是只用循环的话,必须要等待前一个线程结束才能进行后一个线程。
while (true) {
Socket socket=ss.accept();
String name=UUID.randomUUID().toString().replace("-","");//随机生成文件名
BufferedInputStream bis=new BufferedInputStream(socket.getInputStream());
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("C:\\\\Users\\\\WZY\\\\Desktop\\\\"+name+".png"));
byte[] bytes=new byte[1024];
int len;
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("上传成功!");
bw.newLine();
bw.flush();
bw.close();
bos.close();
socket.close();
ss.close();
}
利用多线程实现多客户端
package Excise5;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
//只用循环的话,必须要等待前一个线程结束才能进行后一个线程
public class server {
public static void main(String[] args) throws IOException {
ServerSocket ss=new ServerSocket(10005);
while (true) {
Socket socket=ss.accept();
new Thread(new MyRunnable(socket)).start();
}
}
}
package Excise5;
import java.io.*;
import java.net.Socket;
import java.util.UUID;
//注:直接调用Thread类或Runnable类对象的run()方法是无法启动线程的,这只是一个简单的方法调用必须通过Thread方法中的start()才行。
//创建线程的另一种方法是实现Runnable接口
//必须通过Thread实例才能创建并运行线程。
public class MyRunnable implements Runnable{
private Socket socket;
MyRunnable(Socket socket){
this.socket=socket;
}
@Override
public void run() {
try {
String name= UUID.randomUUID().toString().replace("-","");//随机生成文件名
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("C:\\\\Users\\\\WZY\\\\Desktop\\\\"+name+".png"));
byte[] bytes=new byte[1024];
int len;
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("上传成功!");
bw.newLine();
bw.flush();
bw.close();
bos.close();
//ss.close();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (socket!=null){
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
6.利用线程池可以让结构简洁
//线程池
ThreadPoolExecutor pool=new ThreadPoolExecutor(
3,//核心线程数量
16,//线程池大小
60,//空闲时间
TimeUnit.SECONDS,//空闲时间单位
new ArrayBlockingQueue<>(2),//队列
Executors.defaultThreadFactory(),//线程工厂,让线程池创建线程对象
new ThreadPoolExecutor.AbortPolicy()//阻塞队列
);
调用线程池的方法去启动新线程
pool.submit(new MyRunnable(socket));