UDP

DatagramPacket

UDP在Socket网络中都是通过DatagramPacket的数据格式进行传输的,也就是数据报的形式。UDP数据报是基于IP数据报建立的,UDP数据报同样分为首部和主体,UDP数据报的首部比IP数据报的首部多了8个字节,其中UDP的首部包括:源端口号和目标端口号、 IP首部之后所有内容的长度、 可选的检验和。 理论上UDP包中的数据长度最大是65507字节,但实际上总是比这少得多。

DatagramPacket相关API

  • 接收数据报的构造函数
    public DatagramPacket(byte buf[], int length)
    public DatagramPacket(byte buf[], int offset, int length)

这两个构造函数可以创建新的DatagramPacket对象并从网络接收数据。

  • 第一个构造函数:当Socket接收一个数据报时,它将数据报的数据部分存储在buf字节数组中,从buf[0]开始一直到包完全存储,或者直到向字节数组中写入了length个字节为止。
  • 第二个构造函数:将从buf[offset]开始存储,直到包完全存储,或者直到向字节数组中写入了length个字节为止。
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);

byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, 0, buf.length);
  • 发送数据报的构造函数
    public DatagramPacket(byte buf[], int length, SocketAddress address)
    public DatagramPacket(byte buf[], int offset, int length, SocketAddress address)
    public DatagramPacket(byte buf[], int length,InetAddress address, int port)
    public DatagramPacket(byte buf[], int offset, int length,InetAddress address, int port)

这四个构造函数可以创建新的DatagramPacket对象并从网络发送数据。

  • 这个包用buf字节数据中从offset(如果没有指定该参数则默认为0)开始的length个字节填充,指定了要发送的数据。
  • InetAddress或SocketAddress对象指定了包要发送的目标主机地址。
  • port参数表示该主机上的端口。
// 准备要发送的数据
String str = "This is a test.";
// 转化为byte字节数组
byte[] buf = str.getBytes("UTF-8");
// 构造发送的目标地址和端口
InetAddress address = InetAddress.getByName("www.ibiblio.org");
int port = 7;
// 构造DatagramPacket
DatagramPacket packet = new DatagramPacket(buf, buf.length, address, port);
// 发送数据
DatagramSocket socket = new DatagramSocket();
socket.send(packet);
  • 获取DatagramPacket信息的方法
    DatagramPacket有6个获取数据报不同部分的方法,这些部分包括具体的数据以及首部的字段,这些方法主要用于从网络接收的数据报。
    public synchronized InetAddress getAddress()
    public synchronized int getPort()
    public synchronized SocketAddress getSocketAddress()
    public synchronized byte[] getData()
    public synchronized int getLength()
    public synchronized int getOffset()
  • getAddress():返回一个InetAddress对象,指示远程主机的地址。这里有两种情况:一是如果数据报是从Internet接收的,返回的地址则是发送该数据报的机器的地址(源地址);二是如果数据报是本地创建准备发送到远程主机的,返回的地址则是要发往的远程主机的地址(目标地址)。这个方法常用于确定发送UDP数据报的主机地址,使接收方可以回复。www.ibiblio.org/152.19.134.40
  • getPort():返回一个int整数,指示远程端口号。同样的,如果数据报是从Internet接收的,返回的端口号则是发送该数据报的机器的端口号(源端口号);如果数据报是本地创建准备发送到远程主机的,返回的端口号则是要发往的远程主机的端口号(目标端口号)。
  • getSocketAddress():返回一个SocketAddress对象,指示远程主机的IP地址和端口号。如果数据报是从Internet接收的,返回的地址则是发送该数据报的机器的地址(源地址);如果数据报是本地创建准备发送到远程主机的,返回的地址则是要发往的远程主机的地址(目标地址)。一般要在回复前调用这个方法,获取发送这个DatagramPacket数据报的主机地址和端口号,以便进行回复。与同时调用getAddress()和getPort()相比,实际结果没有显著区别。www.ibiblio.org/152.19.134.40:7
  • getData():返回一个byte数组,指示数据报中的数据部分。通常必须将接收到的字节数组转化为其他的某种数据形式以便使用。
  • getLength():返回一个int整数,指示数据报中数据部分的字节数。它不一定等于getData()返回的数组长度(即getData().length),甚至可能小于。
  • getOffset():返回一个int整数,指示该字节数组中的一个位置,即开始填充数据报的位置
// 转化接收到的字节数组的技巧
// 1. 直接调用String的构造函数进行转化
String data = new String(packet.getData(), "UTF-8");
// 2. 如果数据报不包含文本,可以转化为ByteArrayInputStream
InputStream in = new ByteArrayInputStream(packet.getData(), packet.getOffset(), packet.getLength());
DataInputStream din = new DataInputStream(in);
// 这里可以使用DataInputStream的readInt()、readChar()等方法直接读取...
  • 设置DatagramPacket信息的方法
    DatagramPacket提供了6个方法可以在创建数据报之后改变数据、远程地址、远程端口号。如果创建和销毁DatagramPacket对象的时间会严重影响性能,那么这些方法就很重要了。有些情况下,重用对象比构造新对象要快得多。
    public synchronized void setData(byte[] buf)
    public synchronized void setData(byte[] buf, int offset, int length)
    public synchronized void setAddress(InetAddress iaddr)
    public synchronized void setPort(int iport)
    public synchronized void setSocketAddress(SocketAddress address)
    public synchronized void setLength(int length)
  • setData():该方法会改变数据报的数据部分。如果要向远程主机发送大文件可能会用到这个方法。
  • setData():该重载方法提供另一个途径来发送大量的数据。与发送大量新数组不同,可以将所有数据放在一个数据中,每次发送一部分。
  • setAddress():该方法会修改数据报发往的地址。允许将同一个数据报发送给多个不同的接收方。
  • setPort():该方法会修改数据报发往的端口。
  • setSocketAddress():该方法会修改数据报要发往的地址和端口。在响应回复时可以使用这个方法。
  • setLength():该方法会修改内部缓冲区中包含实际数据报数据的字节数,而不包括未填充数据的空间。这个方法在接收数据报时很有用,当接收到数据报时,其长度设置为入站数据的长度。

