Java程序设计(高级及专题)- 网络编程

2022-08-04 16:22:40 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

概念

1.协议的概念:通信双方事先约定好的通信规则 2.七层网络通信协议;应用成,表示层,会话层,传输层,网络层,数据链路层 3.TCP/IP协议:点对点通信,三层握手,安全有保证 4.UDP协议:广播协议,不保证数据安全但很高效 5.重要APl:InetAddress类,URl类,ServerSocked类,DatagramSocket类 6.网络编程主要有两部分:一是获取管理socket连接,二是获取使用输入输出流传输数据

网络模型

OSI参考模型

代码语言:javascript复制
    物理层(Physical Layer):建立、维护、断开物理连接。
    数据链路层 (Link):逻辑连接、进行硬件地址寻址、差错校验等。
    网络层 (Network):进行逻辑寻址,实现不同网络之间的路径选择。
    传输层 (Transport):定义传输数据的协议端口号,及流控和差错校验。
    会话层(Session Layer):建立、管理、终止会话。
    表示层(Presentation Layer):数据的表示、安全、压缩。
    应用层 (Application):网络服务与最终用户的一个接口
  • 物理层 物理层处于OSI的最底层,是整个开放系统的基础。物理层涉及通信信道上传输的原始比特流(bits),它的功能主要是为数据端设备提供传送数据的通路以及传输数据。
  • 数据链路层 数据链路层的主要任务是实现计算机网络中相邻节点之间的可靠传输,把原始的、有差错的物理传输线路加上数据链路协议以后,构成逻辑上可靠的数据链路。需要完成的功能有链路管理、成帧、差错控制以及流量控制等。其中成帧是对物理层的原始比特流进行界定,数据链路层也能够对帧的丢失进行处理。
  • 网络层 网络层涉及源主机节点到目的主机节点之间可靠的网络传输,它需要完成的功能主要包括路由选择、网络寻址、流量控制、拥塞控制、网络互连等。
  • 传输层 传输层起着承上启下的作用,涉及源端节点到目的端节点之间可靠的信息传输。传输层需要解决跨越网络连接的建立和释放,对底层不可靠的网络,建立连接时需要三次握手,释放连接时需要四次挥手。
  • 会话层和表示层 会话层的主要功能是负责应用程序之间建立、维持和中断会话,同时也提供对设备和结点之间的会话控制,协调系统和服务之间的交流,并通过提供单工、半双工和全双工3种不同的通信方式,使系统和服务之间有序地进行通信。 表示层关心所传输数据信息的格式定义,其主要功能是把应用层提供的信息变换为能够共同理解的形式,提供字符代码、数据格式、控制信息格式、加密等的统一表示。
  • 应用层 应用层为OSI的最高层,是直接为应用进程提供服务的。其作用是在实现多个系统应用进程相互通信的同时,完成一系列业务处理所需的服务。

TCP/IP参考模型

-网络接口层 TCP/IP协议对网络接口层没有给出具体的描述,网络接口层对应着物理层和数据链路层。

  • 互联网层 ( IP层 ) 互联网层是整个TCP/IP协议栈的核心。它的功能是把分组发往目标网络或主机。同时,为了尽快地发送分组,可能需要沿不同的路径同时进行分组传递。因此,分组到达的顺序和发送的顺序可能不同,这就需要上层必须对分组进行排序。互联网层除了需要完成路由的功能外,也可以完成将不同类型的网络(异构网)互连的任务。除此之外,互联网层还需要完成拥塞控制的功能。   -传输层 ( TCP层 ) TCP层负责在应用进程之间建立端到端的连接和可靠通信,它只存在与端节点中。TCP层涉及两个协议,TCP和UDP。其中,TCP协议提供面向连接的服务,提供按字节流的有序、可靠传输,可以实现连接管理、差错控制、流量控制、拥塞控制等。UDP协议提供无连接的服务,用于不需要或无法实现面向连接的网络应用中。
  • 应用层 应用层为Internet中的各种网络应用提供服务

UDP网络程序

使用UDP协议的程序流程简单很多,它收发包流程如下:

  • UDP网络通信的发包过程: 使用DatagramSocket()创建一个数据包套接字。 使用DatagramPacket(byte[]buf,int offset,int length,InetAddress address,int port)创建要发送的数据包。 使用DatagramSocket类的send()方法数据包。
  • UDP网络通信的收包过程: 使用DatagramSocket()创建一个数据包套接字,绑定到指定的端口。 使用DatagramPacket(byte[]buf,int length)创建字节数组来接收数据包. 使用DatagramPacket类的receive()方法接收UDP。

TCP网络程序

  • ServerSocket类 Java.net包中的ServerSocket类用于表示服务器套接字,其主要功能是监听客户端的请求,然后将客户端的请求连接存入队列中,默认请求队列大小是50。

构造方法主要有以下几种形式: ServerSocket():创建非绑定服务器套接字。 ServerSocket(int port):创建绑定到特定端口的服务器套接字。 ServerSocket(int port,int backlog):利用指定的backlog创建服务器套接字并将其绑定到指定的本地端口号。 ServerSocket(int port,int backlog,InetAdress bindAddress):使用指定的端口、监听backlog和要绑定到本地IP地址创建服务器,适用于计算机有多个网卡、多个IP的情景。 实例:

