锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

从java代码到网络编程

时间:2022-08-22 18:30:01 连接器主线

学习目录

  • 前言
    • 一、重温计网
    • 二、从代码到网络
      • 1.InetAddress类
      • 2.Datagram类
        • 2.1DatagramPacket类
        • 2.2DatagramSocket类
    • 三、UDP一发一收模型
    • 四、UDP多发多收模型
    • 五、TCP一发一收模型
    • 六、TCP多发多收模型
    • 七、多线程实现多用户通信
  • 面试八股文整理

前言

在计算网络的基础上,设备之间的通信是通过网络实现数据传输,数据通过网络从一个设备传输到另一个设备。我们知道在以前的研究中java.lang包提供基础库,java.io包中提供io功能函数,而java.net包中提供网络连接的类别或接口让我们实现网络通信,其实Java网络编程的本质是面向接口和类编程

一、重温计网

局域网:覆盖面最小,只覆盖一间教室或一间机房
城域网:覆盖面大,可覆盖城市
广域网:万维网是广域网的代表
IP地址:网络中的设备标记,每个主机都有唯一的标记ip地址,主要分为IPv4,IPv6
在这里插入图片描述
地址形式:公网地址、私网地址、特殊地址ip地址:127.0.0.1或者localhost表示本机
域名:解决记忆ip地址难的问题导致域名,比如www.baidu.com
端口号:标识计算机上特定的网络程序,被规定为一个16位的二进制,范围是0~65535,比如tomcat——8080、mysql——3306、sqlserver——1433…
端口类型:周知端口:0~1023,被预定义的知名应用占用(如:HTTP占用80,FTP占用21)
注册端口:1024~49151,分配给用户流程或某些应用程序。Tomcat占用8080,MySQL占用3306)
动态端口:49152~65535之所以被称为动态端口,是因为它通常不固定分配某个过程,而是动态分配
注意:当我们自己开发的程序选择注册端口时,同一设备不能有相同的程序,否则错误报告
网络通信模型
1.OSI模型自上而下:应用层-表示层-会话层-传输层-网络层-数据链路层-物理层
2.TCP/IP模型自顶向下:应用层-传输层(TCP)-网络层(IP)-物理 数据链路层
协议:常见的协议包括网络中数据传输的规则UDP协议和TCP协议。
TCP协议
使用TCP协议前必须建立TCP连接是一种面向连接的可靠通信协议。在传输数据通道传输之前,采用三次握手的方式是可靠的,TCP协议通信的两个应用程序:客户端和服务端可以在连接中传输大数据。传输完成后,需要释放已建立的连接,效率低下
TCP协议通信:

UDP协议
用户数据报告协议将数据、源和目的包装成数据包,无需建立连接。每个数据报告的大小限制在64K内部,不适合传输大量数据,因为不需要连接,所以不可靠,发送数据不需要释放资源(因为不是面向连接),速度快,效率高,但不安全,容易丢失数据
套接字Socket
套接字(Socket)开发网络应用程序被广泛使用,从而成为实际标准。通信的两端都有Socket,它是两台机器之间通信的终点。网络通信实际上是Socket间的通信,Socket允许程序将网络连接视为一流,数据在两个Socket间通过IO传输。一般来说,主动发起通信的应用程序属于客户端,等待通信请求为服务端

二、从代码到网络

网络编程的本质是面向接口和类编程,Java核心类库为我们提供了面向通信协议和网络模型编程的渠道,这是面向对象思想的体现
所以,我们通过API连接端口,面向协议就可以实现网络编程

1.InetAddress类

InetAddress类用于表示Internet协议(IP)地址,是序列接口Serializable同时也是实现类Inet4Address、Inet6Address的父类

名称 说明
public static InetAddress getLocalHost() 返回主机地址对象
public static InetAddress getByName(String host) 指定主机IP参数是域名或地址对象IP地址
public String getHostName() 获取此IP地址主机名
public String getHostAddress () 返回IP地址字符串
public boolean isReachable(int timeout) 在指定毫秒内连接IP地址对应的主机连接返回true

