计算机网络原理

网络发展史

起初,计算机之间是相互独立的,分别完成不同的工作,效率较为低下.随着时代的发展,计算机开始协同完成任务,就有了网络互连.网络互连是指将多台计算机连接在一起,完成数据共享.根据网络互联的规模不同,可以划分为局域网和广域网.局域网,简称LAN.局域网内的主机可以互相进行通信,局域网和局域网之间在没有连接的情况下是不能够进行通信的.广域网,简称WAN,即通过路由器将多个局域网连接起来,在物理上组成很大范围的网络,就形成了广域网.广域网内部的网络都属于其子网.

网络通信基础

网络互连的目的是进行网络通信,那么如何将数据传输到指定的主机呢?这里就需要使用IP地址 来进行标识.IP地址除了可以用于标识网络主机之外,还可以用来标识如路由器等其他网络设备.IP地址用于定位主机的网络地址.IP地址是一个32位的二进制数,通常被分割成4个8位二进制数,将这8位二进制数用十进制数表示出来即一个IP地址可以写成a.b.c.d 其中的字母均表示十进制数字(范围是0-255),这种标识IP地址的方法称为点分十进制法.通过IP地址可以标识指定一台主机,但主机上有很多进程在同时运行,如何找到指定的进程呢?端口号 ,就是用来解决这一个问题的.端口号可以用来标识主机中发送数据,接受数据的进程.端口号是0-65535范围的数字,在网络通信中,进程可以通过绑定一个端口号,来发送以及接受网络数据.有了IP地址和端口号之后,还需要协议来规定主机之间传输的数据格式**.协议,** 网络协议是网络通信经过的所有网络设备都必须共同遵守的一组约定,规则.协议规定了怎样建立连接,怎样互相识别等.只有遵守这个约定,计算机之间才能相互通信交流.协议最终体现为网络上传输的数据包的格式. 一个网络通信中,通常需要源IP,源端口号,目的IP,目的端口号,协议号.**协议分层,**对于网络协议来说,往往分成几个层次进行定义.

TCP/IP五层模型的从上到下的介绍:1.应用层:负责应用间的程序沟通,如简单电子邮件(SMTP),文传输协议(FTP)等.2.传输层:负责两台主机之间的数据传输,如传输控制协议(TCP),能够确保数据可靠的从源主机发送到目的主机. 3.网络层:负责地址管理和路由选择.常见的路由器就是工作在这一层.4.数据链路层:负责设备之间的数据帧的传输和识别,例如网卡设备的驱动,帧同步,冲突检测,数据差错校验等交换机主要工作在这一层.5.物理层:负责光/电信号的传递方式.集线器工作在物理层.

网络编程

网络编程是指网络上的主机,通过不同的进程,以编程的方式实现网络数据传输.比如,我们在网上打开一个网站观看视频.视频这个网络资源就是通过网络编程来进行传输的.网络编程只是要求在不同的进程之间进行,所以即时是同一台主机的不同进程基于网络来传输数据也成为网络编程.下面将有一个网络编程的实例.Socket套接字,就是操作系统提供的用于网络编程的api,Scoket套接字主要针对传输层的协议划分为如下三类:1.流套接字:给TCP协议使用的. 2.数据报套接字 :使用传输层UDP协议.3.Unix域套接字,不能跨主机通信,现在使用较少.

下面介绍UDP数据报套接字编程.

1.DatagramSocket,DatagramSocket是对于系统中的Socket进行的封装.

2.DatagramPacket,是对于UDP数据报的一个抽象表示,一个DatagramPacket对象,就相当于是一个UDP数据报,一个发送或者接受,就是传输了一个DatagramPacket对象.

下面是代码实例:

服务器端的代码:

复制代码
package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;


//服务器
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){
//         读取请求并且解析,根据请求计算响应,把响应写到客户端.
//            需要使用datagrampacket来进行接收
            DatagramPacket requestPacket=new DatagramPacket(new byte[4000],4000);
            socket.receive(requestPacket);
//            把上述数据转换成String
            String request=new String(requestPacket.getData(),0,requestPacket.getLength());
//            回显,不做处理,直接返回
            String response=process(request);
//            把响应写会客户端
           DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),0,response.getBytes().length,
                   requestPacket.getSocketAddress());
            socket.send(responsePacket);
            System.out.printf("[%s,%d] rep=%s,resp=%s\n",requestPacket.getAddress(),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();
    }
