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的更多相关文章

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

    UDP是一种不可靠的协议,它在通信两端各建立一个socket,这两个socket不会建立持久的通信连接,只会单方面向对方发送数据,不检查发送结果. java中基于UDP协议的通信使用DatagramS ...

  2. 基于TCP和UDP的Socket通信

    TCP的Socket通信 TCP是面向连接的,安全的协议,它是一对一的关系 server client 上面只是单个客户端同服务器通信,可使用多线程编程实现多个客户端的通信 UDP的Socket通信 ...

  3. Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制

    Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制 JAVA 中原生的 socket 通信机制 摘要:本文属于原创,欢迎转载,转载请保留出处:https://github.co ...

  4. TCP、UDP、Socket 通信(原)

    说明:本随笔主要演示自己给自己发送消息例子,分别使用了TCP协议.UDP协议以及socket套接字通信.使用socket套接字了模拟TCP.UDP通信实现原理.其中有些源码都来自<C#高级编程 ...

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

    这几天在博客园上看到好几个写Java和C#的socket通信的帖子.但是都为指出其中关键点. C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的.本人使用的是自己 ...

  6. java和C#之间SOCKET通信的问题

    转自:http://www.cdtarena.com/javapx/201307/9170.html java和C#之间SOCKET通信的问题 一.服务器端(使用java编写) /** * 监听客户端 ...

  7. Java和C#的socket通信相关(转)

    这几天在博客园上看到好几个写Java和C#的socket通信的帖子.但是都为指出其中关键点. C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的.本人使用的是自己 ...

  8. Java进阶(四十七)Socket通信

    Java进阶(四十七)Socket通信   今天讲解一个 Hello Word 级别的 Java Socket 通信的例子.具体通讯过程如下: 先启动Server端,进入一个死循环以便一直监听某端口是 ...

  9. 基于android的Socket通信

    一.Socket通信简介 Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客户 ...

随机推荐

  1. MySQL修改redo_log_size

    MySQL5.5 步骤如下: 1. set global innodb_fast_shutdown = 0; 2. mysqladmin shutdown 3. 修改my.cnf innodb_log ...

  2. jaegeropentracing的Java-client

    关于jaegeropentracing的Java-client做记录如下: 1.依赖jar包 <!-- 以下jar包是jaegeropentracing依赖的日志jar -->slf4j- ...

  3. 【303】C# 复制窗体 & 修改名称

    参考:C#复制粘贴窗体 参考:VS修改项目解决方案名称 一.复制窗体 在“解决方案资源管理器”(以下简称:管理器)中选择要复制的窗体,比如要复制Form2,则在Form2.cs上右单击,选择复制. 在 ...

  4. https 理解

    这张图已经很明白,以下为翻译: 一.秘钥传输过程(确认秘钥相同) 1.请求服务端 2.服务端有公司要 3.公钥发给客户端 4.验证发过来的公钥crt,生成随机秘钥,并用此公钥加密 5.加密后的秘钥发给 ...

  5. Lamber算法 & SurfaceShader自定义光照

    [SurfaceShader自定义光照] 1.在pragma中添加自定义光照函数名:  #pragma surface surf BasicDiffuse 2.实现自定义光照函数.下面就是Lamber ...

  6. linux系统chkconfig使用方法及服务开机启动

    一.基础知识 有关linux系统开机过程.运行等级,执行权限请看另一篇:linux系统启动过程及运行等级详解. 本篇文章实践的系统:centos6.5 二.创建服务 通过之前的说明,我们知道了如果需要 ...

  7. opencv在64位4418上的移植

    1.mkdir build 2.cmake-gui 操作系统写Linux 去掉 去掉WITH_CUDA 去掉WITH_GTK 去掉WITH_1394 去掉WITH_GSTREAMER 去掉WITH_L ...

  8. Java 8特性

    1. Java8的新特性 1.1. Lambda表达式和函数式接口 最简单的Lambda表达式可以用逗号分隔的参数列表.->符号和功能语句块来表示.示例如下: Arrays.asList( &q ...

  9. Openssl enc命令

    一.简介 enc - 对称加密例程,使用对称密钥对数据进行加解密,特点是速度快,能对大量数据进行处理.算法有流算法和分组加密算法,流算法是逐字节加,由于其容易被破译,现在已很少使用:分组加密算法是将数 ...

  10. loadrunner-27077报错解决办法

    警告 -27077: “每次迭代模拟一个新用户”运行时设置为“开”时,“vuser_init”节将包含 Web 函数.这可能会产生具有多次迭代的不可预测结果   [MsgId: MWAR-27077] ...