您的位置:首页 > 编程语言

socket编程(三)---- UDP协议与传输数据报文

2012-02-29 11:12 627 查看
UDP协议一般应用在“群发信息”的场合,所以它可以利用多线程机制,实现多信息的同步发送。

为了改善代码结构,把一些业务逻辑的动作抽象成方法,并封装成类,这样,基于UDP功能的类就可以在其他应用项目里被轻易的重用。

如果把客户端的所有代码都写在一个文件中,那么代码的功能很有可能都聚集在一个方法里,代码的可维护性将会变得很差。所以专门设计ClientBean类,在其中封装了客户端通讯的一些功能方法,在此基础上,通过UDPClient.java文件,实现UDP客户端的功能。

首先,设计ClientBean类。

public class ClientBean {
private DatagramSocket ds;//描述UDP通讯的DatagramSocket对象
private byte buffer[];//用来封装通讯字符串
private int clientport;//客户端的端口号
private int serverport;//服务器端的端口号
private String content;//通讯内容
private InetAddress ia;//描述通讯地址
public DatagramSocket getDs() {
return ds;
}
public void setDs(DatagramSocket ds) {
this.ds = ds;
}
public byte[] getBuffer() {
return buffer;
}
public void setBuffer(byte[] buffer) {
this.buffer = buffer;
}
public int getClientport() {
return clientport;
}
public void setClientport(int clientport) {
this.clientport = clientport;
}
public int getServerport() {
return serverport;
}
public void setServerport(int serverport) {
this.serverport = serverport;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public InetAddress getIa() {
return ia;
}
public void setIa(InetAddress ia) {
this.ia = ia;
}
public ClientBean() throws SocketException, UnknownHostException {
buffer = new byte[1024];
clientport = 1985;
serverport = 1986;
content = "";
ds = new DatagramSocket(clientport);
ia = InetAddress.getByName("localhost");
}
public void sendToServer() throws IOException{
buffer = content.getBytes();
ds.send(new DatagramPacket(buffer, content.length(), ia, serverport));

}
}


在上述的代码里定义了描述用来实现UDP通讯的DatagramSocket类型对象ds,

描述客户端和服务器端的端口号clientport和serverport,

用于描述通讯信息的buffer和content对象。buffer对象是byte数组类型的,可通过UDP的数据报文传输,而content是String类型的,在应用层面表示用户之间的通讯内容,

另外还定义了InetAddress类型的ia变量,用来封装通讯地址信息。

在构造函数里,给哥哥变量赋予了初始值,分别设置了客户端和服务端的端口号,设置了通讯链接地址为本地,并根据客户端的端口号初始化了DatagramSocket对象。当初始化ClientBean时,这段构造函数会自动执行,完成设置通讯各参数等工作。

向服务端发送消息的sendToServer()方法,根据String类型的表示通讯信息的content变量,初始化UDP数据报文,即DatagramPacket对象,并通过DatagramSocket类型对象的send方法,发送该UDP报文。

纵观ClientBean类,可以发现在其中封装了诸如通讯端口,通讯内容和通讯报文等对象以及以UDP方式发送信息的sendToServer方法,所以,在UDPClient类里,可以直接调用其中的借口,方便地实现通讯功能。

其次,设计UDPClient类

public class UDPClient implements Runnable{
public static String content;
public static ClientBean client;
@Override
public void run() {
try {
client.setContent(content);
client.sendToServer();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
client = new ClientBean();
System.out.println("客户端启动...");
while(true){
content = br.readLine();//接收用户输入
if(content == null||content.equalsIgnoreCase("end")||content.equalsIgnoreCase("")){
break;
}
new Thread(new UDPClient()).start();//开启新线程,发送消息
}
}
}


由于要在UDP客户端里通过多线程的机制,同时开多个客户端,向服务器端发送通讯内容,所以我们的UDPClient类必须要实现Runnable接口,并在其中覆盖掉Runnable接口里的run方法。run方法里,我们主要通过了ClientBean类里封装的方法,设置了content内容,并通过了sentToServer方法,将content内容以数据报文的形式发送到服务器端。一旦线程被开启,系统会自动执行定义在run方法里的动作。

main方法里首先初始化了BufferedReader类型的br对象,该对象可以接收从键盘输入的字符串。随后启动一个while(true)的循环,在这个循环体里,接收用户从键盘的输入,如果用户输入的字符串不是“end”,或不是为空,则开启一个UDPClient类型的线程,并通过定义在run方法里的线程主体动作,发送接收到的消息。如果在循环体里,接收到“end”或空字符,则通过break语句,退出循环。对于每次UDP发送请求,UDPClient类都将会启动一个线程来发送消息。

同样的,我们把服务器端所需要的一些通用方法以类的形式封装,而在UDP的服务器端,通过调用封装在ServerBean类里的方法来完成信息的接收工作。

首先,设计ServerBean类

public class ServerBean {
private DatagramSocket ds;//描述UDP通讯的DatagramSocket对象
private byte buffer[];//用来封装通讯字符串
private int clientport;//客户端的端口号
private int serverport;//服务器端的端口号
private String content;//通讯内容
private InetAddress ia;//描述通讯地址
public DatagramSocket getDs() {
return ds;
}
public void setDs(DatagramSocket ds) {
this.ds = ds;
}
public byte[] getBuffer() {
return buffer;
}
public void setBuffer(byte[] buffer) {
this.buffer = buffer;
}
public int getClientport() {
return clientport;
}
public void setClientport(int clientport) {
this.clientport = clientport;
}
public int getServerport() {
return serverport;
}
public void setServerport(int serverport) {
this.serverport = serverport;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public InetAddress getIa() {
return ia;
}
public void setIa(InetAddress ia) {
this.ia = ia;
}
public ServerBean() throws SocketException, UnknownHostException {
buffer = new byte[1024];
clientport = 1985;
serverport = 1986;
content = "";
ds = new DatagramSocket(serverport);
ia = InetAddress.getByName("localhost");
}
public void listenClient() throws IOException {
while(true){
//初始化DatagramPacket类型的变量
DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
//接收消息,并把消息通过dp参数返回
ds.receive(dp);
content = new String(dp.getData(),0,dp.getLength());
print();//打印消息
}
}
private void print() {
System.out.println(content);
}
}


在UDP的服务端里,为了同客户端对应,所以同样把clientport和serverport值设置为1985和1986,同时初始化了DatagramSocket对象,并把服务器的地址也设置成本地。

在listenClient()方法里,构造了一个while循环,循环体内部,调用了封装在DatagramSocket类型里的receive方法,接收客户端发送过来的UDP报文,并打印出来。

接着,来设计UDPServer类

public class UDPServer {
public static void main(String[] args) throws IOException {
System.out.println("服务端启动...");
//初始化ServerBean对象
ServerBean server = new ServerBean();
server.listenClient();
}
}


在UDP的服务器端里,主要通过ServerBean类里提供的listenClient方法,监听从客户端发送过来的UDP报文,并通过解析得到其中包含的字符串,随后输出。

最后,进行测试。先开启服务端,然后开启客户端,在客户端通过键盘向服务器端输入通讯字符串,这些字符串将会以数据报文的形式发送到服务器端

每当我们在客户端发送一条消息,服务器端会收到并输出这条消息,从代码里我们可以得知,每条消息是通过为之新开启的线程发送到服务器端的。如果我们在客户端输入”end”或空字符串,客户端的UDPClient代码会退出。由于UDPServer.java代码里,我们通过一个while(true)的循环来监听客户端的请求,所以当程序运行结束后,可通过Ctrl+C的快捷键的方式退出这段程序。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: