💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] # TCP UDP中只有发送端和接收端,不区分客户端与服务器端,计算机之间可以任意地发送数据。 而TCP通信是严格区分客户端与服务器端的,在通信时,必须先由客户端去连接服务器端才能实现通信,服务器端不可以主动连接客户端,并且服务器端程序需要事先启动,等待客户端的连接。 在JDK中提供了两个类用于实现TCP程序,一个是ServerSocket类,用于表示服务器端,一个是Socket类,用于表示客户端。 ## ServerSocket 使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上(参数port就是端口号)。 ~~~ 创建绑定到特定端口的服务器套接字 ServerSocket(int port) ~~~ ServerSocket的常用方法 ~~~ 侦听并接受到此套接字的连接 Socket accept(); 返回此服务器套接字的本地地址 InetAddress getInetAddress() ~~~ ServerSocket对象负责监听某台计算机的某个端口号,在创建ServerSocket对象后,需要继续调用该对象的accept()方法,接收来自客户端的请求。当执行了accept()方法之后,服务器端程序会发生阻塞,直到客户端发出连接请求,accept()方法才会返回一个Scoket对象用于和客户端实现通信,程序才能继续向下执行 ## Socket 用于实现TCP客户端程序 **构造方法** 该构造方法在创建Socket对象时,会根据参数去连接在指定地址和端口上运行的服务器程序,其中参数host接收的是一个字符串类型的IP地址 ~~~ 创建一个流套接字并将其连接到指定主机上的指定端口号 Socket(String host, int port) ~~~ 参数address用于接收一个InetAddress类型的对象,该对象用于封装一个IP地址。 ~~~ 创建一个流套接字并将其连接到指定主机上的指定端口号 Socket(InetAddress address, int port) ~~~ **常用方法** | 方法声明 | 功能描述 | | --- | --- | | int getPort() | 该方法返回一个int类型对象,该对象是Socket对象与服务器端连接的端口号 | | InetAddress getLocalAddress() | 该方法用于获取Socket对象绑定的本地IP地址,并将IP地址封装成 InetAddress类型的对象返回 | | void close() | 该方法用于关闭Socket连接,结束本次通信。在关闭socket之前,应将与socket相关的所有的输入/输出流全部关闭,这是因为一个良好的程序应该在执行完毕时释放所有的资源 | | InputStream getInputStream() | 该方法返回一个InputStream类型的输入流对象,如果该对象是由服务器端的Socket返回,就用于读取客户端发送的数据,反之,用于读取服务器端发送的数据 | | OutputStream getOutputStream() | 该方法返回一个OutputStream类型的输出流对象,如果该对象是由服务器端的Socket返回,就用于向客户端发送数据,反之,用于向服务器端发送数据 | 在Socket类的常用方法中,getInputStream()和getOutStream()方法分别用于获取输入流和输出流。当客户端和服务端建立连接后,数据是以IO流的形式进行交互的,从而实现通信。 接下来通过一张图来描述服务器端和客户端的数据传输,如下图所示 ![](https://box.kancloud.cn/c346d213f2b884381f403d9eb4984572_916x315.png) ## TCP实现文件上传 * 服务器端 ~~~ /* * 文件上传 服务器端 * */ public class TCPServer { public static void main(String[] args) throws IOException { //1,创建服务器,等待客户端连接 ServerSocket serverSocket = new ServerSocket(8888); Socket clientSocket = serverSocket.accept(); //显示哪个客户端Socket连接上了服务器 InetAddress ipObject = clientSocket.getInetAddress();//得到IP地址对象 String ip = ipObject.getHostAddress(); //得到IP地址字符串 System.out.println("小样,抓到你了,连接我!!" + "IP:" + ip); //7,获取Socket的输入流 InputStream in = clientSocket.getInputStream(); //8,创建目的地的字节输出流 D:\\upload\\192.168.74.58(1).jpg BufferedOutputStream fileOut = new BufferedOutputStream(new FileOutputStream("D:\\upload\\192.168.74.58(1).jpg")); //9,把Socket输入流中的数据,写入目的地的字节输出流中 byte[] buffer = new byte[1024]; int len = -1; while((len = in.read(buffer)) != -1){ //写入目的地的字节输出流中 fileOut.write(buffer, 0, len); } //-----------------反馈信息--------------------- //10,获取Socket的输出流, 作用:写反馈信息给客户端 OutputStream out = clientSocket.getOutputStream(); //11,写反馈信息给客户端 out.write("图片上传成功".getBytes()); out.close(); fileOut.close(); in.close(); clientSocket.close(); //serverSocket.close(); } } ~~~ * 编写客户端,完成上传图片 ~~~ /* * 文件上传 客户端 * * public void shutdownOutput() 禁用此Socket的输出流,间接的相当于告知了服务器数据写入完毕 */ public class TCPClient { public static void main(String[] args) throws IOException { //2,创建客户端Socket,连接服务器 Socket socket = new Socket("192.168.74.58", 8888); //3,获取Socket流中的输出流,功能:用来把数据写到服务器 OutputStream out = socket.getOutputStream(); //4,创建字节输入流,功能:用来读取数据源(图片)的字节 BufferedInputStream fileIn = new BufferedInputStream(new FileInputStream("D:\\NoDir\\test.jpg")); //5,把图片数据写到Socket的输出流中(把数据传给服务器) byte[] buffer = new byte[1024]; int len = -1; while ((len = fileIn.read(buffer)) != -1){ //把数据写到Socket的输出流中 out.write(buffer, 0, len); } //6,客户端发送数据完毕,结束Socket输出流的写入操作,告知服务器端 socket.shutdownOutput(); //-----------------反馈信息--------------------- //12,获取Socket的输入流 作用: 读反馈信息 InputStream in = socket.getInputStream(); //13,读反馈信息 byte[] info = new byte[1024]; //把反馈信息存储到info数组中,并记录字节个数 int length = in.read(info); //显示反馈结果 System.out.println( new String(info, 0, length) ); //关闭流 in.close(); fileIn.close(); out.close(); socket.close(); } } ~~~