[java]基于UDP的Socket通信Demo

  java课编程作业:在老师给的demo的基础上实现客户端发送数据到服务器端,服务器端接受客户端后进行数据广播。

  整体功能类似于聊天室,代码部分不是太难,但是在本机测试的时候出现这样的问题:

    服务端通过将每一个Socket客户端的IP存入Set集合,每次接受到数据后都向当前所有的IP转发。但是本机演示的时候所有开的ChatClient客户端都是同一IP,怎么测试呢?

  解决办法就是本机测试时候服务端向多个不同的端口转发就好了,这样跑起来的客户端是在不同端口上进行监听的(只是为了实现广播,实际应用下还是通过IP来转发)。

  客户端代码:

  

import javax.print.attribute.HashPrintServiceAttributeSet;
import javax.swing.*;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Toolkit;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.*;

public class ChatClient extends JFrame {

    JTextArea contents = new JTextArea();
    JLabel label1 = new JLabel("服务器地址");
    JTextField address = new JTextField();
    JLabel label2 = new JLabel("用户名");
    JTextField username = new JTextField();
    JButton online = new JButton("上线");
    JButton offline = new JButton("下线");
    JTextField input = new JTextField();
    JButton send = new JButton("发送");

    boolean onlineFlag = false;

    public ChatClient() {
        super("Chat Client");

        contents.setEditable(false);
        getContentPane().add(contents);

        JPanel p1 = new JPanel();
        p1.setLayout(new FlowLayout(FlowLayout.LEADING));

        address.setPreferredSize(new Dimension(100, 28));
        username.setPreferredSize(new Dimension(80, 28));
        input.setPreferredSize(new Dimension(180, 28));

        p1.add(label1);
        p1.add(address);
        p1.add(label2);
        p1.add(username);
        p1.add(online);
        p1.add(offline);
        p1.add(input);
        p1.add(send);

        offline.setEnabled(false);
        send.setEnabled(false);

        getContentPane().add(p1, BorderLayout.SOUTH);

        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent we) {
                onlineFlag = false;
                Map<String, Object> m1 = new HashMap<String, Object>();
                m1.put("time", new Date()); // 上线时间
                m1.put("username", getUsername());
                m1.put("onlineFlag", onlineFlag);

                send(getServerAddress(), 2222, m1);

                System.exit(0);
            }
        });

        ActionListener al = new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                String server = getServerAddress();
                if (server == null || server.length() == 0) {
                    JOptionPane.showMessageDialog(ChatClient.this, "请输入服务器地址!");
                    return;
                }

                if (ae.getSource() == online) {
                    // 用户点击了上线按钮
                    if (!onlineFlag) {
                        onlineFlag = true;
                        Map<String, Object> m1 = new HashMap<String, Object>();
                        m1.put("time", new Date()); // 上线时间
                        m1.put("username", getUsername());
                        m1.put("onlineFlag", onlineFlag);
                        online.setEnabled(false);
                        offline.setEnabled(true);
                        send.setEnabled(true);
                        address.setEnabled(false);
                        username.setEnabled(false);
                        send(getServerAddress(), 2222, m1);

                    }
                } else {
                    // 用户点击了下线按钮
                    if (onlineFlag) {
                        onlineFlag = false;
                        Map<String, Object> m1 = new HashMap<String, Object>();
                        m1.put("time", new Date()); // 上线时间
                        m1.put("username", getUsername());
                        m1.put("onlineFlag", onlineFlag);
                        online.setEnabled(true);
                        offline.setEnabled(false);
                        send.setEnabled(false);
                        address.setEnabled(true);
                        username.setEnabled(true);
                        send(getServerAddress(), 2222, m1);
                    }
                }
            }
        };

        ActionListener al2 = new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                Map<String, Object> m1 = new HashMap<String, Object>();
                m1.put("time", new Date());
                m1.put("username", getUsername());
                m1.put("input", getInput());
                send(getServerAddress(), 2222, m1);
            }
        };

        send.addActionListener(al2);

        online.addActionListener(al);
        offline.addActionListener(al);

        // setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(720, 400);
        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();

        setLocation((d.width - 720) / 2, (d.height - 400) / 2);
        setVisible(true);

        new Thread() {
            public void run() {
                try {
                    // 在2221端口接收服务器发送过来的聊天内容,包括上线,下线消息。
                    DatagramSocket rSocket = new DatagramSocket(2220);
                    while (true) {
                        byte[] buffer = new byte[1024 * 16];
                        DatagramPacket recvPacket = new DatagramPacket(buffer, buffer.length);
                        rSocket.receive(recvPacket);
                        byte[] data = recvPacket.getData();
                        byte[] recvData = new byte[recvPacket.getLength()];
                        System.arraycopy(buffer, 0, recvData, 0, recvData.length);
                        //将从服务器接收的数据存入Map并显示
                        Map<String, Object> map = convertByteArrayToMap(recvData);
                        if (map.containsKey("onlineFlag")) { // 上线或者下线
                            boolean b = (boolean) map.get("onlineFlag");
                            if (b) { // 上线
                                map.put("input", "上线");
                            } else { // 下线
                                map.put("input", "下线");
                            }
                        }
                        Date time = (Date) map.get("time");
                        String s = convertDateToFormatString(time);
                        String user = (String) map.get("username");
                        user = "[" + user + "]";
                        contents.append(s + user + ": " + (String) map.get("input") + "\r\n");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }.start();

    }

    public String getServerAddress() {
        String s = address.getText();
        //trim删除多余空格
        s = s.trim();
        return s;
    }

    public String getUsername() {
        String s = username.getText();
        s = s.trim();
        if (s == null || s.length() == 0) {
            s = "匿名";
        }
        return s;
    }

    public String getInput() {
        String s = input.getText();
        s = s.trim();
        if (s == null || s.length() == 0) {
            s = "不想说什么";
        }
        return s;
    }

    public static String convertDateToFormatString(Date d) {
        return new java.text.SimpleDateFormat("(yyyy-MM-dd HH:mm:ss)").format(d);
    }

    public static void send(String ip, int port, Map<String, Object> map) {
        try {
            byte[] data = convertMapToByteArray(map);
            DatagramSocket socket = new DatagramSocket();
            DatagramPacket packet = new DatagramPacket(data, data.length);

            packet.setSocketAddress(new InetSocketAddress(ip, port));
            socket.send(packet);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public static byte[] convertMapToByteArray(Map<String, Object> map) {
        try {
            //完成此代码块,要求将map集合中的数据转换成字节数组

            ByteArrayOutputStream b = new ByteArrayOutputStream();
            ObjectOutputStream ois = new ObjectOutputStream(b);
            ois.writeObject(map);
            byte[] temp = b.toByteArray();
            return temp;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Map<String, Object> convertByteArrayToMap(byte[] data) {
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(data);
            ObjectInputStream ois = new ObjectInputStream(bais);
            Map<String, Object> result = (Map<String, Object>) ois.readObject();
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        new ChatClient();
    }

}

    服务端代码:

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;

import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;

public class ChatServer {

    LinkedBlockingQueue<Map<String, Object>> queue = new LinkedBlockingQueue<Map<String, Object>>();
    Set<String> onlineIPs = new HashSet<String>(); // 当前在线IP。

    public ChatServer() {
        try {

            new Thread() {
                public void run() {
                    while (true) {
                        try {
                            //补齐程序,要求从接收队列中取出数据,遍历在线IP集合,向所有在线IP发送数据

                            if( !queue.isEmpty() ){
                                if( !onlineIPs.isEmpty() ){
                                    for(String ip : onlineIPs){
                                        //下面的端口号可以自定义增加,初始转发至2220端口   多增加的端口可以实现本机内的广播
                                        ChatClient.send(ip, 2221, queue.peek() );
                                        ChatClient.send(ip, 2220, queue.remove() );
                                    }
                                }
                            }

                            //end
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }.start();
            //接受端口2222
            DatagramSocket serverSocket = new DatagramSocket(2222);
            System.out.println("Chat Server started.");
            while (true) {
                byte[] buffer = new byte[1024 * 16];
                DatagramPacket recvPacket = new DatagramPacket(buffer, buffer.length);

                serverSocket.receive(recvPacket);

                InetSocketAddress remoteAddress = new InetSocketAddress(recvPacket.getAddress(),
                        recvPacket.getPort());
                System.out.println("接收到 " + remoteAddress.getHostString() + " 发送过来的数据包");

                byte[] data = recvPacket.getData();
                byte[] recvData = new byte[recvPacket.getLength()];
                System.arraycopy(buffer, 0, recvData, 0, recvData.length);
                System.out.println(recvData.length);
                Map<String, Object> map = ChatClient.convertByteArrayToMap(recvData);
                System.out.print("map长度"+map.size());
                if (map.containsKey("onlineFlag")) { // 上线或者下线
                    boolean b = (boolean) map.get("onlineFlag");
                    if (b) { // 上线
                        onlineIPs.add(remoteAddress.getHostString());
                    } else { // 下线
                        onlineIPs.remove(remoteAddress.getHostString());
                    }
                }

                queue.add(map);

            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new ChatServer();

    }

}

[java]基于UDP的Socket通信Demo

时间: 2024-10-03 01:35:36

[java]基于UDP的Socket通信Demo的相关文章

recv原理、高阶版黏包解决方案、基于UDP的socket通信

recv原理.高阶版黏包解决方案.基于UDP的socket通信 recv原理 源码解释: Receive up to buffersize bytes from the socket. 接收来自socket缓冲区的字节数据, For the optional flags argument, see the Unix manual. 对于这些设置的参数,可以查看Unix手册. When no data is available, block untilat least one byte is av

JAVA基础知识之网络编程——-基于UDP协议的通信例子

UDP是一种不可靠的协议,它在通信两端各建立一个socket,这两个socket不会建立持久的通信连接,只会单方面向对方发送数据,不检查发送结果. java中基于UDP协议的通信使用DatagramSocket类的receive和send方法即可,但消息需要通过一个特定的类封装(DatagramPacket) 下面是一个基于UDP协议的通信的例子, 服务器端, 1 package udp; 2 3 import java.io.IOException; 4 import java.net.Dat

Java 和 Python 的 Socket 通信

网络上两个程序通过一个双向通讯连接实现数据的交换,这个双向链路的一端称为一个Socket.Socket支持的协议有多种,这里主要介绍基于 TCP/IP 协议族的 Socket 编程. 首先,IP协议族决定了socket的地址类型,在通信中必须采用对应的地址.AF_INET(AF 表示 Adress Family)表示要用 ipv4 地址(32位)与端口号(16位)的组合. 然后,根据传输协议又分为:流式 Socket(SOCK_STREAM) 和数据报式 Socket(SOCK_DGRAM):

基于UDP的Socket网络编程实现用户登录的实现

package Test_UDP_Socket; import java.net.DatagramPacket.* //服务器端 import java.net.DatagramSocket; import java.net.InetAddress; public class ServerUDP { public static void main(String[] args) throws Exception{ //1.先实现接收Client信息 DatagramSocket socketSer

基于UDP的socket客户服务器编程

前面我们写了关于TCP的客户/服务器模式,现在我们写关于UDP的客户/服务器模式. 基于TCP编写的应用程序和基于TCP编写的应用程序之间存在一些本质的差异,其原因在于这两个传输层之间的差别:UDP是无连接不可靠的数据报协议,不同于TCP提供的面向连接的可靠字节流. 我们先来说一下简单的模型:在基于UDP的应用程序中,客户不与服务器建立连接,而只是使用sendto函数给服务器发送数据报,其中必须指定目的地(即服务器)的地址作为参数.当然,在服务器端不接受来自客户的连接,只是使用recvfrom函

TCP、UDP、Socket 通信(原)

说明:本随笔主要演示自己给自己发送消息例子,分别使用了TCP协议.UDP协议以及socket套接字通信.使用socket套接字了模拟TCP.UDP通信实现原理.其中有些源码都来自<C#高级编程 第7版>,并附加了自己的理解,有的也进行了一些简单的拓展. 第一次原创随笔,很多地方可能考虑不周或理解有误,希望大家留言指正,与大家共同进步.也希望大家不喜勿喷,给点鼓励还是比较好的~~ 闲话少说,进入主题! 一.TCP 类 TCP是基于连接的一种通信模式,我们需要创建客户端与服务器端来进行通信.在客户

基于UDP的socket编程

一.相关函数说明 UDP是无连接的,即发送数据之前不需要建立连接. 除了基于TCP中的socket编程所需的函数之外,基于UDP的socket编程中还需要用到两个函数. 1.sendto函数:用于客户端中指定一目的地发送数据. (1)函数原型 (2)参数说明 sockfd:套接字 buf:待发送数据的缓冲区 len:缓冲区长度 flags:调用方式标志位,一般为0:若改变flags,则sendto发送数据的形式会变成阻塞 dest_addr:指向目的套接字的地址 addrlen:指向目的套接字的

Java网络编程之Socket通信(二)

之前在前面已经介绍了Socket通信的一些基本原理,以及如何让客户端与服务器端建立通信,和实现通信的一些基本步骤(包括首先使得服务器端与客户端建立连接,建立连接之后,服务器端开始侦听客户端的请求,侦听到客户端的请求之后,通过输入输出流处理相关信息实现通信,最后通信完毕结束通信等一系列流程). 但是之前只是单个客户端与服务器进行通信,而我们实际应用中单个客户端的情况几乎不存在,都是多个客户端同时与服务器进行交互(这里同时交互就会出现并发性的问题,对于并发性的问题暂时还不是很懂,只知道有这个概念),

我看不下去鸟。。。。Java和C#的socket通信真的简单吗?

这几天在博客园上看到好几个写Java和C#的socket通信的帖子.但是都为指出其中关键点. C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的.本人使用的是自己开发的一套组件. Java socket通信的组件也有很多,常用的大多数都是用的mina或者netty.游戏行业使用也是居多. 关于socket的底层写法,实在太多,我就不在BB. 这里我想说,C#和C++或者叫VC++把是使用小端序作为字节序.而java使用的是大端序作为字节序. 也就是说比如一个