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

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

描述客户端和服务器端的端口号clientportserverport

用于描述通讯信息的buffercontent对象。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的服务端里,为了同客户端对应,所以同样把clientportserverport值设置为19851986,同时初始化了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的快捷键的方式退出这段程序。


你可能感兴趣的:(编程,socket,String,服务器,buffer,通讯)