TCP协议概述 * TCP英语全称:transfer control protocol 中文名称:传输控制协议。 TCP协议的特点(记忆) * 面向连接的协议,速度慢但是可靠。 * 通过三次握手建立连接,形成数据传输通道。 * 通过四次挥手断开连接 * 客户端主动发送消息给服务器端,服务器接收到消息之后,可以反馈数据给客户端。 * 基于IO流进行数据传输 * 数据传输大小没有限制。 TCP协议使用场景 * 文件上传和下载 * 邮件发送 * 远程登录 TCP协议相关的类 * Socket:代表客户端程序,一个该类的对象就表示一个客户端程序。 * ServerSocket:代表服务器端,一个该类的对象就表示开启了一个服务器。
TCP协议发送和接收数据
发送: 创建TCP客户端的Socket对象 获取输出流,写数据 释放资源 接收: 创建TCP服务器端的Socket对象 监听客户端连接 获取输入流,读取数据 释放资源
Socket机制
A:通信两端都应该有Socket对象 B:所有的通信都是通过Socket间的IO进行操作的
TCP通信之客户端
127.0.0.1:本机ip TCP客户端代码实现 Socket:代表客户端程序,一个该类的对象就表示一个客户端程序。 Socket类构造方法 * Socket(String host, int port) * 根据ip字符串和端口创建客户端Socket对象 * 立即连接指定的服务器,如果服务器没有开启,则直接抛出异常。 Socket类常用方法 * OutputStream getOutputStream(); 获得字节输出流,通过输出流往服务端输出数据。 * InputStream getInputStream(); 获得字节输入流,通过输入流读取服务器端返回的数据。 TCP客户端实现步骤 * 创建Socket对象:启动一个客户端程序 * 调用Socket对象的getOutputStream方法获得字节输出流对象 * 通过字节输出流对象往服务器输出数据 * 调用Socket对象的getInputStream方法获得字节输入流对象 * 通过字节输入流对象读取服务器返回的数据 * 关闭Socket
连接被拒绝。TCP协议一定要先看服务器。
java.net.ConnectException: Connection refused: connect
public static void main(String[] args) throws IOException { // 创建发送端的Socket对象 // Socket(InetAddress address, int port) // Socket(String host, int port) Socket s = new Socket("192.168.77.92", 8888); // 获取输出流,写数据 // public OutputStream getOutputStream() OutputStream os = s.getOutputStream(); os.write("hello,tcp,我来了".getBytes()); // 释放资源 s.close(); }
TCP通信之服务器端
TCP服务器端代码实现 ServerSocket:代表服务器端,一个该类的对象就表示开启了一个服务器。 ServerSocket类构造方法 * ServerSocket(int port) 根据端口号创建服务器端程序 ServerSocket类成员方法 * Socketaccept(); 等待客户端连接并获得一个与客户端相关的Socket对象 TCP服务器端开发步骤 * 创建ServerSocket对象,开启服务器 * 调用accept方法等待客户端连接并获得Socket对象 * 调用Socket对象getInputStream方法获得字节输入流对象 * 通过字节输入流对象读取客户端发送的数据。 * 调用Socket对象getOutputStream方法获得字节输出流对象 * 通过字节输出流对象往客户端输出数据 * 调用close方法关闭Socket对象。
//TCPClient public static void main(String[] args) throws Exception { // 创建Socket对象 Socket socket = new Socket("127.0.0.1",9999); // 要发送的内容 String content = "你好 TCP服务器端,约吗"; // 获得字节输出流 OutputStream out = socket.getOutputStream(); // 往服务器发送数据 out.write(content.getBytes()); // 接收服务端发送的数据 InputStream in = socket.getInputStream(); // 创建字节数组,用来存储服务器返回的数据 byte[] buf = new byte[1024]; int length = in.read(buf); System.out.println(new String(buf,0,length)); // 关闭资源 socket.close(); } //TCPServer public static void main(String[] args) throws IOException { // 创建服务端的Socket对象 // ServerSocket(int port) ServerSocket ss = new ServerSocket(55555); // 监听客户端连接。返回一个对应的Socket对象 // public Socket accept() Socket s = ss.accept(); // 侦听并接受到此套接字的连接。此方法在连接传入之前一直阻塞。 // 获取输入流,读取数据显示在控制台 InputStream is = s.getInputStream(); byte[] bys = new byte[1024]; int len = is.read(bys); // 阻塞式方法 String str = new String(bys, 0, len); String ip = s.getInetAddress().getHostAddress(); System.out.println(ip + "---" + str); // 释放资源 s.close(); // ss.close(); //这个不应该关闭 }
客户端键盘录入,服务器输出到控制台
public static void main(String[] args) throws Exception { // 创建客户端Socket对象 Socket s = new Socket("192.168.77.1", 22222); // 键盘录入数据 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // 把通道内的流给包装一下 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line = null; while ((line = br.readLine()) != null) { // 键盘录入数据要自定义结束标记 if ("886".equals(line)){ break; } bw.write(line); //如果服务器读取用.readLine,就必须加上.newLine();不然服务器会一直等待换行符 bw.newLine(); //加上.flush()服务器才能读取到数据 bw.flush(); } // 释放资源 bw.close(); br.close(); s.close(); } public static void main(String[] args) throws Exception { // 创建服务器Socket对象 ServerSocket ss = new ServerSocket(22222); // 监听客户端连接 Socket s = ss.accept(); // 包装通道内容的流 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); String line =null; while ((line = br.readLine()) != null){ System.out.println(line); } br.close(); s.close(); ss.close(); }
告诉服务器,读取到这条数据说明我就结束,你也结束吧。
public void shutdownOutput()
public static void main(String[] args) throws IOException { // 创建客户端Socket对象 Socket s = new Socket("192.168.12.92", 11111); // 封装文本文件 BufferedReader br = new BufferedReader(new FileReader("InetAddressDemo.java")); // 封装通道内流 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line = null; while ((line = br.readLine()) != null) { // 阻塞 bw.write(line); bw.newLine(); bw.flush(); } //Socket提供了一个终止,它会通知服务器你别等了,我没有数据过来了,终止后输出流不可再使用,用来传输文件等数据 s.shutdownOutput(); // 接收反馈 BufferedReader brClient = new BufferedReader(new InputStreamReader( s.getInputStream())); String client = brClient.readLine(); // 阻塞 System.out.println(client); // 释放资源 br.close(); s.close(); } }
TCP文件上传
需求:将文件上传到指定IP的主机
TCP的服务端可以接受多个客户端的连接
TCP的客户端上传文件到服务器端
文件保存到c:/java/file文件下,文件名随机生成,只要不出现文件覆盖即可。
服务器端需要反馈上传状态(成功或失败)给客户端。
示例代码
/** * 上传线程 * @author pkxing * */ public class UploadThread extends Thread{ // 声明Socket对象 private Socket socket = null; // 构造方法 public UploadThread(Socket socket) { super(); this.socket = socket; } public void run() { FileOutputStream fos = null; try { // 目标文件夹 File destDir = new File("/aaaa"); // 随机生成一个文件名 String fileName = System.currentTimeMillis()+ new Random().nextInt(100000000)+".png"; // fileName = 1234567424.png fos = new FileOutputStream(new File(destDir,fileName)); // 获得字节输入流对象 InputStream in = socket.getInputStream(); // 定义一个字节数组:用来存储文件数据 byte[] buf = new byte[1024]; // 定义整型变量用来记录实际读取到的字节 int length = -1; // 使用循环读取客户端发送的文件数据 while((length = in.read(buf)) != -1) { // 将文件数据输出到目标文件中 fos.write(buf, 0, length); } // 获得字节输出流对象 OutputStream out = socket.getOutputStream(); // 返回上传成功给客户端 out.write("上传成功".getBytes()); // 获得客户端ip地址 String clientIP = socket.getInetAddress().getHostAddress(); System.out.println("收到 "+clientIP+" 上传的文件"); } catch (Exception e) { try { if(socket == null) return; // 获得字节输出流对象 OutputStream out = socket.getOutputStream(); // 返回上传失败给客户端 out.write("上传失败".getBytes()); } catch (IOException e1) { } } finally { try { if(socket != null) socket.close(); } catch (IOException e) { } try { if(fos != null) fos.close(); } catch (IOException e) { } } } } // 客户端程序 public class TCPClient { public static void main(String[] args) throws Exception { // 创建客户端Socket对象 Socket socket = new Socket("192.168.77.132",8888); // 获得字节输出流对象:往服务器输出数据 OutputStream out = socket.getOutputStream(); // 创建字节输入流 FileInputStream fis = new FileInputStream("/aaaaa.png"); // 定义字节数组存储读取到文件数据 byte[] buf = new byte[1024]; // 定义整型变量用来记录实际读取到字节个数 int length = -1; // 循环读取文件数据 while((length = fis.read(buf)) != -1) { // 将数据输出到服务器端 out.write(buf,0,length); } // 告诉服务器端数据已经传输完毕 // 输出一个结束标记给服务器 socket.shutdownOutput(); System.out.println("333333333333"); // 获得字节输入流对象 InputStream in = socket.getInputStream(); // 读取服务器返回的数据 length = in.read(buf); System.out.println(new String(buf,0,length)); // 关闭流和socket fis.close(); socket.close(); } } /** * 文件上传服务器端 * @author pkxing * */ public class TCPServer { public static void main(String[] args) throws Exception{ // 开启服务端 ServerSocket serverSocket = new ServerSocket(8888); // 使用死循环保证服务器不退出 while(true) { // 等待客户端连接获得获得Socket Socket socket = serverSocket.accept(); // 创建线程对象 new UploadThread(socket).start(); } } }
小结
UDP相关的两个类
DatagramPacket概述 * 数据包对象。用来封装要发送的数据。 DatagramPacket构造方法 * 发送端数据包对象:DatagramPacket(byte[] buf,int length, InetAddress address,int port); * 接收端数据包对象:DatagramPacket(byte[] buf,int length); * 获得发送端的地址:InetAddress getAddress(); * 获得实际接收的字节长度:int getLength(); * 获得发送端的端口号:int getPort(); DatagramSocket概述 * 发送对象,负责接收和发送数据包对象。 DatagramSocket构造方法 * DatagramSocket() 创建发送端Socket对象 * DatagramSocket(int port) 创建接收端Socket对象 DatagramSocket成员方法 * void send(DatagramPacket dp); 发送数据包 * void receive(DatagramPacket dp); 接收数据包 * void close() 关闭资源释放端口号。
TCP相关的两个类
Socket概述 * 客户端Socket对象,代表一个客户端程序。 Socket构造方法 * Socket(String host,int port); 创建客户端Socket对象,立即连接指定的服务器,如果服务器没开启,则直接抛出异常。 Socket成员方法 * InputStream getInputStream() 获得字节输入流对象 * OutputStream getOutputStream() 获得字节输出流对象 * void shutdownOutput() 关闭字节输出流。 * void close() 关闭Socket对象,释放端口。 ServerSocket概述 * 服务器端Socket对象,代表一个服务器程序 ServerSocket构造方法 * ServerSocket(int port); 根据指定的端口创建服务器端程序 ServerSocket成员方法 * Socket accept(); 等待客户端连接并获得Socket对象。