DatagramSocket

要发送和接受数据报DatagramPacket,必须通过DatagramSocket进行传输,所有DatagramSocket都绑定到一个本地端口,在这个端口上监听入站数据,这个端口也会放置到出站数据报的首部中,以便服务器用来响应数据报的发送地址。一般情况下,客户端使用匿名端口,服务器需要指定监听端口。

DatagramSocket相关API

  • 构造函数
    public DatagramSocket() throws SocketException
    public DatagramSocket(int port) throws SocketException
    public DatagramSocket(int port, InetAddress laddr) throws SocketException
    public DatagramSocket(SocketAddress bindaddr) throws SocketException
    protected DatagramSocket(DatagramSocketImpl impl)

所有构造函数都只处理本地地址和端口,远程地址和端口存储在DatagramPacket中,一个DatagramSocket可以从多个远程主机和端口收发数据报。

  1. 该构造函数创建一个绑定到匿名端口的Socket,系统会自动为客户端分配一个端口。
  2. 该构造函数创建一个指定端口的Socket,可以使用这个方法编写在已知端口监听的服务器。
  3. 该构造函数创建在指定端口和网络接口的Socket,多用于多宿主主机(一个主机有多个IP地址)。第二个参数是匹配该主机某个网络接口的InetAddress对象。
  4. 该构造函数与第3个相似,只是网络接口地址和端口由SocketAddress包装。
  5. 该构造函数允许子类提供自己的UDP实现,而不接受默认实现。与其他四个方法不同,这个Socket一开始没有与端口绑定,使用前必须通过bind()方法绑定到一个SocketAddress。
  • 发送和接受数据报
    public void send(DatagramPacket p) throws IOException
    public synchronized void receive(DatagramPacket p) throws IOException
    public void close()
    public int getLocalPort()
    public InetAddress getLocalAddress()
  • getLocalPort():返回一个int整数,表示Socket正在监听的本地端口。如果创建了一个匿名端口的DatagramSocket,可以使用该方法获取到监听的端口号。
  • getLocalAddress():返回一个InetAddress对象,表示Socket绑定到的本地地址。实际中很少这样做,因为你已经知道了监听的地址。
  • 管理连接
    public void connect(InetAddress address, int port)
    public void connect(SocketAddress addr) throws SocketException
    public void disconnect()
    public int getPort()
    public InetAddress getInetAddress()
    public SocketAddress getRemoteSocketAddress()

利用上面5个方法,可以选择允许收发数据报的主机,而拒绝其它所有主机的包。

  • connect():并不真正建立TCP意义上的连接,不过它确实指定了DatagramSocket只对指定远程主机和指定远程端口发送和接受数据报。试图向其它主机和端口发送数据报将抛出异常,而从其它主机和端口接受的数据报将直接丢弃,没有异常也没有通知。
  • disconnect():中断连接,从而可以再次收发其它主机和端口的数据报。
  • getPort():当且仅当DatagramSocket已连接时,该方法返回所连接的远程端口,否则返回-1。
  • getInetAddress():当且仅当DatagramSocket已连接时,该方法返回所连接的远程主机地址,否则返回null。
  • getRemoteSocketAddress():如果DatagramSocket已连接,该方法返回所连接的远程主机的地址,否则返回null。

设置DatagramSocket属性

  • SO_TIMEOUT
  • SO_RCVBUF
  • SO_SNDBUF
  • SO_REUSEADDR
  • SO_BROADCAST
  • IP_TOS
  1. SO_TIMEOUT
    public synchronized void setSoTimeout(int timeout) throws SocketException

该属性是receive()方法在抛出InterruptedException异常前等待接收数据报的时间。如果该属性值为0,则receive()方法永远不会超时。如果要实现一个安全协议,需要在一定时间内响应就可能需要设置该属性。setSoTimeout()方法可以设置超时时间,如果超时了,阻塞的receive()方法就会抛出SocketTimeoutException异常。必须在receive()方法前调用。

  1. SO_RCVBUF
    public synchronized void setReceiveBufferSize(int size) throws SocketException

对于相当快的连接(如以太网的连接),较大的缓冲区有助于提升性能,因为在溢出前可以存储更多的入站数据报。与TCP相比,对于UDP而言,足够大的接收缓冲区甚至更为重要,因为在缓冲区满时到达的UDP数据报会丢失,而缓冲区满时到达的TCP数据报最后还会重传。

  1. SO_SNDBUF
    public synchronized void setSendBufferSize(int size) throws SocketException

设置网络输出的发送缓冲区大小。

  1. SO_REUSEADDR
    public synchronized void setReuseAddress(boolean on) throws SocketException

对于UDP来说,该属性可以控制是否允许多个数据报Socket同时绑定到相同的端口和地址。如果多个Socket绑定到相同端口,接收的包将复制给绑定的所有Socket。必须在新Socket绑定到端口前调用setReuseAddress()。

  1. SO_BROADCAST
    public synchronized void setBroadcast(boolean on) throws SocketException

该属性控制是否允许一个Socket向广播地址收发数据报。默认为true。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容