UDP,TCP特点对比,DatagramPacket,SocketAddress的使用 ,UDP的API,如何编写一个简单的回显服务器及客户端,详细解释(本文内容较难,建议多次阅读,自己敲一敲)

一、

传输层提供协议主要有两个:(两套有不同的API)

1.UDP:无连接,不可靠传输,面向数据报,全双工

2.TCP:有连接,可靠传输,面向字节流,全双工

有/无连接,如同JDBC(看我之前写的里面),先创建一个DataSource,再通过DataSource创建Connection->(TCP编程,也存在这种类似建立连接的过程。

客户端和服务器之间,使用“内存”保存对端信息~~,双方都保存这个信息,此时连接就存在,客户端可以连多个服务器,服务器也可以连接多个客户端。

链接(和连接是不一样的)——其实就是快捷方式,也叫符号链接

TCP可靠传输(效果也会因为可靠而降低):A尽可能的传递给B,并且假如传输失败的时候,A能感知到,成功的时候哦,自己也能知道,自己传递成功了。

UDP不可靠传输(效率偏高一点)

注意⚠️:可靠和安全是两回事,谈到的网络安全,你是否容易被黑客捕获,以及如果捕获后,会不会泄露一些重要信息。

面向字节流,面向数据报:这两种特性很影响代码编写。

TCP和文件操作类似(都是流式,这里传输单位是字节,成为字节流)通过TCP读写100字节数据,可一次读写100字节,当然也可以拆分,一次读10,读10次等等。

UDP的数据报,读写的基本单位,是一个UDP数据报(包含一系列数据属性)

全双工:一个通道,可以双向通信

半双工:一个通道,可单向通信

二、

UDP的socket. API

1.DatagramSocket:是一个Socket对象,操作系统,使用文件这样的概念,来管理一些软硬件资源,网卡,操作系统,也是用文件这样的方式管理网卡,表示网卡这类文件成为Socket文件。JAVA中的socket对象,就对应系统里的socket文件(最终管理到网卡),要进行网络通信,要先有socket对象

DatagramSocket():在客户端这边使用,客户端使用哪个端口,系统是自动分配的

DatagramSocket(int port):在服务器这边使用,服务器使用哪个端口是我们手动指定的。

2.DatagramPacket:

表示一个UDP数据报,代表系统中设定的UDP数据报的二进制结构

这就如同:

大学六厅,9号窗口西安里凉皮<->相当于服务器端口(固定好找),大学六厅就相当于IP地址,同学们坐的位置就相当于客户端端口号,你下次来是找空位,而不是上次的座位,但是9号窗口:客户端的端口号是要求固定的

一个客户端主机,上面运行的程序很多,不知道,你手动指定的端口是不是被别的程序占用 ,让系统自动分配一个端口是更明智的选择,服务器是完全在程序员手里控制的,程序员可以把服务器上多个程序安排好,让他们使用不同的端口。

DatagramSocket提供了三个方法。  

void receive(DatagramPacket p)

         send(DatagramPacket p):发送

         close()关闭

DatagramPacket :表示一个UDP数据报,代表系统中设定的UDP数据报的二进制结构

DatagramPacket(byte[]buf,int length)->接受数组

DatagramPacket(byte[]buf,int offset,int length,SocketAddress address(目的端口和目的IP) )

DatagramPacket作为UDP数组报,必然要承载一些数据,通过手动指定的byte[]作为存储数据的空间。

三个方法(知道就好):

InetAddress getAdress():从接收的数据报中,获取发送端主机IP地址,或从发送的数据报中,获取接收端主机IP地址

int getPort():从接收数据报中,获取发送端主机的端口号,或从发送的数据报中,获取接受端的主机端口号。

byte []getData:获取数据报中的数据

开始写UDP客户端服务器——新的流程

最简单的UDP服务器,回显服务器(客户端发啥,服务器就返回什么东西),编写网络程序,经常会见到SocketException这个异常

最典型的情况,端口号被占用,端口号用来区分主机上的应用程序,一个应用程序可以占据主机上多个端口,一个端口只能被一个进程占用(这话其实也不太严谨,但此处不做讨论)端口已经被别的进程占用了,此时你再创建这个socket对象,占用该端口,就会报错。

一个服务器要给很多用户的客户端提供服务,服务器也不知道客户端什么时候来,服务器只能时刻准备着,随时客户端来了,随时提供服务。

写服务器三个步骤

1.读取请求,并解析

2.根据请求,计算出响应

回显服务器,并不关注这个,但是正经的服务器,主要代码都是在完成这个模块,(处理一个请求,可能会经历几万行的代码,几十万行代码构建出来的逻辑)

3.把响应写回客户端

介绍方法

1.socket.receive():这方法,参数DatagramPacket是一个“输出型参数”,传入receive是一个空的对象,receive内部就会把这个内容填充上,当receive执行结束,于是就得到了一个装满内容的DatagramPacket.(幻想,你把餐盘(DatagramPacket)给食堂大姨,食堂大姨给你添饭(receive),小伙盘子给你,你拿回去

2.DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096)

 这个4096,你随便写,无所谓,但最好别太大,也别太小,这个对象用来保存数据的内存空间,是需要手动指定的,不像我们之前的集合类,能自动申请,释放,扩容内存

3.utf8中,一个汉字,三个字节,我们的String s ——s.length——s.getBytes().length,一个是按照字节存储的,一个是按照字符存储,英文还好,但是假如说是中文,中文的话两个字,是两个字符还是6个字节就有区别了。

4.

DatagramPacket responsePacket=new Datagrampacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());                              

