网络编程 套接字

发送端和接收端

在一次网络数据传输时:

发送端:数据的发送方进程,称为发送端 。发送端主机即网络通信中的源主机。

接收端:数据的接收方进程,称为接收端 。接收端主机即网络通信中的目的主机。

收发端:发送端和接收端两端,也简称为收发端

客户端和服务端

服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端 ,可以提供对外服务。

客户端:获取服务的一方进程,称为客户端

最常见的场景,客户端是指给用户使用的程序,服务端是提供用户服务的程序:

  1. 客户端先发送请求到服务端

  2. 服务端根据请求数据,执行相应的业务处理

  3. 服务端返回响应:发送业务处理结果

  4. 客户端根据响应数据,展示处理结果(展示获取的资源,或提示保存资源的处理结果)

Socket套接字

Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。

基于Socket套接字的网络程序开发就是网络编程。

我们学习网络编程,和应用层打交道就是家常便饭,因为我们的编写程序都要在这里完成,使用操作系统提供的一组api(socket api,传输成提供给应用层)。

传输层为我们提供了两套协议:TCP与UDP。

TCP:有连接、可靠传输、面向字节流、全双工。

UDP:无连接、不可靠传输、面向数据流、全双工。

有/无连接:

这个概念会有些抽象,在逻辑上的链接,不是通过物理通过数据线、网线等方式,而是彼此之间保存对方的信息。就如同身份证一般,身份证上有着你的个人信息,是属于你的物品,即使弄丢了他还是属于你,而不是身份证长在你身上才是你的,离开了就不是了。

在TCP协议中,有连接就是在两个端口里互相保存着对方的信息,能够建立链接,这就是有连接

而DUP协议没有这个功能,不会和对方建立连接,保存对方的信息,这便是无连接

可靠/不可靠传输

网络传输中会出现数据丢失的情况:光电信号由于某些原因遭到破坏、干扰而丢失信息或者变成无效信号,被识别出来数据错误后将其丢弃,简称丢包 。

可靠传输:尽可能提高数据传输的成功率,减少丢包的现象出现。出现丢包现象会及时补发,确保信息能够全部发送过去。缺点:消耗的开销会很大。

不可靠传输:不会管接收方收没收到,只管把数据发送出去就完事了。

将路由器/交换机转发的数据看出跑动的汽车,路由器/交换机看成一个一个的十字路口,当路由器/交换机需转发的数据量超过数据转发数据量的上限,就会出现堵车的现象,进而造成丢包现象出现。

最直接的表现就是在公共场所连接公共网络会很卡。

面向字节流/数据流

顾名思义,读写的时候是以字节为单位还是以一个数据报为单位(不是字符)。

全双工/半双工

**全双工:**一个通信链路支持双向通信(能读能写)

**半双工:**一个通信链路支持单向通信(只能读或者写)

举例子:单行路就是半双工,对面的车就得让行等待。双行道就是全双工,来往都行,不受影响。

UDP的socket api

DatagramSocket类

把操作网卡转换成操作socket文件,间接操控网卡,用于发送和接收UDP数据。

构造DatagramSocket端口号

DatagramSocket():创建一个UDP的数据报套接字的Socket,,绑定到本机的任意一个随机端口。(客户端不需要指定端口号,用系统随机分配一个。)

DatagramSocket(int port):创建一个UDP的数据报套接字的Socket,绑定到本机的指定端口(一般用于服务端,区分同一个主机上不同的应用程序)

接收与发送方法

void recieive(DatagramPacket p):从此套接字接收数据报,若没接收到数据报则阻塞等待

void send(DatagramPacket p):从此套接子发送数据包,不会阻塞,直接发送

void close():关闭此数据报套接字

DatagramPacket

构造方法

DatagramPacket(byte [] buf , int length):构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length)

DatagramPacke(byte [] , int offset ,int length,SocketAddress address):构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length)。address指定目的主机的IP和端口号

接收方法

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

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

getBytes():转换字符串为字节

代码实现

回显服务器

java 复制代码
public class UdpEchoServer{
    private DatagramSocket socket = null;

    public UdpEchoServer(int port) throws SocketException {//网络异常处理
//        指定一个固定端口号,给服务器使用
        socket = new DatagramSocket(port);
    }
//服务器启动
    public void start() throws IOException {
        System.out.println("服务器启动");
        while(true){
            //1.接收读取请求信息
//            DatagramPacket表示一个UDP数据报,传入字节数组就是保存接收UDP的载荷部分。
//            DatagramPacket中也能保存了其他五元组的信息
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);//创建空对象
            socket.receive(requestPacket);//接收客户端发来的数据
//          读取到的二进制转化成字符串
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
//                                                  拿到字节数组,指定有效范围起始位置,有效的长度
            //在调用receive之前,先构一个造新的对象,把对象传递给receive,receive就会把数据从网卡里读出来,填充到参数中


            //2.根据请求计算响应(重点功能)但因为是回显服务器,这一步被简化,你发出上面回应什么
            String response = process(request);

            //3.把响应环境返回给客户端                               得到的字节      不能使用response.length(),代表String字符个数
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,//字节个数
                    requestPacket.getSocketAddress());//返回IP和端口号
            //发送UDP,但因为没有存储对方信息,需要我们手动指定目的IP和目的端口
            //requestPacket中存储了客户端的源IP和源端口,可以将其作为目的IP和目的端口

