[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的更多相关文章
- JAVA基础知识之网络编程——-基于UDP协议的通信例子
UDP是一种不可靠的协议,它在通信两端各建立一个socket,这两个socket不会建立持久的通信连接,只会单方面向对方发送数据,不检查发送结果. java中基于UDP协议的通信使用DatagramS ...
- 基于TCP和UDP的Socket通信
TCP的Socket通信 TCP是面向连接的,安全的协议,它是一对一的关系 server client 上面只是单个客户端同服务器通信,可使用多线程编程实现多个客户端的通信 UDP的Socket通信 ...
- Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制
Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制 JAVA 中原生的 socket 通信机制 摘要:本文属于原创,欢迎转载,转载请保留出处:https://github.co ...
- TCP、UDP、Socket 通信(原)
说明:本随笔主要演示自己给自己发送消息例子,分别使用了TCP协议.UDP协议以及socket套接字通信.使用socket套接字了模拟TCP.UDP通信实现原理.其中有些源码都来自<C#高级编程 ...
- 我看不下去鸟。。。。Java和C#的socket通信真的简单吗?
这几天在博客园上看到好几个写Java和C#的socket通信的帖子.但是都为指出其中关键点. C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的.本人使用的是自己 ...
- java和C#之间SOCKET通信的问题
转自:http://www.cdtarena.com/javapx/201307/9170.html java和C#之间SOCKET通信的问题 一.服务器端(使用java编写) /** * 监听客户端 ...
- Java和C#的socket通信相关(转)
这几天在博客园上看到好几个写Java和C#的socket通信的帖子.但是都为指出其中关键点. C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的.本人使用的是自己 ...
- Java进阶(四十七)Socket通信
Java进阶(四十七)Socket通信 今天讲解一个 Hello Word 级别的 Java Socket 通信的例子.具体通讯过程如下: 先启动Server端,进入一个死循环以便一直监听某端口是 ...
- 基于android的Socket通信
一.Socket通信简介 Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客户 ...
随机推荐
- mysql 乱码 utf8
my.ini [mysql]default-character-set=utf8 [mysqld]character-set-server=utf8 show variables like '%cha ...
- 6. H.264/AVC编码器原理
1. H.264/AVC的应用 H.264 不仅具有优异的压缩性能,而且具有良好的网络亲和性,这对实时的视频通信是十分重要的.和 MPEG-4 中的重点是灵活性不同,H.264 着重在压缩的高效率和传 ...
- C#基础编程试题 4
一.题目 使用递归算法和二维数组,根据下面条件排序上面的对象. 五个人 张三 李四 王五 猴六 麻七 五间房子 黄房子 蓝房子 红房子 绿房子 橙房子 五只宠物 蜗牛 小狗 小猫 小白兔 小金鱼 五个 ...
- Cross-Browser, Event-based, Element Resize Detection(转)
DOM Elements! Y U No Resize Event? During your coding adventures, you may have run into occasions wh ...
- Spring3.0+Hibernate+Atomikos集成构建JTA的分布式事务--解决多数据源跨库事务
一.概念 分布式事务分布式事务是指事务的参与者.支持事务的服务器.资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上.简言之,同时操作多个数据库保持事务的统一,达到跨库事务的效果. JTA ...
- SPI protocol驱动编写实例
内核版本:3.9.5 Linux中SPI驱动有俩个部分组成:controller驱动,直接和底层硬件打交道,protocol驱动,针对特定的设备,也是我们要做的. 这里只考虑SPI protocol驱 ...
- Kafka源码分析
本文主要针对于Kafka的源码进行分析,版本为kafka-0.8.2.1. 由于时间有限,可能更新比较慢... Kafka.scala // 读取配置文件 val props = Utils.load ...
- redis 常用方法整理
1.进入redis redis-cli -p -h 192.168.0.100 -a q9pCeAEMAWEL 2.查询keys keys activity_mobile_* 3.赋值.查值.删除 s ...
- Linux的作业管理
一.作业管理的场景 作业管理(job control)是在bash环境下使用的,主要使用在同一个bash中管理多个作业的场景,譬如登录bash之后想同时复制文件.数据搜索,编译. 但是bash的作业管 ...
- linux系统的命令组与函数
一.命令组 许多时候,我们在shell操作上,需要在一定条件下一次执行多个命令,也就是说,要么不执行,要么就全执行,而不是每次依序的判断是否要执行下一个命令.或是,需要从一些命令执行优先次顺中得到豁免 ...