response.getBytes():接收的字符串数组 

response.getBytes().length:字节长度

requestPacket.getSocketAddress():发给谁,目的端口,IP,要构造一个DatagramPacket对象,把响应发给客户端

三、

回显代码的服务器具体实现

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpEchServer {
    private DatagramSocket socket = null;

    //服务器需要绑定的窗口
    public UdpEchServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动");
        while (true) {
//申请一个4096的字符串数组取装你的语句回显。
            DatagramPacket requestPacker = new DatagramPacket(new byte[4096], 4096);

//接收这个字符串(在客户端来临之前,他会一直停止在这个地方等待)
            socket.receive(requestPacker);

//这句话应该是相当于把你哪个语句内容,放到一个字符串里面。
            String request = new String(requestPacker.getData(), 0, requestPacker.getLength());

            //2.根据请求,计算出响应(由于这个是回显,所以直接返回你的输入值就行,也就是上面的字符串)
            String response = process(request);

            //3.把响应写回客户端,此时需要告知网卡,要发的内容是什么,发给谁。
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length, requestPacker.getSocketAddress());

            //4.把这个发的内容,重新返回给客户端
            socket.send(requestPacker);
//返回之后给他打印一样,C语言的printf
            System.out.printf("[%s:%d] rep:%s,resp:%s\n", requestPacker.getAddress().toString(), requestPacker.getPort(), request, response);
        }
    }

    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        UdpEchServer server = new UdpEchServer(9090);
        server.start();
    }

}

四、❤️

回显服务器客户端的编写

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class UdpEchoClient {
//第一个是用来装客户端发的语句并且传输给服务器的。
    private DatagramSocket socket=null;
//下面两个是本机的IP和连接服务器的端口
    private  String serverIP;
    private  int serverPort;

    public UdpEchoClient(String ip,int port) throws SocketException {
        serverIP = ip;
        serverPort = port;
        socket=new DatagramSocket();
    }
    //让这个客户端反复从控制台读取用户输入的内容,把这个过程构造成UDP的请求,发给服务器,再读取服务器返回的UDP响应
    //最终显示在客户端屏幕上
    public  void start() throws IOException {
        Scanner scanner=new Scanner(System.in);
        System.out.println("客户端启动");
        while(true){
            //1.从控制台读取用户制定的内容
            System.out.println("->");

//输入你要输入的语句。
            String request= scanner.next();

            //2.构造请求对象,并且把你的语句打包,并且发给服务器
//request.getBytes():字符串的字节数组
//request.getBytes().length:字节数组长度
//InetAddress.getByName(serverIP):本机的IP,此时需要的是InetAddress对象,使用inetAddress的静态方法,getByName来进行构造(工厂模式,工厂方法)
//serverPort:服务器的端口号

            DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(serverIP),serverPort);

//把这个打包好的发送给服务器
            socket.send(requestPacket);

            //3.读取服务器的响应,并解析出响应内容
            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 {
//127.0.0.1是用来访问本机地址的
        UdpEchoClient client=new UdpEchoClient("127.0.0.1",9090);
        client.start();
    }


}

五、 

我们如何开启多个客户端呢?

1.点击我点击的地方

2.点这个more的这个东西

3.勾选第一个选项(第二个原来就有),然后退出点击OK保存就可以。

最终的呈现结果

总结:这个勾八内容挺复杂,自己去写一写,感受一下。

你可能感兴趣的:(计算机网络(java方面),udp,tcp/ip,服务器)