代码演示:

        // 1.获取本机地址对象。 InetAddress ip1 = InetAddress.getLocalHost(); System.out.println(ip1.getHostName()); System.out.println(ip1.getHostAddress()); // 2.获取域名ip对象
InetAddress ip2 = InetAddress.getByName("www.baidu.com");
System.out.println(ip2.getHostName());
System.out.println(ip2.getHostAddress());
// 3.获取公网IP对象。
InetAddress ip3 = InetAddress.getByName("112.80.248.76");
System.out.println(ip3.getHostName());
System.out.println(ip3.getHostAddress());

2.Datagram类

2.1DatagramPacket类

主要用于创建发送端数据包对象
构造器:
public DatagramPacket(byte[] buf,int length,InetAddness address,int port)

buf 要发送的内容,字节数组
length 要发送内容的字节长度
address 接收端的IP地址对象
port 要发送的内容,字节数组

成员方法:public int getLength()来获取实际收到的字节数

2.2DatagramSocket类

主要用于发送端和接收端对象
构造器:

public DatagramSocket() 创建发送端的Socket对象,系统会随机分配一个端口号。
public DatagramSocket(int port) 创建接收端的Socket对象并指定端口号

成员方法:
public void send(DatagramPacket dp)用于发送数据包
public void receive(DatagramPacket dp)用于接收数据包

三、UDP一发一收模型


客户端:

①创建DatagramSocket对象(发送端对象)
②创建Datagramacket对象封装需要发送的数据(数据包对象)
③使用DatagramSocket对象的send方法传入DatagramPacket对象
④释放资源

// 1、创建发送端对象 发送端自带默认的端口号
DatagramSocket socket = new DatagramSocket(6666);
// 2、创建一个数据包对象封装数据
byte[] buffer = "hello java".getBytes();
DatagramPacket packet = new DatagramPacket( buffer, buffer.length,InetAddress.getLocalHost() , 8888);
// 3、发送数据出去
socket.send(packet);
// 4、关闭连接
socket.close();

服务器端:

①创建DatagramSocket对象并指定端口(接收端对象)
②创建DatagramPacket对象接收数据(数据包对象)
③使用DatagramSocket对象的receive方法传入DatagramPacket对象
④释放资源

// 1、创建接收端对象:注册端口
DatagramSocket socket = new DatagramSocket(8888);
// 2、创建一个数据包对象接收数据
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// 3、等待接收数据。
socket.receive(packet);
// 4、取出数据即可
int len = packet.getLength();
String rs = new String(buffer,0, len);
System.out.println("收到:" + rs);
// 获取发送端的ip和端口
String ip  =packet.getSocketAddress().toString();
System.out.println("对方地址:" + ip);
int port  = packet.getPort();
System.out.println("对方端口:" + port);
socket.close();

四、UDP多发多收模型

客户端:

①创建DatagramSocket对象(发送端对象)
②使用while死循环不断的接收用户的数据输入,如果用户输入"back"则退出程序
③如果用户输入的不是exit,把数据封装成DatagramPacket
④使用DatagramSocket对象的send方法将数据包对象进行发送—开 ⑤释放资源

// 1、创建发送端对象:发送端自带默认的端口号
DatagramSocket socket = new DatagramSocket(7777);       
Scanner scanner = new Scanner(System.in);
while (true) { 
        
  System.out.println("请说:");
  String msg = scanner.nextLine();
    if("back".equals(msg)){ 
        
        System.out.println("离线成功!");
            socket.close();
            break;
       }
// 2、创建一个数据包对象封装数据
byte[] buffer = msg.getBytes();
DatagramPacket packet = new DatagramPacket( buffer, buffer.length,InetAddress.getLocalHost() , 8888);
// 3、发送数据出去
    socket.send(packet);
 }

服务器端:

①创建DatagramSacket对象并指定端口(接收端对象)
②创建DatagramPacket对象接收数据(数据包对象)
③使用while死循环不断的进行第4步
④使用DatagramSocket对象的receive方法传入DatagramPacket对象