//接下来还需要让服务器能够不停的处理请求.


}

客户端的代码:

复制代码
package  network;

import com.sun.nio.file.ExtendedWatchEventModifier;

import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

public class UdpEchoClient {
    private DatagramSocket socket=null;
    private String serverIp;
    private int serverPort;
//    构造方法,需要知道ip和端口号
    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);
//           读取响应数据
            DatagramPacket responsePacket=new DatagramPacket(new byte[4000],4000 );
            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", 59090);
        client.start();
    }
}

运行结果如下所示:

对请求进行简单的处理:

复制代码
package network;

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;

public class UdpDictServer extends UdpEchoServer {
//    构造一个hasp表
    private HashMap<String,String> dict=null;
    public UdpDictServer(int port) throws SocketException {
//        在构造方法里面进行初始化
        super(port);
        dict=new HashMap<>();
        dict.put("hello","你好");
        dict.put("cat","小猫");
        dict.put("dog","小狗");
        dict.put("pig","小猪");
        dict.put("麻球","麻花");
    }
//    可以来继前面写的服务器的方法
//     重写process方法
    @Override
     public String process(String request){
        return dict.getOrDefault(request,"没有查询到");
     }
     public static void main(String[] args) throws IOException {
        UdpDictServer server=new UdpDictServer(9090);
        server.start();
     }
}

接着写TCP流套接字编程.

API介绍: ServerSocket是创建TCP服务端的API.ServerSocket的构造方法:ServerSocket(int pot)

创建一个服务端流套接字Socket,并绑定到指定端口.

ServerSocket方法:Socket accept():开始监听指定端口,有客户端连接后.返回一个服务端的Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待. void close():关闭此套接字.

Socket: Socket是客户端的Socket,或服务端中接收到客户端建立连接(accept方法)的请求之后,返回的服务端Socket.不管是客户端还是服务端的Socket,都是双方建立连接之后,保存对方的信息,以及用来与对方收发数据的.Socket构造方法:Socket(String host,int port),创建一个客户端流套接字Socket,并于对应IP的主机的对应端口号的进程建立联系.Socket方法: InetAddress getInetAddress():返回套接字所连接的地址. InputStream getInputStream() 返回此套接字的输入流.OutputStream getOutputStream() 返回此套接字的输出流.

