栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【网络编程--UDP、TCP】

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【网络编程--UDP、TCP】

目录
  • 1.UDP
    • 1.1 网络编程
    • 1.2 怎么查看进程的端口号
    • 1.2 InetAddress
    • 1.3 端口协议:
    • 1.4 UDP协议
    • 1.5 UDP 数据交互代码演示
  • 2.TCP
    • 2.1 TCP 三次握手
    • 2.2 TCP四次挥手
    • 2.2 基本代码演示
    • 2.3 TCP发送端 接收端 使用多种方式实现字符串的传输和就收
    • 2.4 TCP 实现图片的传输,优化后结合多线程
  • 3 注意点

1.UDP 1.1 网络编程

网络编程
意义:能在不同的电脑上,传输数据
网络三要素:ip 端口,协议
问题:访问百度首页,本质上是访问百度的服务器

ipv4是点分十进制,如192.168.23.44 ipv6 冒分十六进制。因为ipv4资源枯竭(255255255*255),使用ipv6进行补充( 很多app也支持ipv6 如支付宝app的首页,手机设置中也可以看到)

1.2 怎么查看进程的端口号

1.第一步:任务管理器 -->详细信息–>查看pid —>飞秋

第二步:查到飞秋的pid后,在cmd执行netstat -ano 通过pid 对应端口

1.2 InetAddress
// static InetAddress getByName(String host)确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址
 // String getHostName()                    获取此IP地址的主机名                     
  //String getHostAddress()                 返回文本显示中的IP地址字符串                 

public class InetAddressDemo {
    public static void main(String[] args) throws UnknownHostException {
//InetAddress address = InetAddress.getByName("itheima");
        InetAddress address = InetAddress.getByName("192.168.1.66");

        //public String getHostName():获取此IP地址的主机名
        String name = address.getHostName();
        //public String getHostAddress():返回文本显示中的IP地址字符串
        String ip = address.getHostAddress();

        System.out.println("主机名:" + name);
        System.out.println("IP地址:" + ip);
    }
}
1.3 端口协议:

端口:
设备上应用程序的唯一标识

端口号: 了解:
用两个字节表示的整数,它的取值范围是065535。其中,01023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败

如果平常使用udp 或者tcp 建议直接使用10000以上的端口---->避免端口冲突
如:443—https 80–>http 3306—>数据库的端口

1.4 UDP协议

无连接通信协议,在通信前不需要建立逻辑连接,所以发送的数据可能丢失,发送数据的时候不管你接不接受得到,发完就不管了,接收端也不会向发送端发送反馈信息。

由于使用UDP协议消耗系统资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输

1.5 UDP 数据交互代码演示

UDP三种通讯方式分别是什么?

单播(一对一) 单播用于两个主机之间的端对端通信 组播(多对一) 组播用于对一组特定的主机进行通信 广播(一对多)
广播用于一个主机对整个局域网上所有主机上的数据通信

发送数据

public class ClientDemo {
    public static void main(String[] args) throws IOException {
        //1.找码头--找一个快递员
        DatagramSocket ds = new DatagramSocket();
        //2.打包礼物
        //DatagramPacket​(byte[] buf, int length, InetAddress address, int port)
        String s = "送给村长老丈人的礼物";
        byte[] bytes = s.getBytes();
			//将ip换成了对象
			//指定唯一 一个发送的IP,这是单播,(一对一)
        InetAddress address = InetAddress.getByName("127.0.0.1");
        int port = 10000;
//        如 包裹,包裹的重量,包裹的地址,包裹的电话号码
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);

        //3.由码头发送包裹
        ds.send(dp);

        //4.付钱走羊
        ds.close();
    }
}

接收数据

