精华内容
下载资源
问答
  • 这是我们这学期期末网络编程的作业,老师要求我们做的课程作业。...UI界面背景图片是我用光影魔术手ps的,感觉还可以凑合着看。 压缩包里面个可运行的jar格式的文件,你可以运行试试。我编完后,没有发现BUG
  • Java Socket实现多人聊天室---swing做UI

    千次阅读 2011-08-31 15:06:00
    今天翻硬盘的workspace发现一个Java Socket实现多人聊天室的源码,不记得是什么时候的事情了,貌似不是我写的。但写得还不错, 至少算个样的聊天室工具。我简单的修改了一下,拿出来跟大家分享一下,仅供...

    今天翻硬盘的workspace发现一个Java Socket实现多人聊天室的源码,不记得是什么时候的事情了,貌似不是我写的。但写得还不错,

    至少算个有模有样的聊天室工具。我简单的修改了一下,拿出来跟大家分享一下,仅供参考。。。

    界面是用swing写的,还不懒,简约大方。有图有真相:

    正如上图所示,这个程序分为 服务器端和客户端,说白了就是 两个main class,用eclipse直接运行之。。。。。

    聊天室的设计思想是 :在局域网下, 利用socket进行连接通信,当服务器端启动的时候,利用Thread线程不停的等待客户端的链接;当有客户端开启连

    接的时候,服务器端通过IO流反馈“上线用户”信息给客户端,客户端也使用线程不停的接收服务器的信息,从而实现多人在线聊天功能。

    程序中有三个类,分别 Server.java(服务器端)、Client(客户端)、User.java(javabean)。代码如下:

    Server.java(服务器端):

    import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridLayout; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.BindException; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.StringTokenizer; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.border.TitledBorder; public class Server { private JFrame frame; private JTextArea contentArea; private JTextField txt_message; private JTextField txt_max; private JTextField txt_port; private JButton btn_start; private JButton btn_stop; private JButton btn_send; private JPanel northPanel; private JPanel southPanel; private JScrollPane rightPanel; private JScrollPane leftPanel; private JSplitPane centerSplit; private JList userList; private DefaultListModel listModel; private ServerSocket serverSocket; private ServerThread serverThread; private ArrayList<ClientThread> clients; private boolean isStart = false; // 主方法,程序执行入口 public static void main(String[] args) { new Server(); } // 执行消息发送 public void send() { if (!isStart) { JOptionPane.showMessageDialog(frame, "服务器还未启动,不能发送消息!", "错误", JOptionPane.ERROR_MESSAGE); return; } if (clients.size() == 0) { JOptionPane.showMessageDialog(frame, "没有用户在线,不能发送消息!", "错误", JOptionPane.ERROR_MESSAGE); return; } String message = txt_message.getText().trim(); if (message == null || message.equals("")) { JOptionPane.showMessageDialog(frame, "消息不能为空!", "错误", JOptionPane.ERROR_MESSAGE); return; } sendServerMessage(message);// 群发服务器消息 contentArea.append("服务器说:" + txt_message.getText() + "\r\n"); txt_message.setText(null); } // 构造放法 public Server() { frame = new JFrame("服务器"); // 更改JFrame的图标: //frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Client.class.getResource("qq.png"))); frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Server.class.getResource("qq.png"))); contentArea = new JTextArea(); contentArea.setEditable(false); contentArea.setForeground(Color.blue); txt_message = new JTextField(); txt_max = new JTextField("30"); txt_port = new JTextField("6666"); btn_start = new JButton("启动"); btn_stop = new JButton("停止"); btn_send = new JButton("发送"); btn_stop.setEnabled(false); listModel = new DefaultListModel(); userList = new JList(listModel); southPanel = new JPanel(new BorderLayout()); southPanel.setBorder(new TitledBorder("写消息")); southPanel.add(txt_message, "Center"); southPanel.add(btn_send, "East"); leftPanel = new JScrollPane(userList); leftPanel.setBorder(new TitledBorder("在线用户")); rightPanel = new JScrollPane(contentArea); rightPanel.setBorder(new TitledBorder("消息显示区")); centerSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightPanel); centerSplit.setDividerLocation(100); northPanel = new JPanel(); northPanel.setLayout(new GridLayout(1, 6)); northPanel.add(new JLabel("人数上限")); northPanel.add(txt_max); northPanel.add(new JLabel("端口")); northPanel.add(txt_port); northPanel.add(btn_start); northPanel.add(btn_stop); northPanel.setBorder(new TitledBorder("配置信息")); frame.setLayout(new BorderLayout()); frame.add(northPanel, "North"); frame.add(centerSplit, "Center"); frame.add(southPanel, "South"); frame.setSize(600, 400); //frame.setSize(Toolkit.getDefaultToolkit().getScreenSize());//设置全屏 int screen_width = Toolkit.getDefaultToolkit().getScreenSize().width; int screen_height = Toolkit.getDefaultToolkit().getScreenSize().height; frame.setLocation((screen_width - frame.getWidth()) / 2, (screen_height - frame.getHeight()) / 2); frame.setVisible(true); // 关闭窗口时事件 frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { if (isStart) { closeServer();// 关闭服务器 } System.exit(0);// 退出程序 } }); // 文本框按回车键时事件 txt_message.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { send(); } }); // 单击发送按钮时事件 btn_send.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { send(); } }); // 单击启动服务器按钮时事件 btn_start.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (isStart) { JOptionPane.showMessageDialog(frame, "服务器已处于启动状态,不要重复启动!", "错误", JOptionPane.ERROR_MESSAGE); return; } int max; int port; try { try { max = Integer.parseInt(txt_max.getText()); } catch (Exception e1) { throw new Exception("人数上限为正整数!"); } if (max <= 0) { throw new Exception("人数上限为正整数!"); } try { port = Integer.parseInt(txt_port.getText()); } catch (Exception e1) { throw new Exception("端口号为正整数!"); } if (port <= 0) { throw new Exception("端口号 为正整数!"); } serverStart(max, port); contentArea.append("服务器已成功启动!人数上限:" + max + ",端口:" + port + "\r\n"); JOptionPane.showMessageDialog(frame, "服务器成功启动!"); btn_start.setEnabled(false); txt_max.setEnabled(false); txt_port.setEnabled(false); btn_stop.setEnabled(true); } catch (Exception exc) { JOptionPane.showMessageDialog(frame, exc.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); } } }); // 单击停止服务器按钮时事件 btn_stop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (!isStart) { JOptionPane.showMessageDialog(frame, "服务器还未启动,无需停止!", "错误", JOptionPane.ERROR_MESSAGE); return; } try { closeServer(); btn_start.setEnabled(true); txt_max.setEnabled(true); txt_port.setEnabled(true); btn_stop.setEnabled(false); contentArea.append("服务器成功停止!\r\n"); JOptionPane.showMessageDialog(frame, "服务器成功停止!"); } catch (Exception exc) { JOptionPane.showMessageDialog(frame, "停止服务器发生异常!", "错误", JOptionPane.ERROR_MESSAGE); } } }); } // 启动服务器 public void serverStart(int max, int port) throws java.net.BindException { try { clients = new ArrayList<ClientThread>(); serverSocket = new ServerSocket(port); serverThread = new ServerThread(serverSocket, max); serverThread.start(); isStart = true; } catch (BindException e) { isStart = false; throw new BindException("端口号已被占用,请换一个!"); } catch (Exception e1) { e1.printStackTrace(); isStart = false; throw new BindException("启动服务器异常!"); } } // 关闭服务器 @SuppressWarnings("deprecation") public void closeServer() { try { if (serverThread != null) serverThread.stop();// 停止服务器线程 for (int i = clients.size() - 1; i >= 0; i--) { // 给所有在线用户发送关闭命令 clients.get(i).getWriter().println("CLOSE"); clients.get(i).getWriter().flush(); // 释放资源 clients.get(i).stop();// 停止此条为客户端服务的线程 clients.get(i).reader.close(); clients.get(i).writer.close(); clients.get(i).socket.close(); clients.remove(i); } if (serverSocket != null) { serverSocket.close();// 关闭服务器端连接 } listModel.removeAllElements();// 清空用户列表 isStart = false; } catch (IOException e) { e.printStackTrace(); isStart = true; } } // 群发服务器消息 public void sendServerMessage(String message) { for (int i = clients.size() - 1; i >= 0; i--) { clients.get(i).getWriter().println("服务器:" + message + "(多人发送)"); clients.get(i).getWriter().flush(); } } // 服务器线程 class ServerThread extends Thread { private ServerSocket serverSocket; private int max;// 人数上限 // 服务器线程的构造方法 public ServerThread(ServerSocket serverSocket, int max) { this.serverSocket = serverSocket; this.max = max; } public void run() { while (true) {// 不停的等待客户端的链接 try { Socket socket = serverSocket.accept(); if (clients.size() == max) {// 如果已达人数上限 BufferedReader r = new BufferedReader( new InputStreamReader(socket.getInputStream())); PrintWriter w = new PrintWriter(socket .getOutputStream()); // 接收客户端的基本用户信息 String inf = r.readLine(); StringTokenizer st = new StringTokenizer(inf, "@"); User user = new User(st.nextToken(), st.nextToken()); // 反馈连接成功信息 w.println("MAX@服务器:对不起," + user.getName() + user.getIp() + ",服务器在线人数已达上限,请稍后尝试连接!"); w.flush(); // 释放资源 r.close(); w.close(); socket.close(); continue; } ClientThread client = new ClientThread(socket); client.start();// 开启对此客户端服务的线程 clients.add(client); listModel.addElement(client.getUser().getName());// 更新在线列表 contentArea.append(client.getUser().getName() + client.getUser().getIp() + "上线!\r\n"); } catch (IOException e) { e.printStackTrace(); } } } } // 为一个客户端服务的线程 class ClientThread extends Thread { private Socket socket; private BufferedReader reader; private PrintWriter writer; private User user; public BufferedReader getReader() { return reader; } public PrintWriter getWriter() { return writer; } public User getUser() { return user; } // 客户端线程的构造方法 public ClientThread(Socket socket) { try { this.socket = socket; reader = new BufferedReader(new InputStreamReader(socket .getInputStream())); writer = new PrintWriter(socket.getOutputStream()); // 接收客户端的基本用户信息 String inf = reader.readLine(); StringTokenizer st = new StringTokenizer(inf, "@"); user = new User(st.nextToken(), st.nextToken()); // 反馈连接成功信息 writer.println(user.getName() + user.getIp() + "与服务器连接成功!"); writer.flush(); // 反馈当前在线用户信息 if (clients.size() > 0) { String temp = ""; for (int i = clients.size() - 1; i >= 0; i--) { temp += (clients.get(i).getUser().getName() + "/" + clients .get(i).getUser().getIp()) + "@"; } writer.println("USERLIST@" + clients.size() + "@" + temp); writer.flush(); } // 向所有在线用户发送该用户上线命令 for (int i = clients.size() - 1; i >= 0; i--) { clients.get(i).getWriter().println( "ADD@" + user.getName() + user.getIp()); clients.get(i).getWriter().flush(); } } catch (IOException e) { e.printStackTrace(); } } @SuppressWarnings("deprecation") public void run() {// 不断接收客户端的消息,进行处理。 String message = null; while (true) { try { message = reader.readLine();// 接收客户端消息 if (message.equals("CLOSE"))// 下线命令 { contentArea.append(this.getUser().getName() + this.getUser().getIp() + "下线!\r\n"); // 断开连接释放资源 reader.close(); writer.close(); socket.close(); // 向所有在线用户发送该用户的下线命令 for (int i = clients.size() - 1; i >= 0; i--) { clients.get(i).getWriter().println( "DELETE@" + user.getName()); clients.get(i).getWriter().flush(); } listModel.removeElement(user.getName());// 更新在线列表 // 删除此条客户端服务线程 for (int i = clients.size() - 1; i >= 0; i--) { if (clients.get(i).getUser() == user) { ClientThread temp = clients.get(i); clients.remove(i);// 删除此用户的服务线程 temp.stop();// 停止这条服务线程 return; } } } else { dispatcherMessage(message);// 转发消息 } } catch (IOException e) { e.printStackTrace(); } } } // 转发消息 public void dispatcherMessage(String message) { StringTokenizer stringTokenizer = new StringTokenizer(message, "@"); String source = stringTokenizer.nextToken(); String owner = stringTokenizer.nextToken(); String content = stringTokenizer.nextToken(); message = source + "说:" + content; contentArea.append(message + "\r\n"); if (owner.equals("ALL")) {// 群发 for (int i = clients.size() - 1; i >= 0; i--) { clients.get(i).getWriter().println(message + "(多人发送)"); clients.get(i).getWriter().flush(); } } } } }


    Client(客户端):

    import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridLayout; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.border.TitledBorder; public class Client{ private JFrame frame; private JList userList; private JTextArea textArea; private JTextField textField; private JTextField txt_port; private JTextField txt_hostIp; private JTextField txt_name; private JButton btn_start; private JButton btn_stop; private JButton btn_send; private JPanel northPanel; private JPanel southPanel; private JScrollPane rightScroll; private JScrollPane leftScroll; private JSplitPane centerSplit; private DefaultListModel listModel; private boolean isConnected = false; private Socket socket; private PrintWriter writer; private BufferedReader reader; private MessageThread messageThread;// 负责接收消息的线程 private Map<String, User> onLineUsers = new HashMap<String, User>();// 所有在线用户 // 主方法,程序入口 public static void main(String[] args) { new Client(); } // 执行发送 public void send() { if (!isConnected) { JOptionPane.showMessageDialog(frame, "还没有连接服务器,无法发送消息!", "错误", JOptionPane.ERROR_MESSAGE); return; } String message = textField.getText().trim(); if (message == null || message.equals("")) { JOptionPane.showMessageDialog(frame, "消息不能为空!", "错误", JOptionPane.ERROR_MESSAGE); return; } sendMessage(frame.getTitle() + "@" + "ALL" + "@" + message); textField.setText(null); } // 构造方法 public Client() { textArea = new JTextArea(); textArea.setEditable(false); textArea.setForeground(Color.blue); textField = new JTextField(); txt_port = new JTextField("6666"); txt_hostIp = new JTextField("127.0.0.1"); txt_name = new JTextField("xiaoqiang"); btn_start = new JButton("连接"); btn_stop = new JButton("断开"); btn_send = new JButton("发送"); listModel = new DefaultListModel(); userList = new JList(listModel); northPanel = new JPanel(); northPanel.setLayout(new GridLayout(1, 7)); northPanel.add(new JLabel("端口")); northPanel.add(txt_port); northPanel.add(new JLabel("服务器IP")); northPanel.add(txt_hostIp); northPanel.add(new JLabel("姓名")); northPanel.add(txt_name); northPanel.add(btn_start); northPanel.add(btn_stop); northPanel.setBorder(new TitledBorder("连接信息")); rightScroll = new JScrollPane(textArea); rightScroll.setBorder(new TitledBorder("消息显示区")); leftScroll = new JScrollPane(userList); leftScroll.setBorder(new TitledBorder("在线用户")); southPanel = new JPanel(new BorderLayout()); southPanel.add(textField, "Center"); southPanel.add(btn_send, "East"); southPanel.setBorder(new TitledBorder("写消息")); centerSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftScroll, rightScroll); centerSplit.setDividerLocation(100); frame = new JFrame("客户机"); // 更改JFrame的图标: frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Client.class.getResource("qq.png"))); frame.setLayout(new BorderLayout()); frame.add(northPanel, "North"); frame.add(centerSplit, "Center"); frame.add(southPanel, "South"); frame.setSize(600, 400); int screen_width = Toolkit.getDefaultToolkit().getScreenSize().width; int screen_height = Toolkit.getDefaultToolkit().getScreenSize().height; frame.setLocation((screen_width - frame.getWidth()) / 2, (screen_height - frame.getHeight()) / 2); frame.setVisible(true); // 写消息的文本框中按回车键时事件 textField.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { send(); } }); // 单击发送按钮时事件 btn_send.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { send(); } }); // 单击连接按钮时事件 btn_start.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int port; if (isConnected) { JOptionPane.showMessageDialog(frame, "已处于连接上状态,不要重复连接!", "错误", JOptionPane.ERROR_MESSAGE); return; } try { try { port = Integer.parseInt(txt_port.getText().trim()); } catch (NumberFormatException e2) { throw new Exception("端口号不符合要求!端口为整数!"); } String hostIp = txt_hostIp.getText().trim(); String name = txt_name.getText().trim(); if (name.equals("") || hostIp.equals("")) { throw new Exception("姓名、服务器IP不能为空!"); } boolean flag = connectServer(port, hostIp, name); if (flag == false) { throw new Exception("与服务器连接失败!"); } frame.setTitle(name); JOptionPane.showMessageDialog(frame, "成功连接!"); } catch (Exception exc) { JOptionPane.showMessageDialog(frame, exc.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); } } }); // 单击断开按钮时事件 btn_stop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (!isConnected) { JOptionPane.showMessageDialog(frame, "已处于断开状态,不要重复断开!", "错误", JOptionPane.ERROR_MESSAGE); return; } try { boolean flag = closeConnection();// 断开连接 if (flag == false) { throw new Exception("断开连接发生异常!"); } JOptionPane.showMessageDialog(frame, "成功断开!"); } catch (Exception exc) { JOptionPane.showMessageDialog(frame, exc.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); } } }); // 关闭窗口时事件 frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { if (isConnected) { closeConnection();// 关闭连接 } System.exit(0);// 退出程序 } }); } /** * 连接服务器 * * @param port * @param hostIp * @param name */ public boolean connectServer(int port, String hostIp, String name) { // 连接服务器 try { socket = new Socket(hostIp, port);// 根据端口号和服务器ip建立连接 writer = new PrintWriter(socket.getOutputStream()); reader = new BufferedReader(new InputStreamReader(socket .getInputStream())); // 发送客户端用户基本信息(用户名和ip地址) sendMessage(name + "@" + socket.getLocalAddress().toString()); // 开启接收消息的线程 messageThread = new MessageThread(reader, textArea); messageThread.start(); isConnected = true;// 已经连接上了 return true; } catch (Exception e) { textArea.append("与端口号为:" + port + " IP地址为:" + hostIp + " 的服务器连接失败!" + "\r\n"); isConnected = false;// 未连接上 return false; } } /** * 发送消息 * * @param message */ public void sendMessage(String message) { writer.println(message); writer.flush(); } /** * 客户端主动关闭连接 */ @SuppressWarnings("deprecation") public synchronized boolean closeConnection() { try { sendMessage("CLOSE");// 发送断开连接命令给服务器 messageThread.stop();// 停止接受消息线程 // 释放资源 if (reader != null) { reader.close(); } if (writer != null) { writer.close(); } if (socket != null) { socket.close(); } isConnected = false; return true; } catch (IOException e1) { e1.printStackTrace(); isConnected = true; return false; } } // 不断接收消息的线程 class MessageThread extends Thread { private BufferedReader reader; private JTextArea textArea; // 接收消息线程的构造方法 public MessageThread(BufferedReader reader, JTextArea textArea) { this.reader = reader; this.textArea = textArea; } // 被动的关闭连接 public synchronized void closeCon() throws Exception { // 清空用户列表 listModel.removeAllElements(); // 被动的关闭连接释放资源 if (reader != null) { reader.close(); } if (writer != null) { writer.close(); } if (socket != null) { socket.close(); } isConnected = false;// 修改状态为断开 } public void run() { String message = ""; while (true) { try { message = reader.readLine(); StringTokenizer stringTokenizer = new StringTokenizer( message, "/@"); String command = stringTokenizer.nextToken();// 命令 if (command.equals("CLOSE"))// 服务器已关闭命令 { textArea.append("服务器已关闭!\r\n"); closeCon();// 被动的关闭连接 return;// 结束线程 } else if (command.equals("ADD")) {// 有用户上线更新在线列表 String username = ""; String userIp = ""; if ((username = stringTokenizer.nextToken()) != null && (userIp = stringTokenizer.nextToken()) != null) { User user = new User(username, userIp); onLineUsers.put(username, user); listModel.addElement(username); } } else if (command.equals("DELETE")) {// 有用户下线更新在线列表 String username = stringTokenizer.nextToken(); User user = (User) onLineUsers.get(username); onLineUsers.remove(user); listModel.removeElement(username); } else if (command.equals("USERLIST")) {// 加载在线用户列表 int size = Integer .parseInt(stringTokenizer.nextToken()); String username = null; String userIp = null; for (int i = 0; i < size; i++) { username = stringTokenizer.nextToken(); userIp = stringTokenizer.nextToken(); User user = new User(username, userIp); onLineUsers.put(username, user); listModel.addElement(username); } } else if (command.equals("MAX")) {// 人数已达上限 textArea.append(stringTokenizer.nextToken() + stringTokenizer.nextToken() + "\r\n"); closeCon();// 被动的关闭连接 JOptionPane.showMessageDialog(frame, "服务器缓冲区已满!", "错误", JOptionPane.ERROR_MESSAGE); return;// 结束线程 } else {// 普通消息 textArea.append(message + "\r\n"); } } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } } } }


    User.java

    //用户信息类 public class User{ private String name; private String ip; public User(String name, String ip) { this.name = name; this.ip = ip; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } }


    注: 在eclipse下运行无需另外添加jar文件,其中服务器端和客户端均需要一张图片做icon,大家可以随便找张图片补上。

    Java Socket 多人聊天室 swing 局域网

    展开全文
  • JAVA实现多对多聊天

    2013-12-27 21:25:13
    使用JAVA开发的网络聊天程序,带有UI界面以及客户端和服务器端的全部代码,适合于课程设计和JAVA的Socket编程学习,通信协议为IP。
  • 很不错的蓝牙通信demo实现发送和接受功能,就用了两个类就实现了,具体内容如下说下思路把 主要两个类主界面类和 蓝牙聊天服务类。 首先创建线程 实际上就是创建BluetoothChatService()(蓝牙聊天服务类) 这个时候把...

    很不错的蓝牙通信demo实现发送和接受功能,就用了两个类就实现了,具体内容如下

    说下思路把 主要有两个类主界面类和 蓝牙聊天服务类。 首先创建线程 实际上就是创建BluetoothChatService()(蓝牙聊天服务类) 这个时候把handler 传过去 这样就可以操作UI 界面了,在线程中不断轮询读取蓝牙消息,当主界面点击发送按钮时 调用BluetoothChatService的发送方法write 方法,这里的write 方法 使用了handler 发送消息,在主界面显示,另一个 客户端 不断读取蓝牙消息 类似的有个read 方法 同样显示到界面上去,这样就完成了通信了。

    import java.util.ArrayList;

    import java.util.Set;

    import android.app.Activity;

    import android.app.AlertDialog;

    import android.bluetooth.BluetoothAdapter;

    import android.bluetooth.BluetoothDevice;

    import android.content.BroadcastReceiver;

    import android.content.Context;

    import android.content.DialogInterface;

    import android.content.Intent;

    import android.content.IntentFilter;

    import android.os.Bundle;

    import android.os.Handler;

    import android.os.Message;

    import android.util.Log;

    import android.view.Menu;

    import android.view.MenuItem;

    import android.view.View;

    import android.view.View.OnClickListener;

    import android.view.Window;

    import android.widget.Button;

    import android.widget.EditText;

    import android.widget.TextView;

    import android.widget.Toast;

    public class BluetoothChat extends Activity {

    // Message types sent from the BluetoothChatService Handler

    public static final int MESSAGE_STATE_CHANGE = 1;

    public static final int MESSAGE_READ = 2;

    public static final int MESSAGE_WRITE = 3;

    public static final int MESSAGE_DEVICE_NAME = 4;

    public static final int MESSAGE_TOAST = 5;

    // Key names received from the BluetoothChatService Handler

    public static final String DEVICE_NAME = "device_name";

    public static final String TOAST = "toast";

    // Intent request codes

    private static final int REQUEST_CONNECT_DEVICE = 1;

    private static final int REQUEST_ENABLE_BT = 2;

    private TextView mTitle;

    private EditText text_chat;

    private EditText text_input;

    private Button but_On_Off;

    private Button but_search; // ------> 在菜单中可以搜索

    private Button but_create; // ------> 在菜单中设置"可被发现"

    private Button mSendButton;

    // 连接到的蓝牙设备的名称

    private String mConnectedDeviceName;

    // String buffer for outgoing messages

    private StringBuffer mOutStringBuffer;

    // Local Bluetooth adapter

    private BluetoothAdapter mBluetoothAdapter = null;

    // Member object for the chat services

    private BluetoothChatService mChatService = null;

    private ArrayList mPairedDevicesList = new ArrayList();

    private ArrayList mNewDevicesList = new ArrayList();

    private String[] strName;

    private String address;

    @Override

    public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);

    setContentView(R.layout.main);

    mTitle = (TextView) this.findViewById(R.id.text_title);

    text_chat = (EditText) this.findViewById(R.id.text_chat);

    text_input = (EditText) this.findViewById(R.id.text_input);

    but_On_Off = (Button) this.findViewById(R.id.but_off_on);

    but_search = (Button) this.findViewById(R.id.but_search_div);

    but_create = (Button) this.findViewById(R.id.but_cjlj);

    mSendButton = (Button) this.findViewById(R.id.but_fsxx);

    // 获得本地的蓝牙适配器

    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

    // 如果为null,说明没有蓝牙设备

    if (mBluetoothAdapter == null) {

    Toast.makeText(this, "没有蓝牙设备", Toast.LENGTH_LONG).show();

    finish();

    return;

    }

    if (mBluetoothAdapter.isEnabled()) {

    but_On_Off.setText("关闭蓝牙");

    } else {

    but_On_Off.setText("开启蓝牙");

    }

    but_On_Off.setOnClickListener(new OnClickListener() {

    @Override

    public void onClick(View v) {

    if (!mBluetoothAdapter.isEnabled()) {

    mBluetoothAdapter.enable();

    Toast.makeText(BluetoothChat.this, "蓝牙已开启",

    Toast.LENGTH_SHORT).show();

    but_On_Off.setText("关闭蓝牙");

    } else {

    mBluetoothAdapter.disable();

    Toast.makeText(BluetoothChat.this, "蓝牙已关闭",

    Toast.LENGTH_SHORT).show();

    but_On_Off.setText("开启蓝牙");

    }

    }

    });

    but_search.setOnClickListener(new OnClickListener() {

    @Override

    public void onClick(View v) {

    searchDevice();

    }

    });

    but_create.setOnClickListener(new OnClickListener() {

    @Override

    public void onClick(View v) {

    final EditText et = new EditText(BluetoothChat.this);

    et.setSingleLine();

    et.setText(mBluetoothAdapter.getName());

    new AlertDialog.Builder(BluetoothChat.this)

    .setTitle("请输入房间名:")

    .setView(et)

    .setPositiveButton("确定",

    new DialogInterface.OnClickListener() {

    @Override

    public void onClick(DialogInterface dialog,

    int which) {

    String name = et.getText().toString()

    .trim();

    if (name.equals("")) {

    Toast.makeText(BluetoothChat.this,

    "请输入房间名",

    Toast.LENGTH_SHORT).show();

    return;

    }

    // 设置房间名

    mBluetoothAdapter.setName(name);

    }

    })

    .setNegativeButton("取消",

    new DialogInterface.OnClickListener() {

    @Override

    public void onClick(DialogInterface dialog,

    int which) {

    }

    }).create().show();

    // 创建连接,也就是设备本地蓝牙设备可被其他用户的蓝牙搜到

    ensureDiscoverable();

    }

    });

    // 获得一个已经配对的蓝牙设备的set集合

    Set pairedDevices = mBluetoothAdapter

    .getBondedDevices();

    if (pairedDevices.size() > 0) {

    for (BluetoothDevice device : pairedDevices) {

    mPairedDevicesList.add("已配对:" + device.getName() + "\n"

    + device.getAddress());

    }

    } else {

    Toast.makeText(this, "没有已配对的设备", Toast.LENGTH_SHORT).show();

    }

    // 当发现一个新的蓝牙设备时注册广播

    IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);

    this.registerReceiver(mReceiver, filter);

    // 当搜索完毕后注册广播

    filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);

    this.registerReceiver(mReceiver, filter);

    }

    @Override

    public void onStart() {

    super.onStart();

    // If BT is not on, request that it be enabled.

    // setupChat() will then be called during onActivityResult

    if (!mBluetoothAdapter.isEnabled()) {

    Intent enableIntent = new Intent(

    BluetoothAdapter.ACTION_REQUEST_ENABLE);

    startActivityForResult(enableIntent, REQUEST_ENABLE_BT);

    // Otherwise, setup the chat session

    } else {

    if (mChatService == null)

    setupChat();

    }

    }

    @Override

    public synchronized void onResume() {

    super.onResume();

    // Performing this check in onResume() covers the case in which BT was

    // not enabled during onStart(), so we were paused to enable it...

    // onResume() will be called when ACTION_REQUEST_ENABLE activity

    // returns.

    if (mChatService != null) {

    // Only if the state is STATE_NONE, do we know that we haven't

    // started already

    if (mChatService.getState() == BluetoothChatService.STATE_NONE) {

    // Start the Bluetooth chat services

    mChatService.start();

    }

    }

    }

    private void setupChat() {

    mSendButton.setOnClickListener(new OnClickListener() {

    public void onClick(View v) {

    // Send a message using content of the edit text widget

    String message = text_input.getText().toString();

    sendMessage(message);

    }

    });

    // Initialize the BluetoothChatService to perform bluetooth connections

    mChatService = new BluetoothChatService(this, mHandler);

    // Initialize the buffer for outgoing messages

    mOutStringBuffer = new StringBuffer("");

    }

    @Override

    public void onDestroy() {

    super.onDestroy();

    // Stop the Bluetooth chat services

    if (mChatService != null)

    mChatService.stop();

    // Make sure we're not doing discovery anymore

    if (mBluetoothAdapter != null) {

    mBluetoothAdapter.cancelDiscovery();

    }

    // Unregister broadcast listeners

    this.unregisterReceiver(mReceiver);

    }

    /** 使本地的蓝牙设备可被发现 */

    private void ensureDiscoverable() {

    if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {

    Intent discoverableIntent = new Intent(

    BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);

    discoverableIntent.putExtra(

    BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);

    startActivity(discoverableIntent);

    }

    }

    /**

    * Sends a message.

    *

    * @param message

    * A string of text to send.

    */

    private void sendMessage(String message) {

    // Check that we're actually connected before trying anything

    if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) {

    Toast.makeText(this, "没有连接上", Toast.LENGTH_SHORT).show();

    return;

    }

    // Check that there's actually something to send

    if (message.length() > 0) {

    // Get the message bytes and tell the BluetoothChatService to write

    byte[] send = message.getBytes();

    mChatService.write(send);

    // Reset out string buffer to zero and clear the edit text field

    mOutStringBuffer.setLength(0);

    text_input.setText(mOutStringBuffer);

    }

    }

    // The Handler that gets information back from the BluetoothChatService

    private final Handler mHandler = new Handler() {

    @Override

    public void handleMessage(Message msg) {

    switch (msg.what) {

    case MESSAGE_STATE_CHANGE:

    switch (msg.arg1) {

    case BluetoothChatService.STATE_CONNECTED:

    mTitle.setText("已经连接");

    mTitle.append(mConnectedDeviceName);

    // mConversationArrayAdapter.clear();

    break;

    case BluetoothChatService.STATE_CONNECTING:

    mTitle.setText("正在连接中...");

    break;

    case BluetoothChatService.STATE_LISTEN:

    case BluetoothChatService.STATE_NONE:

    mTitle.setText("未连接上");

    break;

    }

    break;

    case MESSAGE_WRITE:

    byte[] writeBuf = (byte[]) msg.obj;

    // construct a string from the buffer

    String writeMessage = new String(writeBuf);

    // mConversationArrayAdapter.add("Me: " + writeMessage);

    text_chat.append("我:" + writeMessage + "\n");

    break;

    case MESSAGE_READ:

    byte[] readBuf = (byte[]) msg.obj;

    // construct a string from the valid bytes in the buffer

    String readMessage = new String(readBuf, 0, msg.arg1);

    // mConversationArrayAdapter.add(mConnectedDeviceName+": " +

    // readMessage);

    text_chat.append(mConnectedDeviceName + ":" + readMessage

    + "\n");

    break;

    case MESSAGE_DEVICE_NAME:

    // save the connected device's name

    mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);

    Toast.makeText(getApplicationContext(),

    "连接到 " + mConnectedDeviceName, Toast.LENGTH_SHORT)

    .show();

    break;

    case MESSAGE_TOAST:

    Toast.makeText(getApplicationContext(),

    msg.getData().getString(TOAST), Toast.LENGTH_SHORT)

    .show();

    break;

    }

    }

    };

    // 连接蓝牙设备

    private void linkDevice() {

    if (mBluetoothAdapter.isDiscovering()) {

    mBluetoothAdapter.cancelDiscovery();

    }

    int cou = mPairedDevicesList.size() + mNewDevicesList.size();

    if (cou == 0) {

    Toast.makeText(BluetoothChat.this, "没有搜索到可用的蓝牙设备",

    Toast.LENGTH_SHORT).show();

    return;

    }

    // 把已经配对的蓝牙设备和新发现的蓝牙设备的名称都放入数组中,以便在对话框列表中显示

    strName = new String[cou];

    for (int i = 0; i < mPairedDevicesList.size(); i++) {

    strName[i] = mPairedDevicesList.get(i);

    }

    for (int i = mPairedDevicesList.size(); i < strName.length; i++) {

    strName[i] = mNewDevicesList.get(i - mPairedDevicesList.size());

    }

    address = strName[0].substring(strName[0].length() - 17);

    new AlertDialog.Builder(BluetoothChat.this)

    .setTitle("搜索到的蓝牙设备:")

    .setSingleChoiceItems(strName, 0,

    new DialogInterface.OnClickListener() {

    @Override

    public void onClick(DialogInterface dialog,

    int which) {

    // 当用户点击选中的蓝牙设备时,取出选中的蓝牙设备的MAC地址

    address = strName[which].split("\\n")[1].trim();

    }

    })

    .setPositiveButton("连接", new DialogInterface.OnClickListener() {

    @Override

    public void onClick(DialogInterface dialog, int which) {

    if (address == null) {

    Toast.makeText(BluetoothChat.this, "请先连接外部蓝牙设备",

    Toast.LENGTH_SHORT).show();

    return;

    }

    Log.i("sxd", "address:" + address);

    // Get the BLuetoothDevice object

    BluetoothDevice device = mBluetoothAdapter

    .getRemoteDevice(address);

    // Attempt to connect to the device

    mChatService.connect(device);

    }

    })

    .setNegativeButton("取消", new DialogInterface.OnClickListener() {

    @Override

    public void onClick(DialogInterface dialog, int which) {

    }

    }).create().show();

    }

    // 搜索蓝牙设备蓝牙设备

    private void searchDevice() {

    mTitle.setText("正在努力搜索中...");

    setProgressBarIndeterminateVisibility(true);

    if (mBluetoothAdapter.isDiscovering()) {

    mBluetoothAdapter.cancelDiscovery();

    }

    mNewDevicesList.clear();

    mBluetoothAdapter.startDiscovery();

    }

    @Override

    public boolean onCreateOptionsMenu(Menu menu) {

    menu.add(0, 1, 0, "搜索设备");

    menu.add(0, 2, 0, "可被发现");

    return true;

    }

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {

    @Override

    public void onReceive(Context context, Intent intent) {

    String action = intent.getAction();

    // 当发现一个新的蓝牙设备时

    if (BluetoothDevice.ACTION_FOUND.equals(action)) {

    BluetoothDevice device = intent

    .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

    // If it's already paired, skip it, because it's been listed

    // already

    if (device.getBondState() != BluetoothDevice.BOND_BONDED) {

    String s = "未配对: " + device.getName() + "\n"

    + device.getAddress();

    if (!mNewDevicesList.contains(s))

    mNewDevicesList.add(s);

    }

    // When discovery is finished, change the Activity title

    } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED

    .equals(action)) {

    setProgressBarIndeterminateVisibility(false);

    if (mNewDevicesList.size() == 0) {

    Toast.makeText(BluetoothChat.this, "没有发现新设备",

    Toast.LENGTH_SHORT).show();

    }

    mTitle.setText("未连接");

    linkDevice();

    }

    }

    };

    @Override

    public boolean onOptionsItemSelected(MenuItem item) {

    switch (item.getItemId()) {

    case 1:

    searchDevice();

    return true;

    case 2:

    // Ensure this device is discoverable by others

    ensureDiscoverable();

    return true;

    }

    return false;

    }

    }

    package com.it2388.bluetooth;

    /*

    * Copyright (C) 2009 The Android Open Source Project

    *

    * Licensed under the Apache License, Version 2.0 (the "License");

    * you may not use this file except in compliance with the License.

    * You may obtain a copy of the License at

    *

    * http://www.apache.org/licenses/LICENSE-2.0

    *

    * Unless required by applicable law or agreed to in writing, software

    * distributed under the License is distributed on an "AS IS" BASIS,

    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

    * See the License for the specific language governing permissions and

    * limitations under the License.

    */

    import java.io.IOException;

    import java.io.InputStream;

    import java.io.OutputStream;

    import java.util.UUID;

    import android.bluetooth.BluetoothAdapter;

    import android.bluetooth.BluetoothDevice;

    import android.bluetooth.BluetoothServerSocket;

    import android.bluetooth.BluetoothSocket;

    import android.content.Context;

    import android.os.Bundle;

    import android.os.Handler;

    import android.os.Message;

    import android.util.Log;

    /**

    * This class does all the work for setting up and managing Bluetooth

    * connections with other devices. It has a thread that listens for incoming

    * connections, a thread for connecting with a device, and a thread for

    * performing data transmissions when connected.

    *

    * *这个类做所有的工作,建立和管理蓝牙 与其他设备的连接。它有一个线程监听 传入的连接,一个用于连接一个设备的线程,和一个 当连接时执行数据传输的线程。

    */

    public class BluetoothChatService {

    // Debugging

    private static final String TAG = "BluetoothChatService";

    private static final boolean D = true;

    // Name for the SDP record when creating server socket 对于SDP记录名称创建服务器套接字时

    private static final String NAME = "BluetoothChat";

    // Unique UUID for this application

    private static final UUID MY_UUID = UUID

    .fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");

    // Member fields

    private final BluetoothAdapter mAdapter;

    private final Handler mHandler;

    private AcceptThread mAcceptThread;

    private ConnectThread mConnectThread;

    private ConnectedThread mConnectedThread;

    private int mState;

    // Constants that indicate the current connection state

    public static final int STATE_NONE = 0; // we're doing nothing 我们什么都不做

    public static final int STATE_LISTEN = 1; // now listening for incoming 现在监听

    // connections

    public static final int STATE_CONNECTING = 2; // now initiating an outgoing

    // 启动一个外向

    // connection

    public static final int STATE_CONNECTED = 3; // now connected to a remote

    // 连接到一个远程

    // device

    /**

    * Constructor. Prepares a new BluetoothChat session.

    *

    * @param context

    * The UI Activity Context

    * @param handler

    * A Handler to send messages back to the UI Activity

    */

    public BluetoothChatService(Context context, Handler handler) {

    mAdapter = BluetoothAdapter.getDefaultAdapter();

    mState = STATE_NONE;

    mHandler = handler;

    }

    /**

    * Set the current state of the chat connection

    *

    * @param state

    * An integer defining the current connection state

    */

    private synchronized void setState(int state) {

    if (D)

    Log.d(TAG, "setState() " + mState + " -> " + state);

    mState = state;

    // Give the new state to the Handler so the UI Activity can update

    mHandler.obtainMessage(BluetoothChat.MESSAGE_STATE_CHANGE, state, -1)

    .sendToTarget();

    }

    /**

    * Return the current connection state.

    */

    public synchronized int getState() {

    return mState;

    }

    /**

    * Start the chat service. Specifically start AcceptThread to begin a

    * session in listening (server) mode. Called by the Activity onResume()

    */

    public synchronized void start() {

    if (D)

    Log.d(TAG, "start");

    // Cancel any thread attempting to make a connection

    if (mConnectThread != null) {

    mConnectThread.cancel();

    mConnectThread = null;

    }

    // Cancel any thread currently running a connection

    if (mConnectedThread != null) {

    mConnectedThread.cancel();

    mConnectedThread = null;

    }

    // Start the thread to listen on a BluetoothServerSocket

    if (mAcceptThread == null) {

    mAcceptThread = new AcceptThread();

    mAcceptThread.start();

    }

    setState(STATE_LISTEN);

    }

    /**

    * Start the ConnectThread to initiate a connection to a remote device.

    *

    * @param device

    * The BluetoothDevice to connect

    */

    public synchronized void connect(BluetoothDevice device) {

    if (D)

    Log.d(TAG, "connect to: " + device);

    // Cancel any thread attempting to make a connection

    if (mState == STATE_CONNECTING) {

    if (mConnectThread != null) {

    mConnectThread.cancel();

    mConnectThread = null;

    }

    }

    // Cancel any thread currently running a connection

    if (mConnectedThread != null) {

    mConnectedThread.cancel();

    mConnectedThread = null;

    }

    // Start the thread to connect with the given device

    mConnectThread = new ConnectThread(device);

    mConnectThread.start();

    setState(STATE_CONNECTING);

    }

    /**

    * Start the ConnectedThread to begin managing a Bluetooth connection

    *

    * @param socket

    * The BluetoothSocket on which the connection was made

    * @param device

    * The BluetoothDevice that has been connected

    */

    public synchronized void connected(BluetoothSocket socket,

    BluetoothDevice device) {

    if (D)

    Log.d(TAG, "connected");

    // Cancel the thread that completed the connection

    if (mConnectThread != null) {

    mConnectThread.cancel();

    mConnectThread = null;

    }

    // Cancel any thread currently running a connection

    if (mConnectedThread != null) {

    mConnectedThread.cancel();

    mConnectedThread = null;

    }

    // Cancel the accept thread because we only want to connect to one

    // device

    if (mAcceptThread != null) {

    mAcceptThread.cancel();

    mAcceptThread = null;

    }

    // Start the thread to manage the connection and perform transmissions

    mConnectedThread = new ConnectedThread(socket);

    mConnectedThread.start();

    // Send the name of the connected device back to the UI Activity

    Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_DEVICE_NAME);

    Bundle bundle = new Bundle();

    bundle.putString(BluetoothChat.DEVICE_NAME, device.getName());

    msg.setData(bundle);

    mHandler.sendMessage(msg);

    setState(STATE_CONNECTED);

    }

    /**

    * Stop all threads

    */

    public synchronized void stop() {

    if (D)

    Log.d(TAG, "stop");

    if (mConnectThread != null) {

    mConnectThread.cancel();

    mConnectThread = null;

    }

    if (mConnectedThread != null) {

    mConnectedThread.cancel();

    mConnectedThread = null;

    }

    if (mAcceptThread != null) {

    mAcceptThread.cancel();

    mAcceptThread = null;

    }

    setState(STATE_NONE);

    }

    /**

    * Write to the ConnectedThread in an unsynchronized manner

    *

    * @param out

    * The bytes to write

    * @see ConnectedThread#write(byte[])

    */

    public void write(byte[] out) {

    // Create temporary object

    ConnectedThread r;

    // Synchronize a copy of the ConnectedThread

    synchronized (this) {

    if (mState != STATE_CONNECTED)

    return;

    r = mConnectedThread;

    }

    // Perform the write unsynchronized

    r.write(out);

    }

    /**

    * Indicate that the connection attempt failed and notify the UI Activity.

    */

    private void connectionFailed() {

    setState(STATE_LISTEN);

    // Send a failure message back to the Activity

    Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);

    Bundle bundle = new Bundle();

    bundle.putString(BluetoothChat.TOAST, "不能连接到设备");

    msg.setData(bundle);

    mHandler.sendMessage(msg);

    }

    /**

    * Indicate that the connection was lost and notify the UI Activity.

    */

    private void connectionLost() {

    setState(STATE_LISTEN);

    // Send a failure message back to the Activity

    Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);

    Bundle bundle = new Bundle();

    bundle.putString(BluetoothChat.TOAST, "设备连接中断");

    msg.setData(bundle);

    mHandler.sendMessage(msg);

    }

    /**

    * This thread runs while listening for incoming connections. It behaves

    * like a server-side client. It runs until a connection is accepted (or

    * until cancelled).

    */

    private class AcceptThread extends Thread {

    // The local server socket

    private final BluetoothServerSocket mmServerSocket;

    public AcceptThread() {

    BluetoothServerSocket tmp = null;

    // Create a new listening server socket

    try {

    tmp = mAdapter

    .listenUsingRfcommWithServiceRecord(NAME, MY_UUID);

    } catch (IOException e) {

    Log.e("sxd", " ======== ufcomm exception =======", e);

    }

    mmServerSocket = tmp;

    }

    public void run() {

    if (D)

    Log.d(TAG, "BEGIN mAcceptThread" + this);

    setName("AcceptThread");

    BluetoothSocket socket = null;

    // Listen to the server socket if we're not connected

    while (mState != STATE_CONNECTED) {

    try {

    // This is a blocking call and will only return on a

    // successful connection or an exception

    socket = mmServerSocket.accept();

    } catch (IOException e) {

    Log.e("sxd", "---> accept socket failed

    break;

    }

    // If a connection was accepted

    if (socket != null) {

    synchronized (BluetoothChatService.this) {

    switch (mState) {

    case STATE_LISTEN:

    case STATE_CONNECTING:

    // Situation normal. Start the connected thread.

    connected(socket, socket.getRemoteDevice());

    break;

    case STATE_NONE:

    case STATE_CONNECTED:

    // Either not ready or already connected. Terminate

    // new socket.

    try {

    socket.close();

    } catch (IOException e) {

    Log.e(TAG, "Could not close unwanted socket", e);

    }

    break;

    }

    }

    }

    }

    if (D)

    Log.i(TAG, "END mAcceptThread");

    }

    public void cancel() {

    if (D)

    Log.d(TAG, "cancel " + this);

    try {

    mmServerSocket.close();

    } catch (IOException e) {

    Log.e(TAG, "close() of server failed", e);

    }

    }

    }

    /**

    * This thread runs while attempting to make an outgoing connection with a

    * device. It runs straight through; the connection either succeeds or

    * fails.

    */

    private class ConnectThread extends Thread {

    private final BluetoothSocket mmSocket;

    private final BluetoothDevice mmDevice;

    public ConnectThread(BluetoothDevice device) {

    mmDevice = device;

    BluetoothSocket tmp = null;

    // Get a BluetoothSocket for a connection with the

    // given BluetoothDevice

    try {

    tmp = device.createRfcommSocketToServiceRecord(MY_UUID);

    } catch (IOException e) {

    Log.e(TAG, "create() failed", e);

    }

    mmSocket = tmp;

    }

    public void run() {

    Log.i(TAG, "BEGIN mConnectThread");

    setName("ConnectThread");

    // Always cancel discovery because it will slow down a connection

    mAdapter.cancelDiscovery();

    // Make a connection to the BluetoothSocket

    try {

    // This is a blocking call and will only return on a

    // successful connection or an exception

    mmSocket.connect();

    } catch (IOException e) {

    Log.e("sxd", "链接发生了异常", e);

    connectionFailed();

    // Close the socket

    try {

    mmSocket.close();

    } catch (IOException e2) {

    Log.e(TAG,

    "unable to close() socket during connection failure",

    e2);

    }

    // Start the service over to restart listening mode

    BluetoothChatService.this.start();

    return;

    }

    // Reset the ConnectThread because we're done

    synchronized (BluetoothChatService.this) {

    mConnectThread = null;

    }

    // Start the connected thread

    connected(mmSocket, mmDevice);

    }

    public void cancel() {

    try {

    mmSocket.close();

    } catch (IOException e) {

    Log.e(TAG, "close() of connect socket failed", e);

    }

    }

    }

    /**

    * This thread runs during a connection with a remote device. It handles all

    * incoming and outgoing transmissions.

    *

    * 和已经建立连接的设置进行数据的传输

    */

    private class ConnectedThread extends Thread {

    private final BluetoothSocket mmSocket;

    private final InputStream mmInStream;

    private final OutputStream mmOutStream;

    public ConnectedThread(BluetoothSocket socket) {

    Log.d(TAG, "create ConnectedThread");

    mmSocket = socket;

    InputStream tmpIn = null;

    OutputStream tmpOut = null;

    // Get the BluetoothSocket input and output streams

    try {

    tmpIn = socket.getInputStream();

    tmpOut = socket.getOutputStream();

    } catch (IOException e) {

    Log.e(TAG, "temp sockets not created", e);

    }

    mmInStream = tmpIn;

    mmOutStream = tmpOut;

    }

    public void run() {

    Log.i(TAG, "BEGIN mConnectedThread");

    byte[] buffer = new byte[1024];

    int bytes;

    // Keep listening to the InputStream while connected

    while (true) {

    try {

    // Read from the InputStream

    bytes = mmInStream.read(buffer);

    // Send the obtained bytes to the UI Activity

    mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes,

    -1, buffer).sendToTarget();

    } catch (IOException e) {

    Log.e(TAG, "disconnected", e);

    connectionLost();

    break;

    }

    }

    }

    /**

    * Write to the connected OutStream.

    *

    * @param buffer

    * The bytes to write

    */

    public void write(byte[] buffer) {

    try {

    mmOutStream.write(buffer);

    // Share the sent message back to the UI Activity

    mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1,

    buffer).sendToTarget();

    } catch (IOException e) {

    Log.e(TAG, "Exception during write", e);

    }

    }

    public void cancel() {

    try {

    mmSocket.close();

    } catch (IOException e) {

    Log.e(TAG, "close() of connect socket failed", e);

    }

    }

    }

    }

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

    展开全文
  • 本来这次作业我是想搞个图形界面的,然而现实情况是我把题意理解错了,于是乎失去了最初的兴致,还是把程序变成了功能正确但是“UI”不友好的console了,但是不管怎么样,前期的图形界面的开发还是很收获的,毕竟...

    本来这次作业我是想搞个图形界面的,然而现实情况是我把题意理解错了,于是乎失去了最初的兴致,还是把程序变成了功能正确但是“UI”不友好的console了,但是不管怎么样,前期的图形界面的开发还是很有收获的,毕竟讲真,想要把Java搞得有形有色的也是很不容易的,借助可视化的插件windowsBuilder,这个过程还是既exciting 又tiring的。

    好吧 ,然而图形界面已经成为了历史,现在来说说这个功能正确的console 吧

    我也是刚知道的Eclipse里面是可以跑好多个程序的,只要你的一个.java文件中有public static void main,他就能给你一个窗口,让你跑起来。只不过这些窗口堆叠在一起,需要自行选择不同的窗口进行IO操作。

    总的思路是采用c/s的方式,client借助socket完成向server的发送和接受两个工作,当然了,为了体现出真实情况下的双工的特点,发送和接受是需要开两个线程的,也就是说,一个用户需要自己管理两个线程。server则相对来讲比较复杂,因为这里面涉及到了调度,server需要有发送消息给在线client的线程(这个线程要做的事情就是只要有消息就要把消息发到所有的用户的窗口),以及接受client发来的消息的线程(这个线程要做的事情就是将接收到的消息全部交给发送消息的线程,于是这两个线程之间的通信问题也是实现上的一个关键~),为了使得所有的用户消息是同步的,server需要管理一个用户线程的列表,用以实现用户的行为的控制,于是乎这就要求只要有用户请求连接服务器,服务器就要为用户新建一个线程,那么client 和server 之间靠什么来进行联系呢,那就是我们的socket了。

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 importjava.io.BufferedReader;2 importjava.io.InputStreamReader;3 importjava.io.PrintWriter;4 importjava.net.Socket;5

    6 public class Client extendsThread{7

    8 private static String serverIp = "127.0.0.1";9 private static int serverPort = 8001;10

    11 private Socket clientsSocket; //plays key role

    12 private PrintWriter pw; //for send data

    13 private BufferedReader br; //for receive data

    14

    15 publicClient(){16 try{17 clientsSocket = newSocket(serverIp, serverPort);18 pw = new PrintWriter(clientsSocket.getOutputStream(),true);19 br = new BufferedReader(newInputStreamReader(clientsSocket.getInputStream()));20 newreadServer();21

    22 while(true){23 br = new BufferedReader(newInputStreamReader(System.in));24 String input =br.readLine();25 //System.out.println("this is the input :" + input);

    26 pw.println(input);27 }28 } catch(Exception e) {29 e.printStackTrace();30 }31 }32

    33 class readServer extendsThread{34 privateBufferedReader reader;35

    36 publicreadServer(){37 try{38 reader = new BufferedReader(newInputStreamReader(clientsSocket.getInputStream()));39 start();40 } catch(Exception e) {41 e.printStackTrace();42 }43 }44

    45 public voidrun(){46 try{47 while(true){48 String content =reader.readLine();49 if(content.equals("bye Client")){50 break;51 }52 else{53 System.out.println(content);54 }55 }56 } catch(Exception e) {57 e.printStackTrace();58 }59 }60 }61

    62 public static void main(String[] args) throwsException{63 newClient();64 }65 }

    Client codes

    8f900a89c6347c561fdf2122f13be562.png

    961ddebeb323a10fe0623af514929fc1.png

    1 importjava.io.BufferedReader;2 importjava.io.InputStreamReader;3 importjava.io.PrintWriter;4 importjava.net.ServerSocket;5 importjava.net.Socket;6 importjava.util.ArrayList;7 importjava.util.LinkedList;8

    9

    10 public classServer {11

    12 private static int port = 8001;13 private static boolean prit = false;14 private static ArrayList userList = new ArrayList<>();15 private static LinkedList messageList = new LinkedList<>();16 private static ArrayList threadsList = new ArrayList<>();17

    18 privateServerSocket serverSocket;19

    20 publicServer(){21 try{22 serverSocket = newServerSocket(port);23 newPrintClient();24

    25 while(true){26 Socket socket =serverSocket.accept();27 newServerThread(socket);28 }29 } catch(Exception e) {30 e.printStackTrace();31 }32 }33

    34 class PrintClient extendsThread{35 publicPrintClient(){36 start();37 }38

    39 public voidrun(){40 while(true){41 try{42 Thread.sleep(10);43 } catch(Exception e) {44 e.printStackTrace();45 }46 if (prit == true){47 String msg =messageList.getFirst();48 //System.out.println("prepare to sent to Clent");

    49 for(ServerThread sThread : threadsList){50 sThread.sendMessage(msg);51 }52 synchronized(messageList) {53 messageList.removeFirst();54 }55 prit = messageList.size() > 0 ? true : false;56 }57 }58 }59 }60

    61 class ServerThread extendsThread{62 privateSocket client;63 privatePrintWriter pw;64 privateBufferedReader br;65 privateString user;66

    67 publicServerThread(Socket socket){68 try{69 client =socket;70 pw = new PrintWriter(client.getOutputStream(),true);71 br = new BufferedReader(newInputStreamReader(client.getInputStream()));72 br.readLine();73

    74 pw.println("connect success input your name ~");75 start();76 } catch(Exception e) {77 e.printStackTrace();78 }79 }80

    81 public voidpushMessage(String msg){82 synchronized(messageList) {83 messageList.add(msg);84 }85 prit = true;86 }87

    88 public voidsendMessage(String msg){89 pw.println(msg);90 }91

    92

    93 public voidrun(){94 try{95 int first = 1;96 String msg =br.readLine();97 while(!msg.equals("bye")){98 if (first == 1){99 user =msg;100 userList.add(user);101 threadsList.add(this);102 pw.println(user + " hello you can chat now ");103 this.pushMessage("Client " + "join in ~");104 //System.out.println("the prit is " + prit);

    105 first--;106 }107 else{108 this.pushMessage("Client "+ "say :" +msg);109 //System.out.println("the prit is " + prit + " " + messageList.size());

    110 }111 msg =br.readLine();112 //System.out.println(msg);

    113 }114 pw.println("bye Client");115 } catch(Exception e) {116 e.printStackTrace();117 }finally{118 try{119 client.close();120 } catch(Exception e2) {121 e2.printStackTrace();122 }123 threadsList.remove(this);124 userList.remove(user);125 pushMessage(user + " leave ~");126 }127 }128 }129

    130 public static void main(String[] srgs) throwsException{131 newServer();132 }133 }

    Server codes

    在这里注意一个问题,server端的PrintClient线程在run的过程中一定要有sleep的过程,否则会因为一开始的prit设置为false而在一直在while(true)的死循环中得不到更新,也就是说线程的同步出了错,从而会出现客户端无法收到信息的错误。还有一点要注意的是作为良好的编程习惯需要注意多个线程公用的变量要注意互斥操作,防止出现多线程中的“magic bugs”。同步与互斥,真是线程进程调度中的老大难啊,每次编程都要小心处理这两个问题,尽量避免不必要的错误。

    展开全文
  • 我的“java即时聊天系统”是完全可以运行的,很多人根本就是不会设置eclipse或者 myeclipse,甚至还有些人都分不清java版本的区别,就在那喊“代码错误”、“程序 无法运行” ,让我很无语。在此我不是想说我...
  • Java最后一道作业题竟然是写个多线程聊天...该网络聊天程序大致分为三个主要部分:客户端、服务器端和用户图形界面。各个部分的初步设计思想、流程及存储结构如下: 1. 程序整体框架:主程序监听一端口,等待客户接入

    Java最后一道作业题竟然是写个多线程聊天工具。以前在一次实训中做过linux版的。当时就有人说Java版的实现比较简单,如今看来确实有理。尤其是在做UI上,不用像gtk那么麻烦。先将我搜到的资源与大家共享(亲测可用):

    该网络聊天程序大致分为三个主要部分:客户端、服务器端和用户图形界面。各个部分的初步设计思想、流程及存储结构如下:

    1.    程序整体框架:主程序监听一端口,等待客户接入;同时构造一个线程类,准备接管会话。当一个Socket会话产生后,将这个会话交给线程处理,然后主程序继续监听。

     

    打开Socket

     

    命 名

     

    监听端口

     

    建立连接

     

    收发消息

     

    关闭连接

     

    打开Socket

     

     

    连接服务器

     

    收发消息

     

    关闭连接

     

    服务器端程序

     

    客户端程序

     

    2.    客户端(Client)

    客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。客户端不需要指定打开的端口,通常临时的、动态的分配一个端口。

    3.    服务器端(Server)

    服务器端,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。

    4.    用户图形界面

    用户图形界面方便程序与用户的交互,多个用户参加,完成会话功能,具体的设计要方便用户的使用,直观清晰,简洁明了,友好美观。

    5.    存储结构

    下面列出主要存储结构或变量:

    存储结构、变量、对象

    类型

    说明

    post

    InetAddress

    标识IP地址

    Port

    int

    标识端口

    Server [ ]

    ServerThread

    服务器端连接数

    Client [ ]

    Socket

    客户端连接数

    Client(String ip,int p,Face chat)

    public

    Client类成员函数

    Public void run()

    Void

    Client、Server类成员函数

    Server(int port,Face chat)

    public

    Server类成员函数

    Face()

    Public

    Face类成员函数

    1.服务器端

    服务器端主要是使用ServerSocket类,相当于服务器Socket,用来监听试图进入的连接,当新的连接建立后,该类为他们实例化一个Socket对象,同时得到输入输出流,调用相应方法完成会话。

    具体代码如下:

    package nupt.java.socket;
    import java.awt.*;
    import java.net.*;
    import java.io.*;
    public class Server extends Thread {
    ServerSocket skt;   // ServerSocket类监听进入的连接,为每个新的连接产生一个Socket对象      
        Socket Client[ ]=new Socket[10];
        Socket Client1=null;
        int i = 0;
        TextArea in;
        int port,k=0,l=0;
        PrintStream theOutputStream;
        Face chat;
        public Server(int port, Face chat) {
            try {
                this.port = port;
                skt = new ServerSocket(port);
                this.chat = chat;
            } catch (IOException e) {
                chat.ta.append(e.toString());
            }
        }
        public void run() {
            chat.ta.append("等待连线......");
            while (true) {
                try {
                Client[k] = skt.accept();
                                                  //当有客户端连接时就新建一个子线程
                if (i < 2) {
                  ServerThread server[] = new ServerThread[10];
                  server[k]= new ServerThread(Client[k], this.chat, i);
                     l=server.length;
                     server[k].start();
                    chat.ta.append(“客户端“+ Client[k].getInetAddress() + "已连线\n");
                   
                    //for(int j=0;j<server.length;j++)
                    theOutputStream = new PrintStream(server[k].getClient().getOutputStream());
                    i = server[k].getI();
                    k++;
                } else {
                    //theOutputStream = new PrintStream(null);
                }
            } catch (SocketException e) {
                } catch (IOException e) {
                    chat.ta.append(e.toString());
                }
            }
        }
        public void dataout(String data) {
            //for(int j=0;j<l;j++)
            theOutputStream.println(data);
        }
    }
    class ServerThread extends Thread {
        ServerSocket skt;
        Socket Client;
        TextArea in;
        int port,i;
        BufferedReader theInputStream;
        PrintStream theOutputStream;
        String readin;
        Face chat;
    //服务端子线程
        public ServerThread(Socket s, Face chat, int i) {
            this.i = ++i;
            Client = s;
            this.chat = chat;
        }
        public int getI() {
            return this.i;
        }
        public Socket getClient() {
            return this.Client;
        }
        public void run() {
              try {
                theInputStream = new BufferedReader(new InputStreamReader(Client
                        .getInputStream()));
                theOutputStream = new PrintStream(Client.getOutputStream());
                while (true) {
                    readin = theInputStream.readLine();
                    chat.ta.append(readin + "\n");
                }
            } catch (SocketException e) {
                chat.ta.append("连线中断!\n");
                               // 设置组件可用性
                chat.clientBtn.setEnabled(true);
                chat.serverBtn.setEnabled(true);
                chat.tfaddress.setEnabled(true);
                chat.tfport.setEnabled(true);
                try {
                    i - -;
                    skt.close();
                    Client.close();
                } catch (IOException err) {
                    chat.ta.append(err.toString());
                }
            } catch (IOException e) {
                chat.ta.append(e.toString());
            }
        }
        public void dataout(String data) {
            theOutputStream.println(data);
        }
    }
    

    2.客户端

    客户端主要是使用Socket类,该类是JAVA实现网络编程重要的基础类,实现程序间双向的面向连接的通信。调用public Socket(String host,int port)方法设定IP和端口。建好连接后,用户通过得到Socket的输入输出流对象后,利用流的方法实现数据的传输。调用public InputStream getInputStream()和public OutputStream getOutputStream()方法,分别得到Socket对象的输入输出流;

    具体实现代码如下:

    package nupt.java.socket;
    import java.net.*;
    import java.io.*;
    import javax.swing.Timer;
    public class Client extends Thread {
          Socket skt;                                  // 用于客户端的连接
        InetAddress host;                        // 主机地址
        int port;                                     // 端口号
        BufferedReader theInputStream;
        PrintStream theOutputStream;
        String readin;
        Face chat;
        public Client(String ip, int p, Face chat) {
            try {
                host = InetAddress.getByName(ip);            // 获取IP地址
                port = p;                                                  // 获取端口号
                this.chat = chat;
            } catch (IOException e) {
                chat.ta.append(e.toString());
            }
        }
        public void run() {
            try {
                chat.ta.append("准备连线,稍后!");
                skt = new Socket(host, port);                     // 新建Socket对象
                chat.ta.append("成功\n");                   // 缓冲区末尾添加字符串
                theInputStream = new BufferedReader(new InputStreamReader(skt.getInputStream()));
                theOutputStream = new PrintStream(skt.getOutputStream());
                while (true) {
                    readin = theInputStream.readLine();
                    chat.ta.append(readin + "\n");
                }
            } catch (SocketException e) {
                chat.ta.append("未连上!\n");
                chat.clientBtn.setEnabled(true);
                chat.serverBtn.setEnabled(true);
                chat.tfaddress.setEnabled(true);
                chat.tfport.setEnabled(true);
                try {
                    skt.close();
                } catch (IOException err) {
                    chat.ta.append(err.toString());
                }
            } catch (IOException e) {
                chat.ta.append(e.toString());
            }
        }
        public void dataout(String data) {
            theOutputStream.println(data);
        }
    }
    

    3.用户图形界面

    该部分主要是完成界面的初始化,合理布局组件,方便用户交互。主要是JAVA按钮,文本域,标签,布局管理器的使用。主要处理了键盘Enter消息接受,下面是实现代码:

    package nupt.java.socket;
    import java.awt.*;
    import java.awt.event.*;
    public class Face extends Frame {
         
          private static final long serialVersionUID = 1L;
        Button clientBtn, serverBtn;
        TextArea ta;
        TextField tfaddress, tfport, tftype;
        Label lbl1,lbl2,lbl3;
        int port;
        Client client;
        Server server;
        boolean iamserver;
        static Face frm;
        public Face() {
              // 实例化组件
            clientBtn = new Button("客户端");
            serverBtn = new Button("服务器");
            ta = new TextArea("", 10, 50, TextArea.SCROLLBARS_BOTH);
            lbl1 = new Label("IP地址:");
            tfaddress = new TextField("192.168.1.104", 10);
            lbl2 = new Label("端口:");
            tfport = new TextField("8080");
            lbl3 = new Label("发送内容:");
            tftype = new TextField(40);
            tftype.addKeyListener(new TFListener());
            ta.setEditable(false);
            //向容器中加入以上组件
            setLayout(new   FlowLayout());
            add(lbl1);
            add(tfaddress);
            add(lbl2);
            add(tfport);
            add(clientBtn);
            add(serverBtn);
            add(ta);
            add(lbl3);
            add(tftype);
            //设置格式
            setLocation(400, 250);                //窗口显示再屏幕的位置坐标
            setSize(400, 300);                      //设置窗体大小
            setTitle("基于Socket和多线程编程的聊天程序");
            this.setVisible(true);                   //设置窗体可见
            //事件响应
            clientBtn.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    port = Integer.parseInt(tfport.getText());
                    client = new Client(tfaddress.getText(), port, frm);
                    client.start();
                    tfaddress.setEnabled(false);
                    tfport.setEnabled(false);
                    serverBtn.setEnabled(false);
                    clientBtn.setEnabled(false);
                }
            });
            serverBtn.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    port = Integer.parseInt(tfport.getText());
                    server = new Server(port, frm);
                    server.start();
                    iamserver = true;
                    tfaddress.setText("成为服务器");
                    tfaddress.setEnabled(false);
                    tfport.setEnabled(false);
                    serverBtn.setEnabled(false);
                    clientBtn.setEnabled(false);
                }
            });
            addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    System.exit(0);
                }
            });
        }
          public static void main(String[] args) {        //主方法
                 // TODO Auto-generated method stub
                
                 frm = new Face();
          }
          private class TFListener implements KeyListener {
            public void keyPressed(KeyEvent e) {
              if (e.getKeyCode() == KeyEvent.VK_ENTER) { //按Enter输出显示聊天内容
                    ta.append(">" + tftype.getText() + "\n");
                    if (iamserver)
                        server.dataout(tftype.getText());
                    else
                        client.dataout(tftype.getText());
                    tftype.setText("");
                }
            }
            public void keyTyped(KeyEvent e) {
            }
            public void keyReleased(KeyEvent e) {
            }
        }
    }
    

    如有不对的地方或者更好的方法,欢迎大家批评指正,交流学习。

    展开全文
  • 本来这次作业我是想搞个图形界面的,然而现实情况是我把题意理解错了,于是乎失去了最初的兴致,还是把程序变成了功能正确但是“UI”不友好的console了,但是不管怎么样,前期的图形界面的开发还是很收获的,毕竟...
  • Java最后一道作业题竟然是写个多线程聊天...该网络聊天程序大致分为三个主要部分:客户端、服务器端和用户图形界面。各个部分的初步设计思想、流程及存储结构如下: 1. 程序整体框架:主程序监听一端口,等待客户接入
  • 代码这是一个带有UI界面JAVA网络聊天程序,使用Socket连接完成通信。JAVA服务端程序import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.PrintWriter;import java...
  • JAVA上百实例源码以及开源项目

    千次下载 热门讨论 2016-01-03 17:37:40
    摘要:JAVA源码,媒体网络,山寨QQ,Java聊天程序  Java编写的山寨QQ,多人聊天+用户在线,程序分服务端和客户端,典型C/S结构,  当用户发送第一次请求的时候,验证用户登录,创建一个该qq号和服务器端保持通讯连接...
  • Java编写的山寨QQ,多人聊天+用户在线 21个目标文件 摘要:JAVA源码,媒体网络,山寨QQ,Java聊天程序 Java编写的山寨QQ,多人聊天+用户在线,程序分服务端和客户端,典型C/S结构, 当用户发送第一次请求的时候,验证...
  • 虽然功能也不是很齐全,比如暂不支持注册、记录玩家信息及声音的功能,而且UI界面缺陷,但稍加改造即可完善。 欢迎大家的批评意见及交流! 文件清单: build.bat 编译所有源代码的批处理文件 TicTacToe_Server....
  • O2OA : Java企业信息化系统,开源OA openSource OA Platform O2OA是基于J2EE架构,集成移动办公、智能办公,支持私有化部署,自适应负载能力的,能够很大程度上节约企业软件开发成本的基于AGPL协议开放源代码的企业...

空空如也

空空如也

1 2 3
收藏数 60
精华内容 24
关键字:

java聊天程序有ui界面

java 订阅