网络编程

网络编程

当数据交给上一层的时候,是由哪个协议负责进行解析呢??

比如,数据链路层=>网络层,交给IPv4解析?IPv6解析?网终层=>传输层交绘TCP解析还是IDP2。

socket=>操作系统提供的网络编程的API就称为socketapi(插槽),可以认为是"网络编程api"的统称

1.流式套接字=>给TCP使用的

TCP的特点:有连接,可靠传输,面向字节流,,全双工。

2.数据报套接字=>给UDP使用的

UDP1的特点:无连接,不可靠传输,面向数据报,全双工

TCP和UDP都是传输层协议,都是给应用层提供服务的

有连接vs无连接

连接即是要建立连接的双方各自保存对方的信息。

有连接

就像打电话,只有对方允许后才可以接听,也可以选择直接挂掉

通信双方保存对方的信息

无连接

就像发短信/发微信,不需要"先接通",直接上来就发

通信双方不需要保存对方的信息

可靠传输vs不可靠传输

可靠传输

可靠!=安全(传输的数据是否容易被黑客截获掉,一日被截获之后是否会造成严重的影响)

尽力做到数据达到对象进行传输。

在网络信息通信的过程中,A给B传输10个数据报,B实际上只收到9个。

由于网络环境太复杂了,A传输给B,中间可能会经历很多的交换机和路由器,进行转发这些交换机和路由器,也不只是转发你的数据,要转发很多数据。但是如果数据进行堵塞的时候,数据会直接被丢弃掉。

但是如果遇到物理阻断的时候就会导致数据无法传输。

不可靠传输

不可靠传输.传输数据的时候,压根不关心,对方是否收到.发了就完了。

区别:效率问题

面向字节流VS面向数据报

面向字节流

文件操作,就是字节流的字节流,比喻成水流一样.读写操作非常灵活

面向数据报

传输数据的基本单位,是一个个的UDP数据报一次读写,只能读写一个完整的UDP数据报,不能搞半个数据报。

网络传输数据的基本单位

数据报 Datagram UDP

数据段 Segmen tTCP

数据包 Packet IP

数据帧 Frame 数据链路层

全双工VS半双工

全双工:一条链路,能够进行双向通信(TCP,UDP都是全双工)

创建socket对象,既可以读(接受)也可以写(发送)

半双工:一条链路,只能进行单向通信

"C语言中学到的管道"pipe,就属于半双工)

UDP

socketapi都是系统提供的.(不同系统,提供的api是不一样)Java中对于系统的这些api进一步的封装了.

1.DatagramSocket

DatagramSocket就是对于操作系统的socket(系统中的socket,可以理解成是一种文件(针对socket文件的读写操作就相当于针对网卡这个硬件设备进行读写)

文件是一个广义的概念~)

概念的封装

此处,DatagramSocket就可以视为是"操作网卡"的遥控器(句柄)针对这个对象进行读写操作,就是在针对网卡进行读写操作。

2.DatagramPacket

一个DatagramPacket对象,就相当于一个UDP数据报一次发送/一次接受,就是传输了一个DatagramPacket对象。

回显(Echo):正常的服务器,你给他发不同的请求,会返回不同的响应。

网络编程,本质上也是IO操作

java 复制代码
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UdpEchoServer {
    private DatagramSocket socket=null;
    public UdpEchoServer(int port) throws IOException {
        socket = new DatagramSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动");
        while(true){
            //1.读取请求并解析
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(requestPacket);
            //将java代码中处理可以把上述数据中的二进制数据,拿出来,构成String
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());

            //2.根据请求计算响应
            String response=process(request);
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),0,response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
            System.out.println(responsePacket.getAddress());
        }

    }
    //回显服务器
    public String process(String request){
        return request;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer udpEchoServer=new UdpEchoServer(9090);
        udpEchoServer.start();

    }
}

客户端是主动的一方服务器是被动的一方

服务器程序,需要在程序启动的时候,把端口号确定下来

服务器端口号的要求:

1.端口号是合法的1-65535

2.注意端口号不能和别的进程使用的端口号冲突。

写代码的时候,很少会有这样的操作,持有数据的空间,需要额外的创建(但是socket编程原生api,就都是这种风格,这样的风格也延续到java中了)

服务器服务器的ip和端口得是固定的,不能老变。

客户端也需要端口号!!

在进行一次通信的过程中,需要知道至少4个核心指标

:1)源IP发件人地址

2)源端口发件人电话

3)目的IP收件人地址

4)目的端口收件人电话

构造socket对象的时候,没有指定端口号,没指定不代表没有而是操作系统,自动分配了一个空闲的(不和别人冲突)的端口号过来了这个自动分配的端口号,每次重新启动程序都可能不一样。

为啥服务器需要有固定端口号,而客户端就需要让系统自动分配?反过来行不行??

1)服务器要有固定端口号,是因为,客户端需要主动给服务器发请求如果服务器端口号不是固定的,(假设是每次都变,此时客户端就不知道请求发给谁了)

2)客户端为啥要系统自动分配,指定固定的端口号行不行呢?