public class ServerDemo {
    //注意点:
        //1.要先运行接收端,再运行发送端
        //2.如果接收端再启动之后,没有接收到数据,那么会死等(阻塞).
        //3.在接收数据的时候,需要调用一个getLength方法,表示接收到了多少字节
    public static void main(String[] args) throws IOException {
        //1.找码头     ---- 表示接收端从10000端口接收数据的.
        DatagramSocket ds = new DatagramSocket(10000);
        //2,创建一个新的箱子
        byte [] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
        //3.接收礼物,把礼物放到新的箱子中
        System.out.println("-----------接收前----------");
        ds.receive(dp);
        System.out.println("------------接收后---------");
        //4.从新的箱子里面获取礼物
        byte[] data = dp.getData();
        // 获得数据的长度
        int length = dp.getLength();
        System.out.println(new String(data,0,length));
        //5.拿完走羊
        ds.close();
    }
}

代码的练习

UDP使用组播发送消息:

package com.ww.udp2;

import java.io.IOException;
import java.net.*;


public class Client {
    public static void main(String[] args) throws IOException {
        DatagramSocket ds = new DatagramSocket();

        String s="大家好本组的成员们";
        byte[] bytes = s.getBytes();
        int port=10000;
        InetAddress address = InetAddress.getByName("224.0.1.2");//设置组播的地址,向在一个组的接收端发消息
        DatagramPacket dp = new DatagramPacket(bytes,0,bytes.length,address,port);

        ds.send(dp);

        ds.close();

    }
}

UDP使用组播接收消息:

package com.ww.udp2;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;


public class ServiceZu {
    public static void main(String[] args) throws IOException {
        MulticastSocket ms = new MulticastSocket(10000);

        byte [] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes, 0, bytes.length);

        //白名单,只能接收这些IP发来的消息
        InetAddress address = InetAddress.getByName("224.0.1.2");
        InetAddress address2= InetAddress.getByName("224.0.1.3");
        InetAddress address3= InetAddress.getByName("224.0.1.4");
        ms.joinGroup(address);//将ms加入本组的组播地址,这样只要是加入这个组的接收端都能接收到发送端发来的信息
        ms.joinGroup(address2);//将ms加入本组的组播地址,这样只要是加入这个组的接收端都能接收到发送端发来的信息
        ms.joinGroup(address3);//将ms加入本组的组播地址,这样只要是加入这个组的接收端都能接收到发送端发来的信息

        ms.receive(dp);
        byte[] data = dp.getData();
        int length = dp.getLength();
        System.out.println(new String(data,0,length));
        ms.close();

    }
}

UDP的广播案例代码实现

发送端

package com.ww.udp;

import java.io.IOException;
import java.net.*;
import java.util.Scanner;


public class Client {
    public static void main(String[] args) throws IOException {
        //创建码头
        DatagramSocket ds = new DatagramSocket();
        Scanner sc = new Scanner(System.in);

        while (true) {
            String s= sc.nextLine();
            if ("886".equals(s)){
                break;
            }
            byte[] bytes = s.getBytes();
            //"192.168.12.35"
            //这里设置的是广播地址,那么这个发送端发送的消息,所有的接收端都能接收,前提是在同一个端口 发送端和接收端要使用同一个端口
            //为什么要使用同一个端口,就相当于QQ只能发消息给QQ,不能发给微信,每个软件都有自己的端口号,
            // 并且每个人在电脑上的同一款软件,使用的都是默认的端口号,所以同一款软件,在不同的电脑中使用的端口号是相同的,
            // 这样就能解释为什么需要端口号相同才能接收到信息了
            InetAddress address = InetAddress.getByName("255.255.255.255");

            //创建数据
            DatagramPacket dp = new DatagramPacket(bytes,0,bytes.length,address,10000);

            ds.send(dp);
        }

        ds.close();
    }
}

接收端

package com.ww.udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.concurrent.*;

