[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连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客户 ...
随机推荐
- 4、数据类型二:Lists
1.关于list的组织形式 列表数据类型(Lists)可以存储一个有序的字符串列表,常用的操作时向列表两段添加元素,或者获取列表的某一个片段.列表类型的底层实现是一个双向链表(double linke ...
- NetworkStream.write只能使用一次,后面再使用无效
public delegate void ShowMessageMethod2(TcpClient tc); private void ShowMessage2(TcpClient tc) { Str ...
- i++的原子性问题
一.什么是原子性 简单的可以理解为:操作是不可再分割的,比如: int i=0; 但是i++的操作是可以再分的,比如: i++ //分解后 i=i+i 上面的代码在多线程环境下取值是有问题的,比如: ...
- windows下Mysql5.7.10免安装版配置
免安装配置: 在环境变量 Path 中追加 %mysql_home%\bin; 配置mysql目录下的 my-default.ini 文件,在mysql 根目录下新建 data 文件夹 使用管理员权限 ...
- select sum也会返回null值
SELECT SUM(detail.VAL) FROM AI_SDP_ORDER_MONTH_DETAIL_201706 detail 如果所有的VAL都是null的话,或者根本就不存在 ...
- VMWare中三种网络连接模式的区别
VMWare中有桥接.NAT.host-only三种网络连接模式,在搭建伪分布式集群时,需要对集群的网络连接进行配置,而这一操作的前提是理解这三种网络模式的区别. 参考以下两篇文章可以更好的理解: V ...
- Spring3开发(一)
1 Ioc是什么? Ioc:Inversion of Control,控制反转,控制权从应用程序转移到框架(如Ioc容器),是框架的共有特性. 1.1 为什么需要IoC容器?IoC容器是如何演变的? ...
- 06.Lucen入门程序-Field
需求: 实现一个歌词搜索系统,通过关键字搜索,凡是文件名或文件内容包括关键字的文件都要找出来. 注意:该入门程序只对文本文件(.txt)搜索. Lucene中包含两个重要的类: IndexWriter ...
- solr自动提示 - jquery ui autocomplete
需求: 搜索框中 输入部分关键词之后,有下拉联想提示.选中提示,使用鼠标或者使用enter键,则触发搜索功能.没有选择搜索提示,使用部分输入关键词作为搜索,直接使用enter键也能触发搜索功能.整个过 ...
- rtx反向单点登录
1>通过ie浏览器的activx获取当前登录账户,其它浏览器不行,不支持activx. <%@ Page Language="C#" %> <!DOCTYP ...