网络编程基础---UDP-阿里云开发者社区

开发者社区> 开发与运维> 正文
登录阅读全文

网络编程基础---UDP

简介:        上一篇博客主要是为了这一部分服务的,最近学到网络编程部分 刚好计算机网络也讲到ip地址 也就恶补了一些IP传输的知识。

       上一篇博客主要是为了这一部分服务的,最近学到网络编程部分 刚好计算机网络也讲到ip地址 也就恶补了一些IP传输的知识。现在正式来总结一下UDP和TCP两大传输协议吧

网络通讯的协议:

udp通讯协议

tcp通讯协议。

网络通讯的三要素:
1. IP 传输的源地址和目标地址
2. 端口号。
3. 协议.
这里写图片描述

    端口号是没有类描述的。
    端口号的范围: 0~6553501023,系统紧密绑定于一些服务。 
    1024~65535  我们可以使用....
    常用端口       
    HTTPS端口 :443  
  Telnet端口:23 (远程登录)
  HTTP端口:80 (超文本传输协议) 
  FTP 端口:21 (文件传输协议)
  DNS 端口:53 (域名服务系统)
  POP3端口:110(邮筒协议)
  SMTP端口:25 (简单邮件传输协议) 

Socket
socket的英文原义是“孔”或“插座”。作为4BDS UNIX的进程通信机制,取后一种意思。通常也称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄(引用)。
每个插座就是一个应用程序。
注意:
不同的通信规则需要定义不同的插座。
UDP:DatagramSocket 、 DatagramPacket
TCP:ServerSocket 、Socket

UDP:
在java中网络通讯业称作为Socket(插座)通讯,要求通讯 的两台器都必须要安装Socket。

不同的协议就有不同 的插座(Socket)
相当于码头 没有寻址作用 只是把数据包传输出去

UDP通讯协议的特点:
1. 将数据极封装为数据包,面向无连接。
2. 每个数据包大小限制在64K中
3.因为无连接,所以不可靠
4. 因为不需要建立连接,所以速度快
5.udp 通讯是不分服务端与客户端的,只分发送端与接收端。

比如: 物管的对讲机, 飞Q聊天、 游戏... 
你知道为什么游戏的时候会莫名其妙卡死吗?
就是因为是用UDP来传输 导致数据包丢失 为什么用UDP 那是因为速度快响应快

先来讲一下:
InetAddress(IP类)

常用的方法:
getLocalHost(); 获取本机的IP地址
getByName(“IP或者主机名”) 根据一个IP地址的字符串形式或者是一个主机名生成一个IP地址对象。 (用于获取别人的IP地址对象) 一般用IP地址

getHostAddress() 返回一个IP地址的字符串表示形式。
getHostName() 返回计算机的主机名。(主机名可以修改)
getAllByName() 返回主机的全部ip地址 有些域名对应着多个ip地址
就比如百度 百度是一个域名 但是背后是多个服务器 是集群模式

InetAddress[] inetAddress=
InetAddress.getAllByName("www.baidu.com");
for(int i=0;i<inetAddress.length;i++)
System.out.println(inetAddress[i].getHostAddress());

输出:
119.75.213.61
119.75.216.20
    //getLocalHost 获取本机的IP地址对象
        /*InetAddress address = InetAddress.getLocalHost();
        System.out.println("IP地址:"+address.getHostAddress());
        System.out.println("主机名:"+address.getHostName());*/

        //获取别人机器的IP地址对象。


        //可以根据一个IP地址的字符串形式或者是一个主机名生成一个IP地址对象。
        InetAddress address = InetAddress.getByName("Jolly-pc140116");
        System.out.println("IP地址:"+address.getHostAddress());
        System.out.println("主机名:"+address.getHostName());



        InetAddress[]  arr = InetAddress.getAllByName("www.baidu.com");//域名

udp协议下的Socket(也可以叫做套接字):

DatagramSocket(udp插座服务)
DatagramPacket(数据包类)
    DatagramPacket(buf, length, address, port)
    buf: 发送的数据内容
    length : 发送数据内容的大小。
    address : 发送的目的IP地址对象
    port : 端口号。

发送端的使用步骤:
1. 建立udp的服务。
2. 准备数据,把数据封装到数据包中发送。 发送端的数据包要带上ip地址与端口号。
3. 调用udp的服务,发送数据。
4. 关闭资源。

来看一下UDP两台电脑间是如何通信吧
//发送端
public class Demo1Sender {

    public static void main(String[] args) throws IOException {
        //建立udp的服务
        DatagramSocket datagramSocket = new DatagramSocket();
        //准备数据,把数据封装到数据包中。
        String data = "这个是我第一个udp的例子..";
        //创建了一个数据包
        DatagramPacket packet = new DatagramPacket(data.getBytes(), data.getBytes().length,InetAddress.getLocalHost() , 9090);
        //调用udp的服务发送数据包
        datagramSocket.send(packet);
        //关闭资源 ---实际上就是释放占用的端口号
        datagramSocket.close();

    }

}
//接收端
    //建立udp的服务 ,并且要监听一个端口。
        DatagramSocket  socket = new DatagramSocket(9090);

        //准备空的数据包用于存放数据。
        byte[] buf = new byte[1024];
        DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length); // 1024
        //调用udp的服务接收数据
        socket.receive(datagramPacket); //receive是一个阻塞型的方法,没有接收到数据包之前会一直等待。 数据实际上就是存储到了byte的自己数组中了。
        System.out.println("接收端接收到的数据:"+ new String(buf,0,datagramPacket.getLength())); // getLength() 获取数据包存储了几个字节。
        //关闭资源
        socket.close();

