由于最近编写的游戏涉及到了网络编程这块,所以特意记录下。
UDP简介
UDP协议的全称是用户数据报,在网络中它与TCP协议一样用于处理数据报。在OSI模型中,UDP位于第四层——传输层,处于IP协议额上一层。UDP有不提供数据报分组、组装以及不能对数据报排序的缺点。当报文发送之后,是无法得知其是否安全完整到达的。
由于UDP不属于连接性协议的特性,因此具有资源消耗小、处理速度快的优点,所以通过音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响,如人们聊天使用的ICQ和OICQ使用的就是UDP协议。
使用java.net包下的DatagramSocket和DatagramPacket类,可以非常方便地控制用户数据报文。下面就对这两个类进行介绍
DatagramPacket类
DatagramPacket类用于处理报文,他将byte数组、目标地址和目标端口等数据包装成报文或者将报文拆卸成byte数组。
DatagramPacket有多个构造方法,通常情况下它们都有两个共同的参数:byte[] buffer和int length。其中buffer参数包含了一个对保存自寻址数据报信息的字节数组的引用,length表示字节数组的长度。
最简单的构造方法是:
DatagramPacket(byte[] buffer,int length);
这个构造方法确定了数据报数组和数组的长度,但没有任何数据报的地址和端口信息,这些信息可以通过调用方法setAddress(InetAddress addr)和setPort(int port)添加。下面代码示范这些方法的使用:
byte[] buffer = new byte[100];
DatagramPacket dgp = new DatagramPacket(buffer,buffer.length);
InetAddress address = InetAddress.getByName("www.disney.com");
dgp.setAddress(address);
dgp.setPort(6666);//设置数据报发送端口
如果用户更喜欢在调用构造方法时同时包括地址和端口号,则可以使用:
DatagramPacket(byte[] buffer,int length,InetAddress address,int port);
如果在创建DatagramPacket对象后想要改变字节数组和它的长度,可以通过setData()和setLength()方法进行修改:
setData(byte[] buffer);
setLength(int length);
DatagramPacket类的常用方法如下:
![](/qrcode.jpg)
getAddress()、setAddress(InetAddress address):得到、设置数据博地址
getData()、setData(byte[] buffer):得到、设置数据报内容
getLength()、setLength(int length):得到、设置数据报长度
getPort()、setPort(int port)得到、设置端口号
DatagramSocket类
DatagramSocket类在客户端创建数据报套接字与服务端进行通信连接,并发送和接受数据报套接字。
客户端套接字最常用的构造方法是DatagramSocket()函数,而服务器端则是DatagramSocket(int port)函数。如果未能创建套接字或者绑定套接字到本地端口,那么这两个函数都将抛出一个SocketException异常,一旦程序创建了DatagramSocket对象,那么程序分别调用send(DatagramPacket p)和receive(DatagramPacket p)来发送和接受数据报。
DatagramSocket构造方法如下:
DatagramSocket():创建数据报套接字,绑定到本地任意存在的端口;
DatagramSocket(int port):创建数据报套接字,绑定到本地主机指定端口;
DatagramSocket(int port,InetAddress address):创建数据报套接字,绑定到指定本地地址。
常用方法如下:
connect(InetAddress address,int port):连接指定地址
disconnect():断开套接字连接
close():关闭数据报套接字
getInetAddress():得到套接字所连接的主机地址
getLocalAddress():得到套接字绑定的主机地址
getLocalPort():得到套接字的端口号
receive(DatagramPacket p):接收数据报
send(DatagramPacket p):发送数据报。
实践
实现一个简单的双向聊天的功能:
创建两个类:Client类和Server类
初始状况下,双方都处于空闲状态,客户端窗口和服务端窗口输入文字按下回车后,对方的窗口能及时显示对方发送过来的消息。
实现思路:
两个类的代码可以大量复用,创建两个数据报套接字sendSocket和receiveSocket,其中sendSocket用来发送消息,而receiveSocket用来接收消息;实现Runnable接口,创建线程持续监听receiveSocket监听的端口是否有发送来消息,如果有则控制台进行消息的打印。创建一个Scanner对象,持续读取控制台输入,如果控制台输入完毕并回车,则调用send方法将输入的消息封装到数据报然后发送到对方监听的端口。这里服务端监听3002端口,客户端监听3001,代码实现如下:
Server类:
package 网络编程;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class Server implements Runnable{
DatagramSocket sendSocket;
DatagramSocket receiveSocket;
public Server(){
try {
receiveSocket = new DatagramSocket(3002);
sendSocket = new DatagramSocket();
} catch (Exception e) {
e.printStackTrace();
}
new Thread(this).start();
}
public static void main(String[] args) {
System.out.println("服务端|空闲中...");
Server server = new Server();
Scanner input = new Scanner(System.in);
while(true){
String s = input.next();
server.send(s);
}
}
public void send(String str){
try {
byte[] buffer;
buffer = str.getBytes();
InetAddress ia;
ia = InetAddress.getByName("127.0.0.1");
DatagramPacket dgp = new DatagramPacket(buffer,buffer.length,ia,3001);
sendSocket.send(dgp);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
byte[] data = new byte[100];
DatagramPacket dgp = new DatagramPacket(data,data.length);
while(true){
try {
receiveSocket.receive(dgp);
System.out.println("客户端|"+new String(data));
for(int i=0;i<100;i++)
data[i] = 0;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Client类:
package 网络编程;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class Client implements Runnable{
DatagramSocket sendSocket;
DatagramSocket receiveSocket;
public Client(){
try {
receiveSocket = new DatagramSocket(3001);
sendSocket = new DatagramSocket();
} catch (Exception e) {
e.printStackTrace();
}
Thread t = new Thread(this);
t.start();
}
public static void main(String[] args) {
System.out.println("客户端|空闲中...");
Client client = new Client();
Scanner input = new Scanner(System.in);
while(true){
String s = input.next();
client.send(s);
}
}
public void send(String str){
try {
byte[] buffer;
buffer = str.getBytes();
InetAddress ia;
ia = InetAddress.getByName("127.0.0.1");
DatagramPacket dgp = new DatagramPacket(buffer,buffer.length,ia,3002);
sendSocket.send(dgp);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
byte[] data = new byte[100];
DatagramPacket dgp = new DatagramPacket(data,data.length);
while(true){
try {
receiveSocket.receive(dgp);
System.out.println("服务端|"+new String(data));
for(int i=0;i<100;i++)
data[i] = 0;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行效果如图: