[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连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客户 ...
随机推荐
- 跟我学算法-pca(降维)
pca是一种黑箱子式的降维方式,通过映射,希望投影后的数据尽可能的分散, 因此要保证映射后的方差尽可能大,下一个映射的方向与当前映射方向正交 pca的步骤: 第一步: 首先要对当前数据(去均值)求协方 ...
- Subversion Self Signed Certificates
When connecting to Subversion repositories using SSL connections the SVN client checks the server ce ...
- 微信小程序相关一、模仿京东静态登录页面
一.培训的第一天,基本上没有什么最新的东西,但是看到老师的代码收获的确实是不少. 1.1.首页代码很简洁,将共有的样式都提取的很好. 1.2.其次是每一个小块写样式的时候用到了好多子代选择器和后代选择 ...
- Qt Signal and Slot
Qt4中的信号槽 Qt4中的信号槽是通过SIGNAL,SLOT两个宏,将参数转换成字符串.Qt编译前,会从源码的头文件中提取由signal和slot声明的信号和槽的函数, 将其组成一张信号和槽对应的字 ...
- SQL 数据库 学习 004 预备知识
数据库 预备知识 我的电脑系统: Windows 10 64位 使用的SQL Server软件: SQL Server 2014 Express 如果我们要学习这个数据库,我们需要学习什么知识. 预备 ...
- sqlserver分区表索引
对于提高查询性能非常有效,因此,一般应该考虑应该考虑为分区表建立索引,为分区表建立索引与为普通表建立索引的语法一直,但是,其行为与普通索引有所差异. 默认情况下,分区表中创建的索引使用与分区表相同分区 ...
- Spring MVC拦截器(Interceptor )详解
处理器拦截器简介 Spring Web MVC的处理器拦截器(如无特殊说明,下文所说的拦截器即处理器拦截器)类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理. 常见应用场 ...
- if-return 语句
if(A > B): return A+1 return A-1 or if(A > B): return A+1 else: return A-1 +++++++++++++++++++ ...
- 约瑟夫问题的变种 LA3882
题目大意: N个数排成一圈,第一次删除m,以后每k个数删除一次,求最后一被删除的数. 如果这题用链表或者数组模拟整个过程的话,时间复杂度都将高达O(nk),而n<=10000,k<=100 ...
- Python将阿拉伯数字转化为中文大写-乾颐堂
利用Python将阿拉伯数字转化为中文大写,其实最麻烦的地方就是中间空多个0的问题,这种情况下,采用拆分法则,将一个大数字,先拆分成整数部分和小数部分,再对整数部分按照仟.万.亿.兆分位拆分为四个字符 ...