简介
UDP(User Datagram Protocol)用户数据报协议,TCP(Transmission Control Protocol) 传输控制协议,是传输层的两个重要协议。
UDP是一种无连接、不可靠传输的协议。其将数据源IP、目的地IP和端口封装成数据包,不需要建立连接,每个数据包的大小限制在64KB内;发送不管对方是否准备好,接收方收到也不确认,故是不可靠的;可以广播发送,发送数据结束时无需释放资源,开销小,速度快。UDP协议适合于即时通信场景,丢失少量数据包也不影响,例如语音通话、视频等。
TCP协议的使用必须双方先建立连接,即是一种面向连接的可靠通信协议。传输前,采用“三次握手”方式建立连接。在连接中可进行大数据量的传输 。连接、发送数据都需要确认,且传输完毕后,还需释放已建立的连接,通信效率较低。TCP协议适用于对信息安全要求较高的场景,例如文件下载等需要安全的数据通信。
Internet协议(IP)
Internet协议类 — InetAddress ,主要包括以下API:
public static InetAddress getLocalHost() |
返回本主机的地址对象 |
public static InetAddress getByName(String host) |
得到指定主机的IP地址对象,参数是域名或者IP地址 |
public String getHostName() |
获取此IP地址的主机名 |
public String getHostAddress() |
返回IP地址字符串 |
public boolean isReachable(int timeout) |
判断是否可在指定毫秒内连通该IP地址对应的主机 |
//本机ip InetAddress ip1=InetAddress.getLocalHost(); System.out.println(ip1.getHostName()); System.out.println(ip1.getHostAddress()); //域名IP InetAddress ip2=InetAddress.getByName("www.baidu.com"); System.out.println(ip2.getHostAddress()); InetAddress ip3=InetAddress.getByName("112.80.248.75"); System.out.println(ip3.getHostAddress()); //判断是否可以连通 ping System.out.println(ip3.isReachable(5000));
使用UDP通信
假定从客户端(clientP)给服务端(serverP)发消息,我们先实现客户端发消息功能。
- 首先创建DatagramSocket对象socket,作为发送端UDP对象不需要定义端口号,使用随机分配的就可以了。
- 然后使用DatagramPacket:数据包对象,将我们的数据内容进行包装,在这里需要设置好接收端口号。
- 最后使用send方法把数据包发出去。
- 释放通信资源
DatagramSocket socket=new DatagramSocket(); System.out.println("本机端口:"+socket.getLocalPort());//发送端端口号 for (int i = 0; i < 5; i++) { //内容 byte[] buff = ("你好 "+i).getBytes(); DatagramPacket packet = new DatagramPacket(buff, buff.length, InetAddress.getLocalHost(), 8888);//接收端口 //发送 socket.send(packet); } socket.close();
再来实现服务端接收功能。
- 首先也是创建DatagramSocket对象socket,作为接收端UDP对象就需要定义端口号了,与服务端一致。
- 然后使用DatagramPacket,数据包对象,来存放我们需要接收的内容数据包。
- 最后使用receive方法把数据包收回来。
DatagramSocket socket=new DatagramSocket(8888); for (int i = 0; i < 5; i++) { //内容 byte[] buff = new byte[1024 * 10]; DatagramPacket packet = new DatagramPacket(buff, buff.length); //接收 socket.receive(packet); //数据 int len = packet.getLength();//获取收到内容的长度 String rs = new String(buff, 0, len); System.out.println("收到 " +packet.getSocketAddress().toString()+":"+ rs); } socket.close();
以上就实现了客户端往服务端发送消息。这个实现的还只是单方面通信,那我们如何实现互相通信呢。
实现UDP的C-S互通信
基于前面内容我们知道如何实现一方给另一方发消息,这里我们加一个线程进行反过来操作就实现了一个简单的互相通信。
- 服务端
我们设置一个线程来进行数据接收打印,在主线程进行数据发送,再根据关键字识别进行通信中断功能。
public class serverP { public static void main(String[] args) throws Exception { System.out.println("服务端+++"); Thread t=new myThread();//服务端接收线程 t.start(); //发送 对象 DatagramSocket socketp =new DatagramSocket(); Scanner sc=new Scanner(System.in); while(true) { //内容 String msg=sc.nextLine(); byte[] buff = msg.getBytes(); DatagramPacket packet = new DatagramPacket(buff, buff.length, InetAddress.getLocalHost(), 8888); //接收 socketp.send(packet); if("再见".equals(msg)) { socketp.close(); System.out.println("断开连接~"); break; } } } } /** * 接收线程 */ class myThread extends Thread{ @Override public void run() { //对象 DatagramSocket socket= null; try { socket = new DatagramSocket(8889); } catch (SocketException e) { e.printStackTrace(); } while (true) { //内容 byte[] buff = new byte[1024 * 10]; DatagramPacket packet = new DatagramPacket(buff, buff.length); //接收 try { socket.receive(packet); } catch (IOException e) { e.printStackTrace(); } //数据 int len = packet.getLength();//获取收到内容的长度 String rs = new String(buff, 0, len); System.out.println("收到 " +packet.getSocketAddress().toString()+ ":"+ rs); if("再见".equals(rs)) { socket.close(); break; } } } }
- 客户端,类似的有:
public class clientP { public static void main(String[] args) throws Exception { System.out.println("客户端+++"); Thread t=new myThreadC();//服务端接收线程 t.start(); //发送 对象 DatagramSocket socket=new DatagramSocket(); Scanner sc=new Scanner(System.in); while(true) { //内容 String msg=sc.nextLine(); byte[] buff = msg.getBytes(); DatagramPacket packet = new DatagramPacket(buff, buff.length, InetAddress.getLocalHost(), 8889); //接收 socket.send(packet); if("再见".equals(msg)) { socket.close(); System.out.println("断开连接~"); break; } } } } /** * 接收线程 */ class myThreadC extends Thread{ @Override public void run() { //对象 DatagramSocket socket= null; try { socket = new DatagramSocket(8888); } catch (SocketException e) { e.printStackTrace(); } while (true) { //内容 byte[] buff = new byte[1024 * 10]; DatagramPacket packet = new DatagramPacket(buff, buff.length); //接收 try { socket.receive(packet); } catch (IOException e) { e.printStackTrace(); } //数据 int len = packet.getLength();//获取收到内容的长度 String rs = new String(buff, 0, len); System.out.println("收到 " +packet.getSocketAddress().toString()+":"+ rs); if("再见".equals(rs)) { socket.close(); break; } } } }
- 至此我们实现了UDP互相发消息
TCP通信
java中只要是使用java.net.Socket类实现通信,底层即是使用了TCP协议。
public Socket(String host , int port),创建发送端的Socket对象与服务端连接,参数为服务端程序的ip和端口。
方法:getOutputStream、getInputStream,分别是获得字节输出、输入流对象。
public ServerSocket(int port),注册服务端端口。
方法:accept,等待接收客户端的Socket通信连接,连接成功返回Socket对象与客户端建立端到端通信
具体来说,客户端发送实现:
- 创建客户端的Socket对象,请求与服务端的连接。
- 使用socket对象调用getOutputStream()方法得到字节输出流。
- 使用字节输出流完成数据的发送,发送一行内容就要flush。
- 释放资源:关闭socket管道。
Socket socket=new Socket("127.0.0.1",6666); OutputStream os= socket.getOutputStream(); PrintStream ps=new PrintStream(os); while(true) { ps.println("hello~"); ps.flush(); }
服务端接收:
- 创建ServerSocket对象,注册服务端端口。
- 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。
- 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。
- 释放资源:关闭socket管道
ServerSocket serverSocket=new ServerSocket(6666); Socket socket=serverSocket.accept(); InputStream is=socket.getInputStream(); BufferedReader br=new BufferedReader(new InputStreamReader(is)); String msg; while((msg=br.readLine())!=null){ System.out.println(socket.getRemoteSocketAddress()+" "+msg); }
先运行服务端,然后打开客户发送端,成功发送。
TCP的B-S通信
要实现浏览器端的消息(浏览器显示服务端的内容),就要使得消息符合http响应数据协议格式:
System.out.println("***服务端***"); try { //注册端口 ServerSocket serverSocket=new ServerSocket(8080); while(true) { //接到一个socket管道 Socket socket = serverSocket.accept(); System.out.println(socket.getRemoteSocketAddress()+"访问了~"); //新线程处理socket连接消息 new serverReaderThread(socket).start(); } } catch (IOException e) { e.printStackTrace(); } //处理进来的socket管道消息读取 public class serverReaderThread extends Thread { private Socket socket; public serverReaderThread(Socket socket){ this.socket=socket; } @Override public void run() { try { PrintStream ps=new PrintStream(socket.getOutputStream()); //HTTP ps.println("HTTP/1.1 200 OK"); ps.println("Content-Type:text/html;charset=UTF-8"); ps.println(); ps.println("<span>hello </span>"); ps.close(); }catch (Exception e){ System.out.println(socket.getRemoteSocketAddress()+"下线了~"); // e.printStackTrace(); } } }
运行服务端后浏览器端就可以访问了。