总结:
1. 要先打开接收端 再打开发送端 不然就会造成数据丢失
2. receive是一个阻塞型的方法,没有接收到数据包之前会一直等待。相当于sacnner 就是等待输入 意思就是说 接收方只有接收到数据才会往下走
3. 注意:数据包发送字符串传输的时候看仔细是发送字节的长度而不是字符串的长度

//对的
 String data = "这个是我第一个udp的例子..";
DatagramPacket packet = new DatagramPacket(data.getBytes(),   
data.getBytes().length,InetAddress.getLocalHost() , 9090);
//错的 长度取字符串的长度了  显然长度要小于实际长度 就会数据丢失
 String data = "这个是我第一个udp的例子..";
DatagramPacket packet = new DatagramPacket(data.getBytes(),   
data.length,InetAddress.getLocalHost() , 9090);

4.端口号可以随意 只是标识符 但是要在范围内 如1024~65535 我们可以使用…. 本地服务器的端口一般设为80 这是题外话
5. 在udp协议中,有一个IP地址称作为广播地址,广播地址就是主机号为255地址。 只有udp有广播地址 TCP没有 给广播IP地址发送消息的时候,在同一个网络段的机器都可以接收 到信息。

接下来看一个群聊的UDP实现方法:

//群聊发送端
public class ChatSender extends Thread {

    @Override
    public void run() {
        try {
            //建立udp的服务
            DatagramSocket socket = new DatagramSocket();
            //准备数据,把数据封装到数据包中发送
            BufferedReader keyReader = new BufferedReader(new InputStreamReader(System.in));
            String line = null;
            DatagramPacket packet  = null;
            while((line = keyReader.readLine())!=null){
                //把数据封装 到数据数据包中,然后发送数据。
                packet = new DatagramPacket(line.getBytes(), line.getBytes().length, InetAddress.getByName("192.168.15.255"), 9090);
                //把数据发送出去
                socket.send(packet);
            }
            //关闭 资源
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}
//群聊接收端
public class ChatReceive extends Thread {

    @Override
    public void run() {
        try {
            //建立udp的服务,要监听一个端口
            DatagramSocket socket = new DatagramSocket(9090);
            //准备空的数据包存储数据
            byte[] buf = new byte[1024];
            DatagramPacket packet = new DatagramPacket(buf, buf.length);
            boolean flag = true;
            while(flag){
                socket.receive(packet);
                // packet.getAddress() 获取对方数据 包的IP地址对象。
                System.out.println(packet.getAddress().getHostAddress()+"说:"+new String(buf,0,packet.getLength()));
            }
            //关闭资源
            socket.close();

        }catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


    }

}
//主函数
public class ChatMain {

    public static void main(String[] args) {
        ChatReceive chatReceive = new ChatReceive();
        chatReceive.start();

        ChatSender chatSender = new ChatSender();
        chatSender.start();


    }

}

输出:
这里写图片描述

总结:1. 不能重复开启接收端 因为端口被占用了 如果重复启动main函数 相当于监听了两次该端口 就会报这样的错误
这里写图片描述
2. 用多线程操作群聊的时候 while循环要有个退出条件 不然就无限循环 Close的时候会报错 所以要用While+flag变量来控制退出 (具体的可以看看我之前写的多线程的中断)
3. 多线程操作群聊的时候 不能抛出异常 只能用Try catch异常 因为Thread是线程作为父类没有抛出异常 而作为子类是不可以抛出异常的 (具体的可以异常最后的总结部分)

还有个问题得注意下:这个东西把我搞半天 后来发现问题的所在……
如果你是用UTF-8编程的发送方代码 而对方是GBK默认的接受方代码 这个时候传输汉字的时候就会乱码 字符不影响
那怎么解决呢
这个可以用来判断你的文件编码格式 getBytes()默认编码成Byte格式 默认是GBK 如果eclipse设置了 UTF-8格式的话 那它默认就是UTF-8格式
也可以指定编码格式name.getBytes(“utf-8”) 就把name字符串按照utf-8的格式转换成byte数组
new String(name.getBytes(“GBK”),”utf-8”)) 以 GBK格式编码byte又以UTF-8格式转换成字符串

String name="我爱你";
        if(name.equals(new String(name.getBytes(),"utf-8"))) {
            System.out.println(name);
        }
}

回到群聊中:

packet = new DatagramPacket(line.getBytes("GBK"), line.getBytes().length, InetAddress.getByName("192.168.15.255"), 9090);

这样子会报错的 要这样子才可以 得统一

packet = new DatagramPacket(line.getBytes("GBK"), line.getBytes("GBK").length, InetAddress.getByName("192.168.15.255"), 9090);

群聊接收方: 两种方法都可以


String name=new String(datagramPacket.getData(),datagramPacket.getLength());
System.out.println(new String(buffer,0,datagramPacket.getLength()));都可以

接受对方还可以得到发送方的信息
packet.getAddress().getHostAddress() 得到发送方的ip地址
packet.getAddress().getHostName() 得到发送方的电脑名(可修改)

题外话 :
命令行 来运行class文件的时候
java 要加包名 而且是 bin目录下 java myjar.ww myjar是包名

UDP是无连接协议,发送方的端口是由发送方随机生成的(根据不同的网络环境,发送方的IP也有可能是在地址池中随机选取的),发送完以后,该IP+端口就已经无效了。

注意:发送端发送数据的端口是随机的

参考:
https://blog.csdn.net/napolun007/article/details/6050241
各种协议

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享: