网络编程:掌握TCP Socket和UDP Socket

news/2024/9/20 5:58:05 标签: 网络, java, intellij-idea

IP地址:

两台计算机通信,双方都必须有IP地址。

IPV4地址有32位,由四个8位二进制组成,因为不好记所以我们把二进制转化为十进制,比如192.168.0.20,这称为点分十进制。

IPV6有128位,由8个16位的无符号整数组成,每个整数用4个十六进制数表示,这些数之间用冒号(:)分开。

IP地址的分类:

IP地址 = 网络地址 +主机地址

网络地址决定网络

主机地址决定了一个网络中可以存在的计算机最大数量

A类IP地址:前八位表示网络地址,取值范围1-126

B类IP地址:前十六位表示网络地址,取值范围128-191

C类IP地址:前三组表示网络地址,取值范围192-223

D类IP地址:不分网络地址和主机地址, 用于多播(Multicast)。多播通信是一种将数据包发送到多个特定接收者的方式,而不是广播给网络上的所有设备。常用于视频会议、流媒体传输等应用。

特殊的IP地址:

0.0.0.0表示主机

127.0.0.1表示本机回环地址,通常用于本机ping此地址来检查TCP/IP协议安装是否正确

255.255.255.255表示当前子网

IP地址的配置和检测:

查看本机的IP地址:
ipconfig

测试网络是否通畅:

ping  目标IP地址

DNS域名解析(域名系统):

访问网站时,为什么输入网址而不是IP地址?

因为ip地址都是数字,人们输入的时候太枯燥,所以需要一个系统将一个名称映射为它的IP地址,DNS域名解析系统。

网络通信协议:

TCP/IP:五层

首先应用层准备好要发送的数据,然后给了传输层。

传输层的主要作用就是为发送端和接收端提供可靠的连接服务,传输层将数据处理完后就给了网络层。

网络的功能就是管理网络,其中一个核心的功能就是路径的选择(路由选择),从发送端到接收端有很多条路,网络层就负责管理下一步数据应该到哪个路由器。选择好了路径之后,数据就来到了数据链路层,

数据链路层就是负责将数据从一个路由器送到另一个路由器。然后就是物理层了,可以简单的理解,

物理层就是网线一类的最基础的设备。

TCP协议:

传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议

例子:

发货之前工作人员首先得要确认一下路是不是通吧,比如现在是过年,物流全部停运不就是路不通吗,那还发什么货呀。要是没什么问题,物流全部正常运作就发货呗。手机到达小明家后,小明先拆开看看手机在运输途中有没有损坏,有损坏就联系客服处理,没问题就确认收货。再回到上面的定义中,面向连接指的是先建立连接再发送数据,也就是先确认路可以走得通再发货。可靠就是如果货物在运输过程中有损坏或者丢失就让京东重新发货,确保小明收到的手机是没有任何问题的。基于字节流的意思就是比如小明买了手机又买了手机配件,分开发货,几件物品不是在一个包裹里,一个一个发。在这个例子中,京东的工作人员和小明充当了TCP协议的角色,他们两个共同确保了货物的完整性。

总结:确定是通的,如果丢失就重新再发,数据是以字节流的方式传送的。

建立连接 接收数据 发送数据

三次握手四次挥手:

三次握手:

A:是B吗?我要跟你通信,听得到我说话吗?

B:可以通信,你听得到我说话吗?

A:我也听得到。

之所以挥手三次是为了防止丢包的链接被服务端等待

四次挥手:

A:我困了,先不聊了吧

B:还有几件事,说完我们就睡觉…… ……(说完之后)

B:好了,说完了,我挂线了

A:好,你挂吧

B挂断电话

A说完之后就直接睡觉了,不知道电话挂没挂断,在几分钟后听到手机没有声音传来了,即使不用睁眼也知道B挂断了

四次挥手如图:

之所以还需要最后的ack发送后还需要等待,是因为服务端需要收到后才可以断开连接,所有如果ack丢失的话服务端可以再给客户端发一次。

UDP协议:

面向无连接的

TCP和Socket:

TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象,通信之前要保证连接已建立。

通过Socket产生I/O流来进行网络通信。

客户端:

java">public class Socket01 {
    public static void main(String[] args) {
        Socket socket =null;
        OutputStream os =null;
        //客户端
        try {
            socket = new Socket("127.0.0.1",8888);
            //创建输出流
             os = socket.getOutputStream();
            os.write("chenmengyu Hello".getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                os.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

服务器端:

首先要运行接收端,不然发送端连接不上就不会发送数据

Socket s = ss.accept();会一直等待发送端的连接,死等

第一种:循环的接收

不需要字符串了,但是缺点是只能够传输字母,不能传中文

java">public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream inputStream =null;
        try {
            //创建serverSocket
             serverSocket = new ServerSocket(8888);
            //监听客户端获取socket对象
             socket = serverSocket.accept();
            //获取输出流
             inputStream = socket.getInputStream();
            int read;
            while ((read=inputStream.read())!=-1){
                System.out.print((char)read);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                serverSocket.close();
                socket.close();
                inputStream.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

第二种数组接收:

byte b[] = new byte[100];

String str = new String(b);

用byte数组接收再转成字符串输出

java">public class Socket02 {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream inputStream =null;
        try {
            //创建serverSocket
             serverSocket = new ServerSocket(8888);
            //监听客户端获取socket对象
             socket = serverSocket.accept();
            //获取输出流
             inputStream = socket.getInputStream();
           byte b[] = new byte[100];
           inputStream.read(b);
           String str = new String(b);
           System.out.println(str);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                serverSocket.close();
                socket.close();
                inputStream.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

TCP传输序列化对象:

客户端:
java">public class ObjSocket01 {
    public static void main(String[] args) {
        //客户端

        Socket socket = null;
        OutputStream os = null;
        ObjectOutputStream objectOutputStream = null;
        try {
             socket = new Socket("127.0.0.1",8888);
             os = socket.getOutputStream();
             objectOutputStream = new ObjectOutputStream(os);
             objectOutputStream.writeObject(new Student("陈梦雨",21));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                socket.close();
                os.close();
                objectOutputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

服务器端:

readObject()方法的作用是从输入流中读取一个序列化的对象,并将其转换为Java对象。它会返回一个Object类型,通常需要进行类型转换

java">public class ObjSocket02 {
    public static void main(String[] args) {
        ServerSocket socket = null;
        InputStream inputStream = null;
        ObjectInputStream ois = null;
        try {
             socket = new ServerSocket(8888);
             //监听客户端
            Socket socket1 = socket.accept();
            //获取输入流
             inputStream= socket1.getInputStream();
             //创建一个反序列化对象,并且把输入流当参数放进去
             ois = new ObjectInputStream(inputStream);
             //readObject()方法则从流中读取对象并将其转换为Student类型的实例
             Student stu= (Student)ois.readObject();
            System.out.println(stu.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

例题:多客户端用户登录:

TCP的Socket和多线程联动

需要创建子线程的是服务端,它创建多个子线程去处理客户端发送的东西

不使用多线程:

就是用while(true)使服务端一直等一直监听

服务器端:

java">package QuestionPpt02;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class demo01 {
    public static void main(String[] args) {

        ServerSocket serverSocket = null;
        Socket socket = null;
        //服务器端
        try {
            serverSocket = new ServerSocket(8888);
             socket = new Socket();
             while(true){
                 //监听
                 socket = serverSocket.accept();
                 InputStream is = socket.getInputStream();
                 byte b[] = new byte[50];
                 is.read(b);
                 String msg = new String(b);
                 System.out.println(msg);

             }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}

客户端:

java">
public class demoCelint01 {
    public static void main(String[] args){
      //客户端1
        System.out.println("客户端01————————————");

        try {
            Socket socket = new Socket("127.0.0.1",8888);
            OutputStream os = socket.getOutputStream();
            os.write("用户名:张三".getBytes());
            os.close();
            socket.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


    }
}
使用多线程:

它的客户端和上面一样

服务端主线程:

通过构造方法传入监听过来的socket对象,然后进入子线程

java">package QuestionPpt02;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class demo02 {
    public static void main(String[] args) {

        ServerSocket serverSocket = null;
        Socket socket = null;
        //服务器端
        try {
            serverSocket = new ServerSocket(8888);
            socket = new Socket();
            while(true){
                //监听
                socket = serverSocket.accept();
               //一旦有用户来就创建一个子线程
                ThreadTest threadTest = new ThreadTest(socket);
                threadTest.start();
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}

服务端子线程:

java">package QuestionPpt02;

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

public class ThreadTest extends Thread{

    private Socket socket;

    public  ThreadTest(Socket socket){
        this.socket = socket;

    }

    //服务端创建子线程
    @Override
    public  void run(){

        try {
            InputStream is = null;
            is = socket.getInputStream();
            byte b[] = new byte[50];
            is.read(b);
            String msg = new String(b);
            System.out.println(msg);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

UDP和socket:

DatagramSocket:用来发送和接收数据包

DatagramPacket:用来装包和拆包

dp.getData()拆包

ds.send(dp);发送包

基于UDP协议的sokect实现

发送端(客户端):

DatagramPacket的构造方法最常用的,接收(发送内容的比特数组,数组长度,地址对象,端口号)

InetAddress.getByName("127.0.0.1")是一个地址对象,反正这里不能直接填地址

java">public class LoginCelint {
    //客户端
    public static void main(String[] args) {
        System.out.println("我是客户端---------------");
        //发送信息
        Scanner scanner = new Scanner(System.in);
        DatagramPacket dp = null;
        DatagramSocket ds = null;

        try {
            
                System.out.print("客户端请输入:");
                String msg = scanner.nextLine();
                dp = new DatagramPacket(msg.getBytes(),
                        msg.getBytes().length,
                        InetAddress.getByName("127.0.0.1"),
                        8888);

                ds = new DatagramSocket();
                ds.send(dp);
            
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            ds.close();

        }
    }
}

接收端(服务器):
  1. 一定要先运行接收方, 在 UDP 通信中,接收方需要在指定的端口上监听和等待数据包

DatagramPacket的构造方法(byte数组,数组长度)

  1. String msg = new String(dp.getData());先 获取DatagramPacket中存储的数据的字节数组。再转成String
java">public class LoginServer {
    //服务器
    public static void main(String[] args) {
        System.out.println("我是服务端——————————");
        Scanner scanner = new Scanner(System.in);
        DatagramPacket dp = null;
        DatagramSocket ds = null;
        try {
            ds = new DatagramSocket(8888);
                byte b[] = new byte[100];
                dp = new DatagramPacket(b, b.length);

                //等待接收
                ds.receive(dp);
                //拆包
                String msg = new String(dp.getData());
                System.out.println("客户端对我说:" + msg);
          

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            ds.close();
        }

    }
}

http://www.niftyadmin.cn/n/5666672.html

相关文章

Java项目实战II基于Java+Spring Boot+MySQL的房屋租赁管理系统的设计与实现

目录 一、前言 二、技术介绍 三、系统实现 四、论文参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 随着城市租…

1网络安全的基本概念

文章目录 网络安全的基本概念可以总结为以下几个方面: 网络安全的需求: 信息安全的重要性:信息安全是计算机、通信、物理、数学等领域的交叉学科,对于社会的发展至关重要。信息安全的目标:主要包括保密性、完整性、可用…

数据库系统原理(第一章 数据库概述)

文章目录 1.数据1.1数据的概念1.2数据与信息的关系1.3数据使用 2.数据管理3.数据库与数据库管理系统3.1数据库3.2数据库管理系统 4.数据库系统4.1数据库系统组成4.2 数据库系统的特点4.3数据库系统体系结构4.3.1内部体系结构4.3.2外部体系结构 本文首先从数据讲起,然…

深度解析ElasticSearch:构建高效搜索与分析的基石原创

引言 在数据爆炸的时代,如何快速、准确地从海量数据中检索出有价值的信息成为了企业面临的重要挑战。ElasticSearch,作为一款基于Lucene的开源分布式搜索和分析引擎,凭借其强大的实时搜索、分析和扩展能力,成为了众多企业的首选。…

tomcat,el表达式执行带参数命令,字符串数组,String[],el表达式注入

准备环境: docker pull tomcat:8;docker run --name tomcat8 -p 808:8080 -v /tmp/CC:/usr/local/tomcat/webapps/ -d tomcat:8;如下为 /tmp/CC/app/index.jsp <% page language"java" contentType"text/html; charsetUTF-8" pageEncoding"UTF-8…

Java 数据类型转换详解:隐式转换(自动转换)与强制转换(手动转换)

目录 前言 取值范围从小到大的关系&#xff1a; 隐式转换&#xff08;自动转换&#xff09; &#x1f4dc;示例 1&#xff1a;基本类型隐式转换 &#x1f4dc;示例 2&#xff1a;算术运算中的类型提升 &#x1f4dc;示例 3&#xff1a;byte、short 和 char 的自动转换 隐…

Greiner 经典力学(多体系统和哈密顿力学)第二章 学习笔记

第二章 学习笔记 第二章的题目是 Free Fall on the Rotating Earth。这章的内容就是第一章结论的一个直接应用。这一章假设地心是做匀速直线运动的&#xff0c;也就是地心坐标系是惯性系 L。在地表处建立一个 M 坐标系。 首先先指出书上一个错误&#xff0c;书上公式 2.1 写的…

在windows上使用vs code调试Rust程序

视频参考&#xff1a;https://www.youtube.com/watch?vTlfGs7ExC0A 前置条件 需要安装的软件&#xff1a; rustvs codeMinGW 或者其它能在 Windows 平台上运行 gdb、gcc 和 g 的软件。 需要安装的插件&#xff1a; rust-analyzer CodeLLDB 然后&#xff0c;在 vs code 中…