自学内容网 自学内容网

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

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流来进行网络通信。

客户端:

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();会一直等待发送端的连接,死等

第一种:循环的接收

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

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数组接收再转成字符串输出

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传输序列化对象:

客户端:
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类型,通常需要进行类型转换

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)使服务端一直等一直监听

服务器端:

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);
        }

    }
}

客户端:


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对象,然后进入子线程

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);
        }

    }
}

服务端子线程:

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")是一个地址对象,反正这里不能直接填地址

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
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();
        }

    }
}

原文地址:https://blog.csdn.net/qq_62859013/article/details/142361097

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!