// 1、创建接收端对象:注册端口
DatagramSocket socket = new DatagramSocket(8888);
// 2、创建一个数据包对象接收数据
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while (true) { 
        
   // 3、等待接收数据
   socket.receive(packet);
   // 4、取出数据即可
   // 读取多少倒出多少
   int len = packet.getLength();
   String rs = new String(buffer,0, len);
   System.out.println("收到了来自:" + packet.getAddress() +", 对方端口是" +packet.getPort() +"的消息:" + rs);
     }

之所以可以接收很多发送端的消息是因为服务器的接收端只负责接收数据包
扩展
UDP实现广播:发送端发送的数据包的目的地写的是广播地址,且指定端口,本机所在的网段的机群程序注册对应端口
UDP实现组播:发送端的数据包目的地是组播IP,接收端必须绑定改组播IP,端口要注册发送端的目的端口

五、TCP一发一收模型

客户端

①创建客户端的Socket对象,请求与服务端的连接
②使用socket对象调用getQutputStream()方法得到字节输出流
③使用字节输出流完成数据的发送
④释放资源:关闭socket管道

// 1、创建Socket通信管道请求有服务端的连接
Socket socket = new Socket("127.0.0.1", 7777);
// 2、从socket通信管道中得到一个字节输出流 负责发送数据
OutputStream os = socket.getOutputStream();
// 3、把低级的字节流包装成打印流
PrintStream ps = new PrintStream(os);
// 4、发送消息
ps.println("hello java");
ps.flush(); //刷新!
// 关闭资源
socket.close();

服务器端
注册端口调用方法接收就完事儿

// 1、注册端口
ServerSocket serverSocket = new ServerSocket(7777);
// 2、必须调用accept方法:等待接收客户端的Socket连接请求,建立Socket通信管道
Socket socket = serverSocket.accept();
// 3、从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
// 4、把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// 5、按照行读取消息
String msg;
if ((msg = br.readLine()) != null){ 
        
 System.out.println(socket.getRemoteSocketAddress() + "收到的内容是:: " + msg);
  }

六、TCP多发多收模型

客户端

①创建ServerSocket对象,注册服务端端口
②调用ServerSacket对象的accept()方法,等待客户端的连接,并得到Socket管道对象
③通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收
④释放资源:关闭socket管道

// 1、创建Socket通信管道请求有服务端的连接
Socket socket = new Socket("127.0.0.1", 7777);
// 2、从socket通信管道中得到一个字节输出流 负责发送数据
OutputStream os = socket.getOutputStream();
// 3、把低级的字节流包装成打印流
PrintStream ps = new PrintStream(os);
Scanner sc =  new Scanner(System.in);
while (true) { 
        
  System.out.println("请发送:");
  String msg = sc.nextLine();
// 4、发送消息
  ps.println(msg);
  ps.flush();
   }
// 关闭资源。
socket.close();

服务器端
接收请求,io读取

   // 1、注册端口
ServerSocket serverSocket = new ServerSocket(7777);
while (true) { 
        
   // 2、必须调用accept方法:等待接收客户端的Socket连接请求,建立Socket通信管道
Socket socket = serverSocket.accept();
   // 3、从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
   // 4、把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
   // 5、按照行读取消息
String msg;
  while ((msg = br.readLine()) != null){ 
        
System.out.println(socket.getRemoteSocketAddress() + "收到的内容是:: " + msg);
       }
   }

上述一发一收、多发多收的本质还是客户端发,服务器端接、客户端循环发,服务器端循环接
服务器端是单线程的,每次只能处理一个客户端的消息

七、多线程实现多用户通信

设计思路:
服务器端主线程定义一个循环体用来接收客户端Socket连接,每连接到一个Socket通道后分配一个独立的线程来处理它,如图所示

1.此时的客户端还是在不断地发消息

// 1、创建Socket通信管道请求有服务端的连接
  Socket socket = new Socket("127.0.0.1", 7777);
// 2、从socket通信管道中得到一个字节输出流 负责发送数据
  OutputStream os = socket.getOutputStream();
// 3、把低级的字节流包装成打印流
  PrintStream ps = new PrintStream(os);
  Scanner sc =  new Scanner(System.in);
   while (true) { 
        
   System.out.println("请说:");
   String msg = sc.nextLine();
// 4、发送消息
   ps.println(msg);
   ps.flush();
    }
   // 关闭资源。