如果就给客户端指定固定的端口号,是不行的!!因为指定的固定端口号,是可能会和客户端所在电脑上的其他的程序,冲突的一旦端口冲突,就会导致你的程序启动不了了。

这里应用的是回显服务器(请求是啥,响应就是啥)

一个正常的服务器,要做三个事情

1.读取请求并解析

2.根据请求,计算响应

3.把响应写回到客户端

java 复制代码
package network;

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

public class UdpEchoClient {
private DatagramSocket socket = null;
private String serverIp;
private int serverPort;

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) {
        System.out.print("请输入要发送的请求: ");
// 1. 从控制台读取用户输入
        String request = scanner.next();
// 2. 构造请求并发送
// 构造请求数据报的时候, 不光要有数据, 还要有 "目标"
        DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), 0, request.getBytes().length,
                InetAddress.getByName(serverIp), serverPort);
        socket.send(requestPacket);
// 3. 读取响应数据
        DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
        socket.receive(responsePacket);
// 4. 显示响应到控制台上.
        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);
//        UdpEchoClient client = new UdpEchoClient("139.155.74.81", 9090);
        client.start();
    }
}

"127.0.0.1"服务器和客户端在一个主机上此时就固定写这个ip地址(环回ip,系统提供的特殊的ip)

9090:此处写的是服务器在创建建socket对象时,指定的端口号。

getBytes().length();和.length()方法的区别:

前者是得到字节数组然后通过字节十足的长度,这里的长度单位是"字节数",我们可以看到request是String类型所以需要取字符

后者是是长度单位是字符数,本身就是字符类型所以不需要取字符。

如果不转换会发生那些问题呢,如果字符串是英文字母或者数字的话问题不大,但是在字符串中出现了字符或者汉字来说,就会导致长度变小,例:java中String的字节大多都是三个字符,如果不改变就会导致3倍差距。

客户端流程: 服务器流程

1.从控制台读取字符串

2.把字符串发送给服务器-----

| 1.读取请求并解析

ms级别 2.根据请求计算响应

| 3.把响应写回到客户端

3.从服务器读取到响应-------- (如果没有收到服务器的响应会阻塞)

4.把响应打印到控制台上

先运行服务器,后运行客户端

如果不能开多个客户端就可以这样操作

当两个电脑在同一个局域网下,才可以进行互联。

主要是因为电脑的ip不行,我们用的是私网ip但是另一个是公网ip。(可以通过租服务器解决)。

私网ip有

1.10.

2.172.16-172.31.

3.192.168.

java 复制代码
import java.io.IOException;
import java.util.HashMap;

public class UdpDictServer extends UdpEchoServer {
    private HashMap<String, String> dict = null;

    public UdpDictServer(int port) throws IOException {
        super(port);
        dict = new HashMap<>();
        dict.put("hello", "world");
        dict.put("123", "456");
        dict.put("12345", "上山打老虎");
    }

    @Override
    public String process(String message) {
        return dict.getOrDefault(message, "没有查到");
    }
}

可以通过继承已有的方法来实现process的方法。

不用调用close 的原因是因为scocket对象不能释放,结束了就会自动释放了。

TCP

TCP的socket api

1.ServerSocke

t专门给服务器用的ServerSocket(intport)

Socketaccept()接听操作

void close()

Socket
java 复制代码
Socket(String host, int port)   构造方法本身,就能够和指定的服务器,简历连接(拨号的过程)

InputStream getlnputStream()  获取到socket内部持有的流对象

OutputStream   getOutputStream()   也是通过InputStream和OutputStream来操作的,通过read和write来进行。

InetAddress getlnetAddress()   获取地址
java 复制代码
private serverSocket=null;
Socket clientSocket =serverSocket.accept();

服务器一启动,就会立即执行到这里如果客户端没有连接过来,accept也会产生阻塞直到说有客户端真的连接上来了!

相关推荐
铁蛋Q8 分钟前
进程的状态
linux·服务器·ubuntu
嘻嘻仙人23 分钟前
【网络通信基础与实践第四讲】用户数据报协议UDP和传输控制协议TCP
网络·网络协议·udp·tcp·三次握手·流量控制·拥塞控制
极客小张25 分钟前
基于正点原子Linux开发板的智能监控与家电控制系统设计:深度解析Video4Linux和TCP/IP技术栈
linux·运维·c++·物联网·网络协议·tcp/ip·算法
sunxunyong25 分钟前
Linux 删除文件不释放空间问题处理
大数据·linux·运维·服务器
只对您心动1 小时前
【C高级】有关shell脚本的一些练习
linux·c语言·shell·脚本
lldhsds1 小时前
linux下的分布式Minio部署实践
linux·minio·分布式对象存储
OH五星上将2 小时前
OpenHarmony(鸿蒙南向开发)——小型系统内核(LiteOS-A)【内核通信机制】上
linux·嵌入式硬件·harmonyos·openharmony·鸿蒙开发·liteos-a·鸿蒙内核
DC_BLOG3 小时前
IPv6(四)
运维·服务器·网络·ip
shelby_loo3 小时前
通过 Docker 部署 MySQL 服务器
服务器·mysql·docker