网络编程 套接字

发送端和接收端

在一次网络数据传输时:

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

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

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

客户端和服务端

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

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

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

  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 byte4096,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();
    }
}

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

相关推荐
技术不好的崎鸣同学2 小时前
[极客大挑战 2019]EasySQL 思路及解法
网络·安全·web安全
Web极客码2 小时前
跨国网络抖动下的 AI 爬虫流调优:我如何用 Claude 打造“智能退避”资讯清洗管道
网络·人工智能·爬虫
念何架构之路3 小时前
moby-http-api-server
网络·网络协议·http
namexingyun3 小时前
Scaling Law bug实战启示:从“虚胖“到“精瘦“的算力效率革命
开发语言·网络·人工智能·bug·ai编程
云栖梦泽在3 小时前
原生 IP、机房 IP、住宅 IP、广播 IP 有何不同?从网络身份到 ASN 识别的技术科普
网络·网络协议·tcp/ip·性能优化
AI创界者4 小时前
【实战演练】基于 Kali Linux 的自动化漏洞扫描与安全加固指南
网络·安全·web安全
从零开始的代码生活_4 小时前
Linux epoll 多路转接详解
linux·运维·网络·后端·tcp/ip·计算机网络·php
小鹿软件办公5 小时前
通过调整编码参数使用 VLC 媒体播放器无损压缩视频方法
运维·网络·音视频
魔尔助理顾问5 小时前
HTTP Response中的CORS Headers
网络·网络协议·http
yqcoder6 小时前
什么是 Math 对象
网络·网络协议·udp