简单认识一下传输层中的UDP和TCP:
TCP:有链接,可靠传输,面向字节流,全双工
UDP:无连接,不可靠传输,面向数据报,全双工
有链接类似于打电话,通了就是有链接。没通就一直在等待。
无连接类似于发短信,只管发,不管到。
可靠传输就是保证信息传输的可靠性。就好比打电话时,你会询问对方在吗,对方回复你,你在发送重要数据给对方。不可靠传输,就好比发短信。假设对方开启了飞行模式,你短信依然能发,但是对方收不收得到,你并不关心也不会询问。
字节流:能按需所取,比如100个字节,可以一个字节一个字节取100次,也能5个字节5个字节的读20次。
数据报:固定的字节数据构成一个数据报,一次只能读取发送一个数据报。
全双工就是双向通信。既能发送也能接收的意思。
简单介绍了TCP,UDP的一些特性,下面我们来看Java中两个重要的网络编程的类。
DatagramSocket这个类提供了两个构造方法:
一个是无参构造方法,相当于只是创建了一个socket,另外一个需要传入一个端口号。 此时就是让当前的socket对象和这个指定的端口,关联起来。无参构造也会分配一个端口号,不过是系统自动分配空闲的端口号。
这两个方法的参数是一个DatagaramPacket.这也是一个类,先来介绍下:
用于释放资源的。
DatagramPacket这个类也提供了2个构造方法:
第一个构造方法相当于设置好了一个缓冲区。
第二个构造方法既构造了一个缓冲区又构造了一个地址。
基于这两个API,构造一个最简单的UDP客户端服务器程序。
服务器端的代码:
package network;
//Udp回显的服务器import javax.imageio.IIOException;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;public class UdpEchoServer {//网络编程的本质是要操作网卡//网卡不好直接操作,因此操作系统把socket这样的文件抽象成了网卡//因此进行网络通信,势必先有一个socket对象。private DatagramSocket socket=null;//对于服务器来说,创建socket对象的同时,要让他绑定上一个具体的端口号public UdpEchoServer(int port) throws SocketException {socket =new DatagramSocket(port);}public void start() throws IOException {System.out.println("服务器启动!!");while (true){//并不知道有多少个客户端想建立链接,因此写个循环//只要有客户端过来,就可以提供服务//1.读取客户端发来的请求是啥//receive方法,需要一个空白DatagramPacket对象,交给receive来进行填充。填充的数据来自于网卡DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);socket.receive(requestPacket);//此时这个DatagramPacket是一个特殊的对象,并不方便直接处理,可以把这里包含的数据拿出来,构成一个字符串String request=new String(requestPacket.getData(),0,requestPacket.getLength());//2.根据请求计算响应,由于此处是回显服务器,响应和请求相同。String response=process(request);//3.把回显写回到客户端,send参数也是DatagramPacket 需要把这个Packet对象构造好DatagramPacket responsePacket =new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());socket.send(responsePacket);//4.打印请求响应的处理中间结果System.out.printf("[%s:%d] req: %s; resp:%s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);}}//这个方法表示根据请求计算响应public String process(String request){return request;}public static void main(String[] args) throws IOException {UdpEchoServer server=new UdpEchoServer(9090);server.start();}}
这里我们用while循环实现了一直等待客户端发送请求这么一个过程。假设客户端不停的发送请求,并且请求的频率非常快。我这个服务器是否就忙不过来处理了呢?这个情况是很可能出现的。那么解决的方法就是高并发执行。好比我开了一家餐馆,生意不好的时候,我一个人绰绰有余。但是生意非常好。我一个人忙不过来,这个时候我雇了2个人来帮忙。这就是高并发的原理。那么高并发是如何实现呢。那就是通过多线程实现。一个核心一个线程的并发执行。效率就会高很多。但是硬件设施终有上限。那么通过加机器也就就解决这个硬件问题了。
客户端的代码:
package network;import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;//Udp回显客户端
public class UdpEchoClient {private DatagramSocket socket=null;private String serverIp=null;private int serverPort=0;public UdpEchoClient (String serverIp,int serverPort) throws SocketException {socket =new DatagramSocket();this.serverIp=serverIp;this.serverPort=serverPort;}public void start()throws IOException {System.out.println("客户端启动!");Scanner scanner=new Scanner(System.in);while (true) {//1.从控制台读取要发送的数据System.out.print("> ");String request =scanner.next();if(request.equals("exit")){System.out.println("goodbye");break;}//2.构造成UDP请求,并发送,构造这个Packet的时候,需要传入serverIp和port,此处的IP地址是需要一个32位的整数形式//而上述的IP是一个字符串,所以需要进行转换DatagramPacket requestPacket =new DatagramPacket(request.getBytes(),request.getBytes().length,InetAddress.getByName(serverIp),serverPort);socket.send(requestPacket);//3.读取服务器的UDP请求,并解析DatagramPacket responsePacket =new DatagramPacket(new byte[4096],4096);socket.receive(responsePacket);String response =new String(responsePacket.getData(),0,responsePacket.getLength());//4.解析结果显示出来System.out.println(response);}}public static void main(String[] args) throws IOException{UdpEchoClient client =new UdpEchoClient("127.0.0.1",9090);client.start();}
}
首先启动我们的服务器:
就会进入等待请求的状态。
再启动服务器:
我们输入一个hello;
服务器就会给我们回一个hello。我们再看服务器那边:
收到了客户端的的IP地址端口号。以上就是实现了一个简单的回显服务器和客户端。