TCP通信协议是一种可靠的网络协议,他在通信的两端各建立一个Socket对象
通信之前要保证连接已经建立
通过Socket产生IO流来进行网络通信
1.创建客户端的Socket对象(Socket)与指定服务端链接
Socket(String host,int port)
2.获取输出流,写数据
OutputStream getOutputStream()
3.释放资源 close()
代码:
public static void main(String[] args) throws IOException { //创建Socket对象 Socket socket =new Socket("127.0.0.1",9999); OutputStream ost=socket.getOutputStream(); //将数据写到服务器 ost.write("hellp".getBytes()); //释放资源 ost.close(); socket.close(); }
步骤:
1.创建服务器端的Socke对象(ServerSocket)
ServerSocket(int port)
2.监听客户链接,返回一个Socket对象
Socket accept()
3.获取输入流,读数据,并把数据显示在控制台
InputStream getInputStream()
4.释放资源
void close()
代码:
public class Server { public static void main(String[] args) throws IOException { //创建服务器对象 ServerSocket serverSocket = new ServerSocket(9999); // 服务端调用accept()接收并返回一个Socket对象 // accept会一直等待连接 Socket accept = serverSocket.accept(); //获得输入流对象 InputStream is = accept.getInputStream(); int b; while ((b = is.read()) != -1) { //强转回字符 System.out.println((char) b); } } }
1.客户端向向服务器发送链接请求,等待服务器缺人
2.服务器向客户端返回一个响应,告诉客户端收到请求
3.客户端向服务器再次发出缺人信息,连接建立
1.客户端发送取消连接请求
2.服务器向客户端返回响应表示收到客户端取消请求,并将最后的数据处理完毕
3.服务器向客户端发出确认取消信息
4.客户端再次向服务器发出确认取消信息,连接取消
客户端:发送数据,接收服务器反馈
服务器:接收数据,给出反馈
客户端:
public class Client { public static void main(String[] args) throws IOException { //创建Socket对象 Socket socket =new Socket("127.0.0.1",9999); OutputStream ost=socket.getOutputStream(); //将数据写到服务器 ost.write("hello".getBytes()); //关闭输出流但是不影响socket,否则server的Reader会阻塞,一直等 socket.shutdownOutput(); BufferedReader br =new BufferedReader(new InputStreamReader(socket.getInputStream())); String line; while ((line= br.readLine())!=null){ System.out.println(line); } //释放资源\ br.close(); ost.close(); socket.close(); } }
服务端:
public class Server { public static void main(String[] args) throws IOException { //创建服务器对象 ServerSocket serverSocket = new ServerSocket(9999); // 服务端调用accept()接收并返回一个Socket对象 // accept会一直等待连接 Socket accept = serverSocket.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(); serverSocket.close(); } }
客户端:
public class Client { public static void main(String[] args) throws IOException { //创建Socket对象 Socket socket =new Socket("127.0.0.1",9999); //创建本地文件输入流 BufferedInputStream bf=new BufferedInputStream(new FileInputStream("test\\image\\1.jpg")); //创建网络输出流 OutputStream ost=socket.getOutputStream(); BufferedOutputStream bos =new BufferedOutputStream(ost); //将数据写到网络 int b; while ((b=bf.read())!=-1){ bos.write(b); } //关闭输出流但是不影响socket,否则server的Reader会阻塞,一直等 socket.shutdownOutput(); BufferedReader br =new BufferedReader(new InputStreamReader(socket.getInputStream())); String line; while ((line= br.readLine())!=null){ System.out.println(line); } //释放资源 br.close(); socket.close(); } }
public class Server { public static void main(String[] args) throws IOException { //创建服务器对象 ServerSocket serverSocket = new ServerSocket(9999); // 服务端调用accept()接收并返回一个Socket对象 // accept会一直等待连接 Socket accept = serverSocket.accept(); //获得网络输入流对象 InputStream is = accept.getInputStream(); //转换成缓冲流 BufferedInputStream bis =new BufferedInputStream(is); //创建本地输出流 BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("test\\image\\4.jpg")); //读取网络流的内容并写到新文件中 int b; while ((b = bis.read()) != -1) { bos.write(b); } System.out.println("看看是否这里阻塞了"); //涉及到中文,我们把字节流转换成字符流 BufferedWriter bw =new BufferedWriter(new OutputStreamWriter(accept.getOutputStream())); bw.write("传送完毕"); bw.newLine(); bw.flush(); bos.close(); accept.close(); serverSocket.close(); } }
关于关流,网络上的流只需要关闭socket就可以了
本地的流需要手动关一下
刚才编写的服务器,客户端一旦完成文件传输,服务器就关闭了
但是有时候客户端需要多次传输,或者有多个客户端需要传输
那么我们在服务器端直接加一个循环进去:
public class Server { public static void main(String[] args) throws IOException { //创建服务器对象 ServerSocket serverSocket = new ServerSocket(9999); // 服务端调用accept()接收并返回一个Socket对象 // accept会一直等待连接 while (true) { Socket accept = serverSocket.accept(); //获得网络输入流对象 InputStream is = accept.getInputStream(); //转换成缓冲流 BufferedInputStream bis =new BufferedInputStream(is); //创建本地输出流 BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("test\\image\\4.jpg")); //读取网络流的内容并写到新文件中 int b; while ((b = bis.read()) != -1) { bos.write(b); } System.out.println("看看是否这里阻塞了"); //涉及到中文,我们把字节流转换成字符流 BufferedWriter bw =new BufferedWriter(new OutputStreamWriter(accept.getOutputStream())); bw.write("传送完毕"); bw.newLine(); bw.flush(); bos.close(); accept.close(); } //服务器不关闭 // serverSocket.close(); } }
那么我们可以在写文件的时候,将文件名设置成随机就可以了
但是用Random方法显然不符合要求
UUID:一个表示不可变的通用唯一标识符的类
静态方法:.randomUUID()
随机生成一个uuid对象
然后再调用.toString()方法就可以转换成字符串
将文件名改成 UUID.randomUUID().toString();就可以了
我们可以利用多线程
修改后的Server:
public class Server { public static void main(String[] args) throws IOException { //创建服务器对象 ServerSocket serverSocket = new ServerSocket(9999); // 服务端调用accept()接收并返回一个Socket对象 // accept会一直等待连接 while (true) { Socket accept= serverSocket.accept(); //当接收到socket对象就开一条线程 并且将accept传递过去 ServerThread st= new ServerThread(accept); new Thread(st).start(); } //服务器不关闭 // serverSocket.close(); } }
创建的线程类:
public class ServerThread implements Runnable{ private final Socket accept; public ServerThread(Socket accept) { this.accept=accept; } @Override public void run() { BufferedOutputStream bos=null; try { //获得网络输入流对象 InputStream is = accept.getInputStream(); //转换成缓冲流 BufferedInputStream bis =new BufferedInputStream(is); //创建本地输出流 bos=new BufferedOutputStream(new FileOutputStream("test\\image\\"+ UUID.randomUUID().toString() +".jpg")); //读取网络流的内容并写到新文件中 int b; while ((b = bis.read()) != -1) { bos.write(b); } System.out.println("看看是否这里阻塞了"); //涉及到中文,我们把字节流转换成字符流 BufferedWriter bw =new BufferedWriter(new OutputStreamWriter(accept.getOutputStream())); bw.write("传送完毕"); bw.newLine(); bw.flush(); } catch (IOException e) { throw new RuntimeException(e); } finally { try { assert bos != null; bos.close(); accept.close(); } catch (IOException e) { throw new RuntimeException(e); } } } }
直接在Server创建一个线程池
public class Server { public static void main(String[] args) throws IOException { //创建服务器对象 ServerSocket serverSocket = new ServerSocket(9999); ThreadPoolExecutor pool = new ThreadPoolExecutor( 3,//核心线程数量 10,//线程池最大容量 60,//临时线程空闲时间 TimeUnit.SECONDS,//临时线程空闲时间的单位 new ArrayBlockingQueue<>(5),//允许阻塞的个数 Executors.defaultThreadFactory(),//创建线程的方式 new ThreadPoolExecutor.AbortPolicy()//任务拒绝策略 ); // 服务端调用accept()接收并返回一个Socket对象 // accept会一直等待连接 while (true) { Socket accept = serverSocket.accept(); //当接收到socket对象就开一条线程 ServerThread st = new ServerThread(accept); // new Thread(st).start(); //将线程丢进线程池 pool.submit(st); } //服务器不关闭 // serverSocket.close(); } }