复制代码
package network;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TcpEchoServer {
    private ServerSocket serverSocker=null;
//   构造方法,用来绑定端口号
    public TcpEchoServer(int port) throws IOException {
        this.serverSocker=new ServerSocket(port);
    }
//    启动服务器
    public void start() throws IOException {
        System.out.println("服务器启动");
        while(true){
           Socket clientsocket=serverSocker.accept();
            processConnection(clientsocket);
        }
    }
//    建立连接逻辑
    private void processConnection(Socket clientSocket) throws IOException {
//        打印日志,当前有客户端连接上了
        System.out.printf("[%s,%d] 客户端上线\n",clientSocket.getInetAddress(),clientSocket.getPort());
//     从socket中获取到流对象,来进行进一步操作.
        try(InputStream inputStream=clientSocket.getInputStream();
            OutputStream outputStream=clientSocket.getOutputStream();
            ) {
//            每个连接服务器可能会发送来多个请求,服务器也就需要返回多个响应了

            while(true){
                Scanner scanner=new Scanner(inputStream);
                if(!scanner.hasNext()){
                    System.out.printf("[%s,%n] 客户端下线",clientSocket.getInetAddress(),clientSocket.getPort());
                    break;
                }
                String request=scanner.next();
//                根据请求计算响应
                String response=process(request);
//                把响应写会到客户端
                outputStream.write(response.getBytes());
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            clientSocket.close();
        }


    }
    public String process(String request){
        return request+"\n";
    }
   public static void main(String[] args) throws IOException {
        TcpEchoServer server=new TcpEchoServer(9090);
        server.start();
   }
}

package network;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class TcpEchoClient {
//    创建一个Socket
    private Socket socket=null;
    public TcpEchoClient(String serverIp,int serverPort) throws IOException {
//        Socket构造方法:Socket(String host,int port),创建一个客户端流套接字Socket,并于对应IP的主机的对应端口号的进程建立联系.
        socket=new Socket(serverIp,serverPort);
    }
    public void start(){

        System.out.println("客户端启动");
        Scanner scanner=new Scanner(System.in);
        try(InputStream inputStream=socket.getInputStream();
            OutputStream outputStream=socket.getOutputStream()) {
            Scanner scannerNetwork=new Scanner(inputStream);
            while(true){
//                从控制台读取数据,把请求发送给服务器,从服务器读取响应,把响应显示到控制台
                System.out.print("请输入要发送的数据: ");
                String request=scanner.next();
                request+="\n";
                outputStream.write(request.getBytes());
                if(!scannerNetwork.hasNext()){
                    break;
                }
                String response=scannerNetwork.next();
                System.out.println(response);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static void main(String[] args) throws IOException {
        TcpEchoClient client=new TcpEchoClient("127.0.0.1",9090);
        client.start();
    }
}

使用线程池来满足多个客户端:

复制代码
package network;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoServer {
    private ServerSocket serverSocker=null;
//   构造方法,用来绑定端口号
    public TcpEchoServer(int port) throws IOException {
        this.serverSocker=new ServerSocket(port);
    }
//    启动服务器
    public void start() throws IOException {
        System.out.println("服务器启动");
        ExecutorService service= Executors.newCachedThreadPool();
        while(true){
           Socket clientsocket=serverSocker.accept();
           service.submit(new Runnable() {
               @Override
               public void run() {
                   try {
                       processConnection(clientsocket);
                   } catch (IOException e) {
                       throw new RuntimeException(e);
                   }
               }
           });
        }
    }
//    建立连接逻辑
    private void processConnection(Socket clientSocket) throws IOException {
//        打印日志,当前有客户端连接上了
        System.out.printf("[%s,%d] 客户端上线\n",clientSocket.getInetAddress(),clientSocket.getPort());
//     从socket中获取到流对象,来进行进一步操作.
        try(InputStream inputStream=clientSocket.getInputStream();
            OutputStream outputStream=clientSocket.getOutputStream();
            ) {
//            每个连接服务器可能会发送来多个请求,服务器也就需要返回多个响应了

            while(true){
                Scanner scanner=new Scanner(inputStream);
                if(!scanner.hasNext()){
                    System.out.printf("[%s,%n] 客户端下线",clientSocket.getInetAddress(),clientSocket.getPort());
                    break;
                }
                String request=scanner.next();
//                根据请求计算响应
                String response=process(request);
//                把响应写会到客户端
                outputStream.write(response.getBytes());
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            clientSocket.close();
        }


    }
    public String process(String request){
        return request+"\n";
    }
   public static void main(String[] args) throws IOException {
        TcpEchoServer server=new TcpEchoServer(9090);
        server.start();
   }
}
相关推荐
hgdlip17 分钟前
soul能用ip精准定位吗?ip属地准确吗
网络·网络协议·tcp/ip·soul
贺椿椿31 分钟前
ensp服务器DNS/HTTP/DHCP配置
linux·服务器·网络·http·智能路由器
7yewh1 小时前
Linux系统移植篇(十一)Linux 内核启动流程
linux·运维·服务器·arm开发·驱动开发
PLUTO t1 小时前
深入理解TCP/IP网络模型及Linux网络管理
linux·网络
祢真伟大2 小时前
Linux IP 配置
linux·服务器·tcp/ip
vortex52 小时前
通过 TTL 识别操作系统的原理详解
网络·网络安全·智能路由器
无名之逆2 小时前
Hyperlane:Rust 语言打造的 Web 后端框架新标杆
开发语言·前端·网络·网络协议·rust·github·ssl
一个儒雅随和的男子2 小时前
tcp/ip三次握手和四次挥手原理详细解析
服务器·网络·tcp/ip
兴达易控2 小时前
Profinet转Modbus RTU/TCP以太网通讯处理器
网络