概述
- TCP通信能实现两台计算机之间的数据交付,通信的两端,要严格区分为客户端(Client)与服务端(Server)
- 两端通信步骤:
- 服务端程序,需提前启动,等待客户端的连接
- 客户端主动连接服务器端,连接成功才能通信。服务器端不可以主动连接客户端
- java中提供了两个类实现TCP通信
- 客户端:Socket类。创建Socket对象,向服务端发送连接请求,服务端响应请求,两者建立连接开始通信
- 服务端:ServerSocket类,创建ServerSocket对象,相当于开启一个服务器,并等待客户端的连接
Socket类
- 概述:
- 该类实现客户端套接字(也称为“套接字”,包含了IP地址和端口号的网络单位)。 套接字是两台机器之间通讯的端点。
- 套接字的实际工作由SocketImpl类的实例执行。 应用程序通过更改创建套接字实现的套接字工厂,可以配置自己创建适合本地防火墙的套接字。
-
构造方法
Socket(String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号。 host:服务器主机名称/服务器ip地址 port:端口号
-
成员方法
OutputStream getOutputStream() 返回此套接字的输出流。 InputStream getInputStream() 返回此套接字的输入流。 void close() 关闭此套接字。 使用步骤: 1、创建一个客户端对象Socket,构造方法中绑定服务器IP地址和端口号 2、使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象 3、使用网络字节输出流OutputStream对象write方法,给服务器发送数据 4、使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象 5、使用网络字节输入流InputStream对象read方法,读取服务器回写的数据 6、使用资源(Socket) 注意: 1、客户端和服务器端进行交互,必须使用Socket中提供的网络流对象,不能使用自己创建的流对象 2、当我们创建客户端对象Socket的时候,就会去请求服务器和服务器经过3次握手建立连接通路。
// 客户端 try { Socket socket = new Socket("127.168.2.140",8888); OutputStream outputStream = socket.getOutputStream(); outputStream.write("你好".getBytes()); InputStream inputStream = socket.getInputStream(); byte[] bytes=new byte[1024]; int len=inputStream.read(bytes); System.out.println(new String(bytes,0,len)); socket.close(); } catch (IOException e) { e.printStackTrace(); }
ServerSocket类
- 概述:
- 这个类实现了服务器套接字。 服务器套接字等待通过网络进入的请求。 它根据该请求执行一些操作,然后可能将结果返回给请求者。
- 服务器套接字的实际工作由SocketImpl类的实例执行。 应用程序可以更改创建套接字实现的套接字工厂,以配置自己创建适合本地防火墙的套接字。
-
构造方法
ServerSocket(int port) 创建绑定到指定端口的服务器套接字。
-
成员方法
Socket accept() 侦听要连接到此套接字并接受它。 使用步骤: 1、创建服务器端ServerSocket对象和系统指定的端口号 2、使用ServerSocket对象中的accept对象,获取到请求的客户端对象Socket 3、使用Socket对象中的方法getInputStream,获取网络字节输入流InputStream对象 4、获取网络字节输入流InputStream对象中的方法,读取客户端发送的数据 5、使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象 6、使用网络字节输出流OutputStream对象中的write,给客户端回写数据 7、释放资源(Socket,ServerSocket)
// 服务器端 try { ServerSocket serverSocket = new ServerSocket(8888); Socket socket = serverSocket.accept(); InputStream inputStream = socket.getInputStream(); byte[] bytes=new byte[1024]; int len= inputStream.read(bytes); System.out.println(new String(bytes,0,len)); OutputStream outputStream = socket.getOutputStream(); outputStream.write("收到".getBytes()); socket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); }
案例
-
文件上传
// 服务器端 public class FileUploadTCPServr { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(8888); Socket accept = serverSocket.accept(); InputStream internetInputStream = accept.getInputStream(); File localFile = new File("day16\\src\\FileUpload"); if(!localFile.exists()){ localFile.mkdir(); // 创建文件夹 } FileOutputStream localFileOutputStream = new FileOutputStream(localFile + "\\b.txt"); int len=0; byte[] bytes = new byte[1024]; while ((len=internetInputStream.read(bytes))!=-1){ localFileOutputStream.write(bytes,0,len); } accept.getOutputStream().write("上传成功".getBytes()); localFileOutputStream.close(); accept.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }
// 服务端的优化 public class FileUploadTCPServr { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(8888); /* 让服务器一直处于监听状态(死循环accept方法) 有一个客户端上传文件,就保存文件 */ while (true){ /* 使用多线程提高程序效率 有一个客户端上传文件,就开启一个线程,完成文件的上传 */ Socket accept = serverSocket.accept(); new Thread(new Runnable() { @Override public void run() { try { InputStream internetInputStream = accept.getInputStream(); File localFile = new File("day16\\src\\FileUpload"); if(!localFile.exists()){ localFile.mkdir(); // 创建文件夹 } /* 自定义文件的命名规则 规则:域名+毫米值+随机数 */ String fileName="itcast"+System.currentTimeMillis()+new Random().nextInt(9999)+".txt"; FileOutputStream localFileOutputStream = new FileOutputStream(localFile + "\\"+fileName); int len=0; byte[] bytes = new byte[1024]; while ((len=internetInputStream.read(bytes))!=-1){ localFileOutputStream.write(bytes,0,len); } accept.getOutputStream().write("上传成功".getBytes()); localFileOutputStream.close(); accept.close(); }catch (Exception e){ e.printStackTrace(); } } }).start(); } } catch (IOException e) { e.printStackTrace(); } } }
// 客户端 public class FileUploadTCPClient { public static void main(String[] args) { try { FileInputStream localFileInputStream = new FileInputStream("day16\\src\\FileUpload\\a.txt"); Socket socket = new Socket("192.168.2.140", 8888); OutputStream internetOutputStream = socket.getOutputStream(); byte[] bytes = new byte[1024]; int len=0; while ((len= localFileInputStream.read(bytes))!=-1){ internetOutputStream.write(bytes,0,len); } socket.shutdownOutput();// 给服务器一个结束标记 InputStream internetInputStream = socket.getInputStream(); while ((len= internetInputStream.read(bytes))!=-1){ System.out.println(new String(bytes,0,len)); } localFileInputStream.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
-
模拟B/S服务器
// 服务器端 public class BSTCPServer { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(8080); Socket accept = serverSocket.accept(); InputStream internetInputStream = accept.getInputStream(); /*byte[] bytes1 = new byte[1024]; int len1=0; while ((len1=internetInputStream.read(bytes1))!=-1){ System.out.println(len1); System.out.println(new String(bytes1,0,465)); }*/ //把internetInputStream网络字节输入流对象,转换为字符缓冲输入流 BufferedReader br = new BufferedReader(new InputStreamReader(internetInputStream)); //把客户端请求信息的第一行读取出来 String s = br.readLine(); // GET /day16/web/test.html HTTP/1.1 // 把读取的信息进行切割,并截取 String[] arr=s.split(""); String htmlPath=arr[1].substring(1); // day16/web/test.html HTTP/1.1 // 创建本地的字节输入流,构造方法中绑定要输入的文件路径 FileInputStream localInputStream=new FileInputStream(htmlPath); // 使用Socket中的getOutputStream方法获取网络字节输出流OutputStream OutputStream internetOutputStream = accept.getOutputStream(); // 写入HTTP协议响应头,固定写法 internetOutputStream.write("HTTP/1.1 200 OK\r\n".getBytes()); internetOutputStream.write("Content-Type:text/html\r\n".getBytes()); // 必须写入空行,否则浏览器不解析 internetOutputStream.write("\r\n".getBytes()); // 一读一写复制文件,把服务端读取的html文件回写到客户端 int len=0; byte[] bytes = new byte[1024]; while ((len=localInputStream.read(bytes))!=-1){ internetOutputStream.write(bytes,0,len); } // 释放资源 localInputStream.close(); accept.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }
// 服务端改进 public class BSTCPServer { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(8080); while (true){ Socket accept = serverSocket.accept(); new Thread(new Runnable() { @Override public void run() { try{ InputStream internetInputStream = accept.getInputStream(); //把internetInputStream网络字节输入流对象,转换为字符缓冲输入流 BufferedReader br = new BufferedReader(new InputStreamReader(internetInputStream)); //把客户端请求信息的第一行读取出来 String s = br.readLine(); // GET /day16/web/test.html HTTP/1.1 // 把读取的信息进行切割,并截取 String[] arr=s.split(" "); String htmlPath=arr[1].substring(1); // day16/web/test.html HTTP/1.1 // 创建本地的字节输入流,构造方法中绑定要输入的文件路径 FileInputStream localInputStream=new FileInputStream(htmlPath); // 使用Socket中的getOutputStream方法获取网络字节输出流OutputStream OutputStream internetOutputStream = accept.getOutputStream(); // 写入HTTP协议响应头,固定写法 internetOutputStream.write("HTTP/1.1 200 OK\r\n".getBytes()); internetOutputStream.write("Content-Type:text/html\r\n".getBytes()); // 必须写入空行,否则浏览器不解析 internetOutputStream.write("\r\n".getBytes()); // 一读一写复制文件,把服务端读取的html文件回写到客户端 int len=0; byte[] bytes = new byte[1024]; while ((len=localInputStream.read(bytes))!=-1){ internetOutputStream.write(bytes,0,len); } // 释放资源 localInputStream.close(); accept.close(); }catch (IOException e){ e.printStackTrace(); } } }).start(); } } catch (IOException e) { e.printStackTrace(); } } }