// Base64util.GenerateImage(new String(bytes,0,length),"mz.jpg")
public class Service {
    public static void main(String[] args) throws IOException {
        DatagramSocket ds = new DatagramSocket(10000);


        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
                3,
                10,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(5),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

//192.168.12.125
        for (int i=0;i<15;i++) {
            poolExecutor.submit(//这里使用了线程池实现多线程,多次提交任务
                    new Runnable() {
                                @Override
                                public void run() {
                                    Object o =1;
                                    synchronized (o){
                                        byte [] arr = new byte[1024];
                                        DatagramPacket dp = new DatagramPacket(arr,0,arr.length);
                                        try {
                                            System.out.println("准备接收信息...");
                                            ds.receive(dp);//receive()方法会阻塞线程除非接收到数据
                                            System.out.println("接收到信息了....");
                                        } catch (IOException e) {

                                            e.printStackTrace();
                                        }
                                        //从箱子中获取数据
//            byte[] data = dp.getData();
                                        int length = dp.getLength();
                                        String hostName = dp.getAddress().getHostName();

                                        System.out.println("来自"+hostName+"小朋友:"+new String(arr,0,length));
                                    }

                                }
                            });
        }


        // poolExecutor.shutdown();
       // ds.close();

    }
}
2.TCP 2.1 TCP 三次握手

TCP协议:面向连接、通信效率低、数据传输可靠无差错,大小没有限制

了解:
tcp协议建立连接时需要经过三次握手确保连接建立。为什么是三次握手呢?
发送端向接收端发送一个连接信息,然后接收端回馈一个消息给发送端。这里就经过了两次握手了,那么发送端可以确定自己发送的消息接收端可以接收到,但是接收端还不能确定自己反馈的消息有没有被发送端接收到,所以发送端接收到接收端的反馈信息后,还需要向接收端发一条消息告诉接收端它的反馈已经被接收。这样就能确定接收端和发送端互相发的消息都能接收到

2.2 TCP四次挥手

2.2 基本代码演示
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        //创建服务器端的Socket对象(ServerSocket)
        //ServerSocket(int port) 创建绑定到指定端口的服务器套接字
        ServerSocket ss = new ServerSocket(10000);

        //Socket accept() 侦听要连接到此套接字并接受它
        Socket s = ss.accept();

        //获取输入流,读数据,并把数据显示在控制台
        InputStream is = s.getInputStream();
        byte[] bys = new byte[1024];
        int len = is.read(bys);
        String data = new String(bys,0,len);
        System.out.println("数据是:" + data);

        //释放资源
        s.close();
        ss.close();
    }
}
2.3 TCP发送端 接收端 使用多种方式实现字符串的传输和就收

Client 发送端 发送字符串 读写包含多种方式

package com.ww.tcp;

import java.io.*;
import java.net.Socket;
//使用tcp来发送信息,需要经过三次握手
public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1",10000);

        OutputStream os = socket.getOutputStream();
        String s= "嗨喽";
        //第一种方式:-------------------------------------
        // 使用字节输出流一次写一个字节数组
       // os.write(s.getBytes());
        //----------------------------------------------

       //第二种方式:--------------------------------------------
       //使用转换流,将字节输出流,转换成字符输出流,一次写一个字符串
        OutputStreamWriter osw = new OutputStreamWriter(os, "utf-8");
        osw.write(s);
        osw.flush();//这里需要使用flush()将数据从流冲刷出去,这样才能被服务端接收到数据
        //因为后面还需要使用到socket所以不能直接使用socket.close,只能使用shutdownOutput(),
        // 告诉服务端一个介素标记我已经写完,服务端才能展示读到的数据
        socket.shutdownOutput();

        //--------------------------------------------------------


       //使用缓冲流接收服务端回复的消息
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            System.out.println("服务器的回复信息:"+br.readLine());

        //释放资源

        socket.close();


    }
}

Service 接收端 接收字符串 读写包含多种方式

package com.ww.tcp;

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