socket.close();

2.这时的服务器端要同时处理多个客户端的通信需求
首先把主线程写好,无限循环用来接收客户端的消息

// 1、注册端口
   ServerSocket serverSocket = new ServerSocket(7777);
// 定义一个死循环由主线程负责不断的接收客户端的Socket管道连接。
    while (true) { 
        
// 2、每接收到一个客户端的Socket管道,交给一个独立的子线程负责读取消息
 Socket socket = serverSocket.accept();
 System.out.println(socket.getRemoteSocketAddress()+ "鸡汤来咯~");
// 3、开始创建独立线程处理socket
 new ServerReaderThread(socket).start();
  }

3.实现Thread类中的子线程用来处理主线程随机分配下来的的任务,主线程无限的接,接到一个消息主线程就new一个子线程然后start()

public class ServerReaderThread extends Thread{ 
        
    private Socket socket;
    public ServerReaderThread(Socket socket){ 
        this.socket = socket;}
    @Override
    public void run() { 
        
         // 3、从socket通信管道中得到一个字节输入流
         InputStream is = socket.getInputStream();
         // 4、把字节输入流包装成缓冲字符输入流进行消息的接收
         BufferedReader br = new BufferedReader(new InputStreamReader(is));
         // 5、按照行读取消息
         String msg;
         while ((msg = br.readLine()) != null){ 
        
           System.out.println(socket.getRemoteSocketAddress() + "收到的消息是:: " + msg);
          }      
    }
}

实现同时处理多个客户端消息的本质,就是把创建子线程的代码写到了服务器端接收消息的死循环里
上述的多用户通信模式存在一定的问题
由于是N-N的关系,所以并发问题比较严重,这个问题就等到学完并发编程再回来优化吧

面试八股文整理

面试官:假如子网掩码维255.255.255.245有多少个ip可用?

256-245-2=9个

面试官:判断192.162.1.1 是A、B、C类那种网络ip地址?

C类、C类P以110开头,从192.0.0.1到223.255.255.255

面试官:TCP和UDP有什么区别?

1.TCP提供面向连接的传输,通信前要先建立连接(三次握手机制);UDP提供无连接的传输,通信前不需要建立连接
2.TCP提供可靠的传输(有序,无差错,不丢失,不重复);UDP提供不可靠的传输
3.TCP面向字节流的传输,因此它能将信息分割成组,并在接收端将其重组;UDP是面向数据报的传输,没有分组开销
4.TCP提供拥塞控制和流量控制机制;UDP不提供拥塞控制和流量控制机制

面试官:进程间通讯的方式有哪些?

1.管道:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程之间使用。进程的亲缘关系通常是指父子进程关系
2.有名管道(FIFO):有名管道也是半双工的通信方式,但是允许在没有亲缘关系的进程之间使用,管道是先进先出的通信方式。
3.信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种
机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
4.消息队列:消息队列是有消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
5.信号 ( sinal ) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
6.共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC
方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
7.套接字( socket ) :套接字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信

面试官:tcp连接建立时三次握手的具体过程,以及每一步原因?

第一步:源主机A的TCP向主机B发出连接请求报文段,其首部中的SYN(同步)标志位应置为1,表示想与目标主机B进行通信,并发送一个同步序列号X(例:SEQ=100)进行同步,表明在后面传送数据时的第一个数据字节的序号是X+1(即101)。SYN同步报文会指明客户端使用的端口以及TCP连接的初始序号
第二步:目标主机B的TCP收到连接请求报文段后,如同意,则发回确认。在确认报中应将ACK位和SYN位置1,表示客户端的请求被接受。确认号应为X+1(图中为101),同时也为自己选择一个序号Y。
第三步:源主机A的TCP收到目标主机B的确认后要向目标主机B给出确认,其ACK置1,确认号为Y+1,而自己的序号为X+1。TCP的标准规定,SYN置1的报文段要消耗掉一个序号

——部分题目引自《常见网络编程面试题整理》作者:繁华如梦

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章