代码语言:javascript复制
//服务端程序
import java.io.*;
import java.net.*;
public class MyTcpServer { 
   
	private BufferedReader reader;
	private PrintWriter writer;
	private ServerSocket server;
	private Socket socket;
	void getserver(){ 
   
		try{ 
   
			server = new ServerSocket(8866);
			System.out.println("服务器套接字已经创建成功");
			while(true){ 
   
				System.out.println("等待客户机的连接");
				socket = server.accept();	//阻塞的
				reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
				writer = new PrintWriter(socket.getOutputStream(),true);
				getClientMessage();			}
		}catch(Exception e){ 
   
			e.printStackTrace();
		}
	}
	private void getClientMessage(){ 
   
		try{ 
   
			while(true){ 
   
				System.out.println("客户端信息接收:"  reader.readLine());
				writer.println("欢迎您连接服务端");
			}
		}catch(Exception e){ 
   
			e.printStackTrace();
		}
		try{ 
   
			if(reader != null){ 
   
				reader.close();
			}
			if(writer != null){ 
   
				writer.close();
			}
			if(socket != null){ 
   
				socket.close();
			}
		}catch(IOException e){ 
   
			e.printStackTrace();
		}
	}
}
public class Main { 
   
	public static void main(String[] args) { 
   
		MyTcpServer tcpserv = new MyTcpServer();
		tcpserv.getserver();
	}
 
}
代码语言:javascript复制
//客户端程序
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
 
 
public class MyTcpClient { 
   
	private PrintWriter writer;
	private BufferedReader reader;
	Socket socket;
	public void connect(){ 
   
		System.out.println("尝试连接");
		try{ 
   
			socket = new Socket("127.0.0.1",8866);
			writer = new PrintWriter(socket.getOutputStream(),true);
			reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			System.out.println("连接成功");
			writer.println("你好,来自客户端的连接");
			getserverMessage();
		}catch(Exception e){ 
   
			e.printStackTrace();
		}
	}
	private void getserverMessage(){ 
   
		try{ 
   
			while(true){ 
   
				System.out.println(":"  reader.readLine());				
			}
		}catch(Exception e){ 
   
			e.printStackTrace();
		}
		try{ 
   
			if(reader != null){ 
   
				reader.close();
			}
			if(writer != null){ 
   
				writer.close();
			}
			if(socket != null){ 
   
				socket.close();
			}
		}catch(IOException e){ 
   
			e.printStackTrace();
		}
	}
	
}
 
public class Main { 
   
	public static void main(String[] args) { 
   
		MyTcpClient tcpclient;
		tcpclient = new MyTcpClient();
		tcpclient.connect();
	}
 
}

服务端控制台:

客户端控制台:

总结:网络编程是程序实现网络通信的基石,在互联网高度繁荣的当下,几乎没有什么应用不需要网络支持。网络通信基于TCP/IP,基于此,网络编程可以选择使用TCP传输或UDP传输,它们两个是比较底层的通信协议,TCP提供可靠的连接,UDP则不提供可靠的连接,在实际应用中大多数选用TCP,而UDP主要用于音视频、NTP对时这种对数据可靠性要求不是那么高的场合。

IO模型

对于一次IO访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区page cache中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个read操作发生时,它会经历两个阶段:

代码语言:javascript复制
  等待数据准备
  将数据从内核拷贝到进程中

IO模型的分类有下:

代码语言:javascript复制
阻塞 I/O(blocking IO)
非阻塞 I/O(nonblocking IO)
I/O 多路复用( IO multiplexing)
异步 I/O(asynchronous IO)

BIO 阻塞 I/O

缺点:一个请求一个线程,浪费线程,且上下文切换开销大; 当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据(对于网络IO来说,很多时候数据在一开始还没有到达。比如,还没有收到一个完整的UDP包。这个时候kernel就要等待足够的数据到来)。这个过程需要等待,也就是说数据被拷贝到操作系统内核的缓冲区中是需要一个过程的。而在用户进程这边,整个进程会被阻塞(当然,是进程自己选择的阻塞)。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。

NIO 非阻塞 I/O

当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error 。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。 nonblocking IO的特点是用户进程需要不断的主动询问kernel数据好了没有。

I/O 多路复用

IO multiplexing就是我们说的select,poll,epoll,有些地方也称这种IO方式为event driven IO。select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select,poll,epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。 机制:一个线程以阻塞的方式监听客户端请求;另一个线程采用NIO的形式select已经接收到数据的channel信道,处理请求;Linux的多种IO模型以及select,poll,epoll模型 – 处理更多的连接。

上面所说的多路复用的select,poll,epoll本质上都是同步IO,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,实际上是指阻塞在select上面,必须等到读就绪、写就绪等网络事件。异步IO则无需自己负责进行读写,异步IO的实现会负责把数据从内核拷贝到用户空间。

代码语言:javascript复制
I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,
而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()
函数就可以返回。所以,如果处理的连接数不是很高的话,使用select/epoll的web
server不一定比使用multi-threading   blocking IO的web
server性能更好,可能延迟还更大。select/
epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/106664.html原文链接:https://javaforall.cn

0 人点赞