public class Service {
    public static void main(String[] args) throws IOException, InterruptedException {
        ServerSocket ss = new ServerSocket(10000);

        Socket accept = ss.accept();

        InputStream is = accept.getInputStream();



       

        //第三种方式:使用转化流将字节输入流转换成字符输入流,读取字符
        InputStreamReader isr = new InputStreamReader(is, "utf-8");
        int a;
        while ((a= isr.read())!=-1){//一次读取一个字符
            System.out.print((char)a);//输出字符串
        }


        Thread.sleep(2000);//回馈的时候用休息一下
        //使用缓冲流回复客户端,告诉客户端我已经接收到消息
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
        bw.write("我接收到你的嗨喽了,很高兴认识你!");
        bw.flush();//这里写完之后为什么没有不用accept调用shutdownOutput(),给客户端传递结束标记
        //因为下面的accept.close()的close()方法就包含了shutdownOutput(),所以不用再调用shutdownOutput()方法了

        //释放资源
        accept.close();
        ss.close();



    }
}
2.4 TCP 实现图片的传输,优化后结合多线程

Client 发送端:

package com.ww.tcp;

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

public class Service {
    public static void main(String[] args) throws IOException, InterruptedException {
        ServerSocket ss = new ServerSocket(10000);

        Socket accept = ss.accept();

        InputStream is = accept.getInputStream();



       

        //第三种方式:使用转化流将字节输入流转换成字符输入流,读取字符
        InputStreamReader isr = new InputStreamReader(is, "utf-8");
        int a;
        while ((a= isr.read())!=-1){//一次读取一个字符
            System.out.print((char)a);//输出字符串
        }


        Thread.sleep(2000);//回馈的时候用休息一下
        //使用缓冲流回复客户端,告诉客户端我已经接收到消息
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
        bw.write("我接收到你的嗨喽了,很高兴认识你!");
        bw.flush();//这里写完之后为什么没有不用accept调用shutdownOutput(),给客户端传递结束标记
        //因为下面的accept.close()的close()方法就包含了shutdownOutput(),所以不用再调用shutdownOutput()方法了

        //释放资源
        accept.close();
        ss.close();



    }
}

自定义的Runnable的 实现类 ThreadSocket把需要完成的任务放进run()方法中执行,从而在Server中使用线程池来实现多线程

Server 接收端

package com.ww.tcp2;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10000);
//192.168.232.1
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
                3,   //核心线程数量
                8,   //线程池的总数量
                60,   //临时线程空闲时间
                TimeUnit.SECONDS,   //临时线程空闲时间的单位
                new LinkedBlockingQueue<>(10),  //阻塞队列 自定义阻塞队列的长度为10
                Executors.defaultThreadFactory(),  //创建线程的方式  使用工厂创建线程
                new ThreadPoolExecutor.AbortPolicy()  //任务拒绝策略
        );
        while (true) {
            Socket accept = ss.accept();
            ThreadSocket ts = new ThreadSocket(accept);//ts是Runnable的实现类
            //new Thread(ts).start();//实现多线程
            poolExecutor.submit(ts);//使用自定义的线程池来实现多线程

        }
        //ss.close(); 死循环服务器端就不关闭了

    }
}
3 注意点

注意事项:

  1. accept方法是阻塞的,作用就是等待客户端连接

  2. 客户端创建对象并连接服务器,此时是通过三次握手协议,保证跟服务器之间的连接

  3. 针对客户端来讲,是往外写的,所以是输出流
    针对服务器来讲,是往里读的,所以是输入流
    这里的客户端和服务器端都是在内存中的,输入输出都是相对于内存来说的

  4. read方法也是阻塞的
    创建客户端对象,创建输入流对象指向文件,每读一次数据就给服务器输出一次数据,输出结束后使用shutdownOutput()方法告知服务端传输结束

  5. 客户端在关流的时候,还多了一个往服务器写结束标记的动作

  6. 最后一步断开连接,通过四次挥手协议保证连接终止

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1035779.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号