            socket.send(responsePacket);
            //4。打印日志
            System.out.printf("[%s:%d] req: %s, res :%\n",
                    requestPacket.getAddress().toString(),
                    requestPacket.getPort(),
                    request,
                    response);
        }
    }

    private String process(String request) {
        return request;
    }
 public static void main(String[] args) throws IOException {
        UdpEchoServer server = new UdpEchoServer(9090);
        server.start();
    }
}

socket对象代表网卡文件,对socket读文件相当于接收网卡中的数据,写文件相当于网卡发送数据

复制代码
public class UdpEchoClient {
    private DatagramSocket socket = null;

    public UdpEchoClient(int port) throws SocketException {

        socket = new DatagramSocket(port);
    }

getSocketAddress方法以读取接受存储到DatagramPacket里的源IP和源端口,特别的,hetAddress只能拿到IP,getPort只能拿到端口

DatagramPacket虽然说是读取UDP报头信息,但实际上他也会读取IP报头里 的信息,获取源IP和目的IP。

在while循环中:

构建DatagramPacket对象requestPacket (创建一个DUP数据包,报头+载荷),由receive接收请求数据包(作为输出型参数接收)往里面填充

DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);

socket.receive(requestPacket);//客户端若没有发来数据会阻塞等待

将UDP数据包中载荷取出:requestPacket.getData()可以拿到字节数组,requestPacket.getLength()可以拿到长度,然后构建一个Sting。

由于这里是回显服务器,你传进来是什么发回去就是什么,将Sting进行包装。

复制代码
String request = new String(requestPacket.getData(),0,requestPacket.getLength());

String response = process(request);

response.getBytes()将字符串转换为字节数组、response.getBytes().length得到字节长度,requestPacket.getSocketAddress()取出源IP和源端口信息,然后新构建一个DatagramPacket数据包,然后调用send发送回去。

复制代码
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());
复制代码
socket.send(responsePacket);

用户端

java 复制代码
public class UdpEchoClient {

    private String serverIP;

    private int serverPort;

    private DatagramSocket socket = null;
          //指定访问服务器的地址
    public UdpEchoClient(String serverIP,int serverPort) throws SocketException {
        this.serverIP = serverIP;//目的IP
        this.serverPort = serverPort;//目的端口
        //serverIP是目的IP,serverPort是目的端口,源IP是客户端主机IP,源端口是本机一个系统随机分配的端口
        socket = new DatagramSocket();//不能传固定源端口,每次都是随机分配使用,一个固定的端口可能会被其他应用使用导致无法使用。

    }
    public void start() throws IOException {

        while(true) {
            System.out.println("输入发送的信息");
            Scanner scanner = new  Scanner(System.in);

            if (!scanner.hasNext()) {
                break;
            }
            String request = scanner.next();
            //构造DatagramPacket数据报,传入载荷、目的IP和目的端口,InetAddress.getByName()方法按照字符串创建(给人看的)的IP转换成字符串(给机器看的)。
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                    InetAddress.getByName(serverIP), serverPort);
            //发送数据报
            socket.send(requestPacket);
            //接收服务器响应,创建一个数据报,用具接收服务端的信息
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket);
            //解析,将收到的数据报转化成字符串类型
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
            System.out.println(response);
        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient client = new UdpEchoClient("127.0.0.1",9090);
        client.start();
    }
}

用户端的代码这里就不展开讲了,基本上和服务端一样的。

相关推荐
K_i13427 分钟前
云原生网络基础:IP、端口与网关实战
网络·ip·接口隔离原则
m0_6515939144 分钟前
Netty网络架构与Reactor模式深度解析
网络·架构
大面积秃头1 小时前
Http基础协议和解析
网络·网络协议·http
我也要当昏君3 小时前
6.3 文件传输协议 (答案见原书 P277)
网络
Greedy Alg3 小时前
Socket编程学习记录
网络·websocket·学习
刘逸潇20054 小时前
FastAPI(二)——请求与响应
网络·python·fastapi
Mongnewer5 小时前
通过虚拟串口和网络UDP进行数据收发的Delphi7, Lazarus, VB6和VisualFreeBasic实践
网络
我也要当昏君5 小时前
6.5 万维网(答案见原书P294)
网络
嶔某6 小时前
网络:传输层协议UDP和TCP
网络·tcp/ip·udp
文火冰糖的硅基工坊6 小时前
[嵌入式系统-154]:各种工业现场总线比较
网络·自动驾驶·硬件架构