大家好,又见面了,我是你们的朋友全栈君。
1.协议的概念:通信双方事先约定好的通信规则 2.七层网络通信协议;应用成,表示层,会话层,传输层,网络层,数据链路层 3.TCP/IP协议:点对点通信,三层握手,安全有保证 4.UDP协议:广播协议,不保证数据安全但很高效 5.重要APl:InetAddress类,URl类,ServerSocked类,DatagramSocket类 6.网络编程主要有两部分:一是获取管理socket连接,二是获取使用输入输出流传输数据
物理层(Physical Layer):建立、维护、断开物理连接。
数据链路层 (Link):逻辑连接、进行硬件地址寻址、差错校验等。
网络层 (Network):进行逻辑寻址,实现不同网络之间的路径选择。
传输层 (Transport):定义传输数据的协议端口号,及流控和差错校验。
会话层(Session Layer):建立、管理、终止会话。
表示层(Presentation Layer):数据的表示、安全、压缩。
应用层 (Application):网络服务与最终用户的一个接口
-网络接口层 TCP/IP协议对网络接口层没有给出具体的描述,网络接口层对应着物理层和数据链路层。
使用UDP协议的程序流程简单很多,它收发包流程如下:
构造方法主要有以下几种形式: ServerSocket():创建非绑定服务器套接字。 ServerSocket(int port):创建绑定到特定端口的服务器套接字。 ServerSocket(int port,int backlog):利用指定的backlog创建服务器套接字并将其绑定到指定的本地端口号。 ServerSocket(int port,int backlog,InetAdress bindAddress):使用指定的端口、监听backlog和要绑定到本地IP地址创建服务器,适用于计算机有多个网卡、多个IP的情景。 实例:
//服务端程序
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();
}
}
//客户端程序
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访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区page cache中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个read操作发生时,它会经历两个阶段:
等待数据准备
将数据从内核拷贝到进程中
IO模型的分类有下:
阻塞 I/O(blocking IO)
非阻塞 I/O(nonblocking IO)
I/O 多路复用( IO multiplexing)
异步 I/O(asynchronous IO)
缺点:一个请求一个线程,浪费线程,且上下文切换开销大; 当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据(对于网络IO来说,很多时候数据在一开始还没有到达。比如,还没有收到一个完整的UDP包。这个时候kernel就要等待足够的数据到来)。这个过程需要等待,也就是说数据被拷贝到操作系统内核的缓冲区中是需要一个过程的。而在用户进程这边,整个进程会被阻塞(当然,是进程自己选择的阻塞)。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。
当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error 。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。 nonblocking IO的特点是用户进程需要不断的主动询问kernel数据好了没有。
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的实现会负责把数据从内核拷贝到用户空间。
I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,
而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()
函数就可以返回。所以,如果处理的连接数不是很高的话,使用select/epoll的web
server不一定比使用multi-threading + blocking IO的web
server性能更好,可能延迟还更大。select/
epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/106664.html原文链接:https://javaforall.cn