java学习之即时通信项目实战
项目总结:这次项目主要是根据视频来的,结果跟到一半感觉跟不上,慢慢自己有了自己的想法,决定自己先不看学习视频,自己先试着写。
总结写前面,算是写的第一个项目吧。项目中遇到几点问题,首先Scoket对象创建后,服务器端和客户端不能同时创建输入流,否者会引起堵塞。
然后,读入流应该重新创建个线程做等待写入服务,因为读入流会引起当前线程进入阻塞状态。
还有一个用户线程对应一个服务线程,不是多个用户线程对应一个服务线程。
对对象的操作应该由那个对象本身提供操作方法,比如操作UI界面的变化应该由界面本身提拱。
最后最重要的是写代码之前应该先画个流程图,写代码时参数乱传的,哪里需要就调参数过来。导致思路不清.
首先是需求分析:
本次项目是模拟及时通信中最基本的功能,类似QQ的应用.
项目分为:
(1)服务器端:
服务器端主要负责用户管理,消息转发功能
(2) 客户端:
客户端主要负责用户间的消息发送
详细设计
服务器端
1、登录服务器后,进行客户端的监听,如有客户端连接,启动用户服务线程,与客户端进行交互。
2、如客户端向所有人发送消息,服务器将向所有在线用户广播该消息。
3、如客户端向指定人发送消息,服务器将查找接收人用户线程后转发消息。
4、用户登录后,向所有人更新用户列表。
客户端:
1、用户登录功能。
2、登录后用户可以看到在线用户列表
3 、向指定人发送消息
4、向所有人发送消息
代码的实现
首先,先构建界面图形出来
上面左边是用户界面(服务器界面和用户界面一样),右边是登录界面
由于全用手写代码比较麻烦,我用了可视化UI插件;
画出来如下图
界面做出来了,然后构建对象模型,
这里主要需要信息对象,和用户对象;
信息对象又分,登录信息,发送信息和退出信息。这里我把登录信息单独用一个对象构建,因为保存了帐号密码,以后好增加登录验证功能。
然后就是逻辑功能的实现了。
这里我发现看似简单的功能,实现起来还是有点麻烦的。学会了一点就是要模块化。不然自己很容易搞迷糊。
直接上代码:
客户端代码
package com.gh.Client; import java.awt.Toolkit; public class ClientFrame { private JFrame Clientframe;
private JTextField textField;
private String username;
private JTextArea textArea = null;
private JList<String> list = null;
private DefaultListModel<String> model = null;
private UserService us; // 初始化用户列表
public void InitUserList() {
model = new DefaultListModel<String>();
model.addElement("所有人");
list.setModel(model);
} // 添加用户到在线列表
public void AddUserToList(String username) {
// 拆了东墙补西墙。。。之前添加的要全删了,再添加一次
model.removeAllElements();
model.addElement("所有人");
String[] str = username.split(",");
for (String s : str) {
model.addElement(s);
}
//list.setModel(model);
} public void updateText(String text) {
StringBuffer sb = new StringBuffer();
sb.append(textArea.getText()).append(DateUtil.getTime() + "\n").append(text).append("\n");
textArea.setText(sb.toString());
}
public void DelUser(String username){
model.removeElement(username);
} /**
* Create the application.
*/
public ClientFrame(String username,UserService us) {
this.username = username;
this.us=us;
initialize();
// 初始化用户列表
InitUserList();
} /**
* Initialize the contents of the frame.
*/
private void initialize() {
Clientframe = new JFrame();
Clientframe.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
Info info=new Info();
info.setFromUser(username);
info.setInfoType(EnumInfoType.EXIT);
us.send(info);
us.setFlag(false);
Clientframe.dispose();
}
});
Clientframe.setVisible(true);
Clientframe
.setIconImage(Toolkit.getDefaultToolkit().getImage(ClientFrame.class.getResource("/com/gh/res/1.png")));
Clientframe.setTitle("\u804A\u804A-\u5BA2\u6237\u7AEF");
Toolkit tk = Toolkit.getDefaultToolkit();
Dimension d = tk.getScreenSize();
int x = (int) (d.getWidth() - 534) / 2;
int y = (int) (d.getHeight() - 383) / 2;
Clientframe.setBounds(x, y, 534, 383);
Clientframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Clientframe.getContentPane().setLayout(new BorderLayout(0, 0)); JLabel label = new JLabel("当前用户是:" + username);
Clientframe.getContentPane().add(label, BorderLayout.NORTH); JPanel jpanel = new JPanel();
Clientframe.getContentPane().add(jpanel, BorderLayout.EAST);
jpanel.setLayout(new BorderLayout(0, 0)); JLabel lblNewLabel = new JLabel("--\u5728\u7EBF\u7528\u6237\u5217\u8868--");
jpanel.add(lblNewLabel, BorderLayout.NORTH); list = new JList<String>();
jpanel.add(list, BorderLayout.CENTER); JScrollPane scrollPane = new JScrollPane();
Clientframe.getContentPane().add(scrollPane, BorderLayout.CENTER); textArea = new JTextArea();
scrollPane.setViewportView(textArea); JPanel panel = new JPanel();
Clientframe.getContentPane().add(panel, BorderLayout.SOUTH);
panel.setLayout(new BorderLayout(0, 0)); JLabel lblNewLabel_1 = new JLabel("\u8BF7\u8F93\u5165\uFF1A");
panel.add(lblNewLabel_1, BorderLayout.WEST); textField = new JTextField();
panel.add(textField, BorderLayout.CENTER);
textField.setColumns(10); JButton button = new JButton("\u53D1\u9001");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// 获取发送信息
String sendContent = textField.getText().trim();
// 获取发送对象
Info info = new Info();
info.setFromUser(username);
info.setToUser((String) list.getSelectedValue());
info.setContent(sendContent);
info.setInfoType(EnumInfoType.SEND_INFO);
System.out.println(info.getToUser());
// 首先判断发送对象是否为空
if ("".equals(info.getToUser()) || info.getToUser() == null) {
JOptionPane.showMessageDialog(Clientframe, "请选择发送对象");
return;
} else if (info.getToUser().equals(username)) {
JOptionPane.showMessageDialog(Clientframe, "不能对自己发送");
return;
} else if (info.getContent().equals("") || info.getContent() == null) {
JOptionPane.showMessageDialog(Clientframe, "内容不能为空");
return;
} else
us.send(info);
textField.setText("");
}
});
panel.add(button, BorderLayout.EAST);
} }
ClientFrame
package com.gh.Client; import java.awt.Dimension; @SuppressWarnings("unused")
public class LoginFrame { private JFrame frame;
private JTextField id;
private JPasswordField pwd; /**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
LoginFrame window = new LoginFrame();
window.frame.setVisible(true);
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
}
});
} /**
* Create the application.
*/
public LoginFrame() {
initialize();
} /**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setIconImage(Toolkit.getDefaultToolkit().getImage(LoginFrame.class.getResource("/com/gh/res/1.png")));
Toolkit tk=Toolkit.getDefaultToolkit();
Dimension d=tk.getScreenSize();
int x=(int)(d.getWidth()-379)/2;
int y=(int)(d.getHeight()-171)/2;
frame.setResizable(false);
frame.setTitle("\u767B\u5F55");
frame.setBounds(x, y, 379, 171);
frame.getContentPane().setLayout(new GridLayout(3, 1, 20, 5)); JPanel panel = new JPanel();
frame.getContentPane().add(panel);
FlowLayout fl_panel = new FlowLayout(FlowLayout.CENTER, 5, 10);
fl_panel.setAlignOnBaseline(true);
panel.setLayout(fl_panel); JLabel label = new JLabel("\u7528\u6237\u5E10\u53F7\uFF1A");
label.setFont(new Font("微软雅黑", Font.PLAIN, 15));
panel.add(label); id = new JTextField();
panel.add(id);
id.setColumns(15); JPanel panel_1 = new JPanel();
FlowLayout flowLayout = (FlowLayout) panel_1.getLayout();
flowLayout.setAlignOnBaseline(true);
flowLayout.setVgap(10);
frame.getContentPane().add(panel_1); JLabel label_1 = new JLabel("\u7528\u6237\u5BC6\u7801\uFF1A");
label_1.setFont(new Font("微软雅黑", Font.PLAIN, 15));
panel_1.add(label_1); pwd = new JPasswordField();
pwd.setColumns(15);
panel_1.add(pwd); JPanel panel_2 = new JPanel();
FlowLayout flowLayout_1 = (FlowLayout) panel_2.getLayout();
flowLayout_1.setVgap(6);
frame.getContentPane().add(panel_2); JButton button = new JButton("\u767B\u5F55");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
String username=id.getText().trim();
String password=new String(pwd.getPassword());
if("".equals(username)||username==null||"".equals(password)||password==null){
JOptionPane.showMessageDialog(frame, "用户名和密码不能为空");
return;
}
new UserService().login(username, password,frame);
}
});
button.setFont(new Font("微软雅黑", Font.PLAIN, 15));
panel_2.add(button); JButton button_1 = new JButton("\u9000\u51FA");
button_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int v=JOptionPane.showConfirmDialog(frame, "真的要退出吗", "退出", JOptionPane.YES_NO_OPTION);
if(v==JOptionPane.YES_OPTION)System.exit(0);
}
});
button_1.setFont(new Font("微软雅黑", Font.PLAIN, 15));
panel_2.add(button_1);
} }
LoginFrame
package com.gh.Client; import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket; import javax.swing.JFrame;
import javax.swing.JOptionPane; import com.gh.model.Info;
import com.gh.model.LoginInfo; public class UserService {
private LoginInfo logininfo = null;
private ClientFrame clientframe = null;
private boolean flag = true;
private Info getInfo = null;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
private ObjectOutputStream bw;
private ObjectInputStream br;
public void login(final String username, final String password, final JFrame frame) {
//接收消息线程
Thread t=new Thread(new Runnable() {
@Override
public void run() {
try {
Socket sk = new Socket("192.168.1.102", 8000);
//这里被堵了好久,必须要先创建bw再创建br,因为scoket不许客户端和服务端同时创建输入流否者会堵塞Scoket通道
bw =new ObjectOutputStream(sk.getOutputStream());
br = new ObjectInputStream(sk.getInputStream());
logininfo = new LoginInfo(username, password);
bw.writeObject(logininfo);//向服务器发送登录信息
bw.flush();
//从服务接收登录信息
LoginInfo objflag = (LoginInfo) br.readObject();
if (objflag.isFlag()) {// 如果可以登录
// 创建聊天界面
clientframe = new ClientFrame(logininfo.getName(),UserService.this);
// 聊天界面显示欢迎信息
clientframe.updateText("服务器:欢迎登录!");
frame.dispose();
// 等待接收消息
while (isFlag()) {
getInfo = new Info();
getInfo = (Info) br.readObject();
switch (getInfo.getInfoType()) {
case SEND_INFO:
clientframe.updateText("用户:"+getInfo.getFromUser()+"说:"+getInfo.getContent());
break;
case ADD_USER:
clientframe.AddUserToList(getInfo.getContent());
break;
case EXIT:
clientframe.DelUser(getInfo.getFromUser());
clientframe.updateText("服务器:用户"+getInfo.getFromUser()+"下线了");
break;
default:
break;
} }
} else {
JOptionPane.showMessageDialog(frame, "用户名或密码错误");
}
} catch (Exception e) {
JOptionPane.showMessageDialog(frame, "服务器连接异常");
//e.printStackTrace();
}
}
});
t.start();
}
//发送消息线程
public void send(Info info){
try {
bw.writeObject(info);
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
UserService
服务端代码
package com.gh.Sever; import java.awt.EventQueue; public class ServerFrame { private JFrame Severframe;
private JTextField textField;
private JTextArea textArea = null;
private DefaultListModel<String> model=null;
private JList<String> list=null;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ServerFrame window = new ServerFrame();
window.Severframe.setVisible(true);
// 设置UI风格为系统默认的风格
//UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
}
}); }
//初始化用户列表
public void InitUserList(){
model=new DefaultListModel<String>();
model.addElement("所有人");
list.setModel(model);
}
//添加用户到在线列表
public void AddUserToList(String username){
model.addElement(username);
list.setModel(model);
}
public void updateText(String text) {
StringBuffer sb = new StringBuffer();
sb.append(textArea.getText()).append("\n").append(DateUtil.getTime()+"--")
.append(text);
textArea.setText(sb.toString());
}
public void DelUser(String username){
model.removeElement(username);
}
/**
* Create the application.
*/
public ServerFrame() {
initialize();
// 启动服务
startSever();
//初始化用户列表
InitUserList();
} private void startSever() {
textArea.setText("服务器已经启动,正在监听8000端口...");
new Thread(new Runnable() {
@Override
public void run() {
new ServerService(ServerFrame.this).startSever();
}
}).start();
} /**
* Initialize the contents of the frame.
*/
private void initialize() {
Severframe = new JFrame();
Severframe
.setIconImage(Toolkit.getDefaultToolkit().getImage(ServerFrame.class.getResource("/com/gh/res/1.png")));
Severframe.setTitle("\u804A\u804A-\u670D\u52A1\u7AEF");
Toolkit tk = Toolkit.getDefaultToolkit();
Dimension d = tk.getScreenSize();
int x = (int) (d.getWidth() - 534) / 2;
int y = (int) (d.getHeight() - 383) / 2;
Severframe.setBounds(x, y, 534, 383);
Severframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Severframe.getContentPane().setLayout(new BorderLayout(0, 0)); JLabel label = new JLabel("\u670D\u52A1\u5668\u7AEF");
Severframe.getContentPane().add(label, BorderLayout.NORTH); JPanel jpanel = new JPanel();
Severframe.getContentPane().add(jpanel, BorderLayout.EAST);
jpanel.setLayout(new BorderLayout(0, 0)); JLabel lblNewLabel = new JLabel("--\u5728\u7EBF\u7528\u6237\u5217\u8868--");
jpanel.add(lblNewLabel, BorderLayout.NORTH); list = new JList<String>();
jpanel.add(list, BorderLayout.CENTER); JScrollPane scrollPane = new JScrollPane();
Severframe.getContentPane().add(scrollPane, BorderLayout.CENTER); textArea = new JTextArea();
scrollPane.setViewportView(textArea); JPanel panel = new JPanel();
Severframe.getContentPane().add(panel, BorderLayout.SOUTH);
panel.setLayout(new BorderLayout(0, 0)); JLabel lblNewLabel_1 = new JLabel("\u8BF7\u8F93\u5165\uFF1A");
panel.add(lblNewLabel_1, BorderLayout.WEST); textField = new JTextField();
panel.add(textField, BorderLayout.CENTER);
textField.setColumns(10); JButton button = new JButton("\u53D1\u9001");
panel.add(button, BorderLayout.EAST);
} }
ServerFrame
package com.gh.Sever; import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import com.gh.model.Info;
import com.gh.model.LoginInfo;
import com.gh.util.EnumInfoType; /**
* 服务器客户端监听
*
* @author ganhang
*/
public class ServerService {
private Info currInfo = null;
private ServerFrame serFrame;
private boolean flag;
private UserServerThread ust;
ExecutorService es = Executors.newScheduledThreadPool(1);
// 保存所有在线用户服务线程
private Vector<UserServerThread> userThreads; public ServerService(ServerFrame serFrame) {
this.serFrame = serFrame;
} public void startSever() {
flag = true;
try {
ServerSocket server = new ServerSocket(8000);
userThreads = new Vector<UserServerThread>();
while (flag) {
Socket client = server.accept();
System.out.println("Server:有用户连接");
ObjectInputStream ois = new ObjectInputStream(client.getInputStream());
ObjectOutputStream oos = new ObjectOutputStream(client.getOutputStream());
LoginInfo logininfo = (LoginInfo) ois.readObject();// 获取用户帐号密码
System.out.println(logininfo);
// 登录验证
if (UserLogin.islogin(logininfo.getName(), logininfo.getPwd())) {
logininfo.setFlag(true);
oos.writeObject(logininfo);// 发送确认登录信息
oos.flush();
// 保存当前用户信息
currInfo = new Info();
currInfo.setFromUser(logininfo.getName());
// 在服务器端显示用户登录信息
serFrame.updateText("用户" + logininfo.getName() + "--已上线!");
// 创建用户服务线程
UserServerThread ust = new UserServerThread(currInfo, ois, oos);// 创建用户服务进程
userThreads.add(ust);// 添加到进程池
es.execute(ust);// 启动线程
} else
oos.writeObject(logininfo);
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
} /**
* 用户服务线程
*
* @author ganhang
*/
class UserServerThread implements Runnable {
private Info info = null;
private ObjectOutputStream oos;
private ObjectInputStream ois; public UserServerThread(Info info, ObjectInputStream ois, ObjectOutputStream oos) {
this.info = info;
this.oos = oos;
this.ois = ois;
} @Override
public void run() {
// 更新服务器的在线用户列表
serFrame.AddUserToList(currInfo.getFromUser());
// 先获得在线用户的列表
StringBuffer sb = new StringBuffer();
for (UserServerThread u : userThreads) {
sb.append(u.info.getFromUser()).append(",");
}
System.out.println(sb.toString());
// 首先向线程池里每个用户发送一个自己的在线用户列表
for (UserServerThread usts : userThreads) {
try {
currInfo.setInfoType(EnumInfoType.ADD_USER);
currInfo.setContent(sb.toString());
usts.oos.writeObject(currInfo);// 注意要写那个线程的输出流
usts.oos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
// 等待接收用户信息
System.out.println(flag);
new Thread(new Wait(ois, oos)).start(); } public void send(Info sendinfo) throws IOException {
oos.writeObject(sendinfo);
oos.flush();
}
} // 接收信息
class Wait implements Runnable {
private ObjectOutputStream oos;
private ObjectInputStream ois;
private boolean wait = true; public Wait(ObjectInputStream ois, ObjectOutputStream oos) {
this.oos = oos;
this.ois = ois;
} @Override
public void run() {
while (wait) {
try {
// 接收信息必须重开线程因为会把当前线程阻塞
Info info = (Info) ois.readObject();
System.out.println(Thread.currentThread().getName() + "收到一个send信息");
System.out.println(info);
switch (info.getInfoType()) {
case SEND_INFO:
if (info.getToUser().equals("所有人")) {
for (UserServerThread user : userThreads) {
//这里容易发错人,注意每个用户都有一个服务线程,哪个用户要接收信息就由它的服务线程去发给它,其他用户的服务线程发不了。
if (!info.getFromUser().equals(user.info.getFromUser()))
user.send(info);
System.out.print(user.info.getFromUser() + "/");
}
System.out.println("消息发送名单");
} else {
for (UserServerThread user : userThreads) {
if (info.getToUser().equals(user.info.getFromUser())) {
user.send(info);
}
}
}
break;
case EXIT:
serFrame.DelUser(info.getFromUser());
serFrame.updateText("用户" + info.getFromUser() + "下线了");
for (UserServerThread user : userThreads) {
if (!info.getFromUser().equals(user.info.getFromUser())) {
user.send(info);
}
//这里ust保存的是最后登录的用户的线程因为一直在被覆盖,
//需要重新写如当前要删的
else ust = user;
System.out.print(user.info.getFromUser() + ",");
}
System.out.println("退出发送名单");
// 删除服务器上的该在线用户
wait = false;
System.out.println("移除的用户:" + ust.info.getFromUser());
//这里容易删错用户线程
userThreads.remove(ust);
break;
default:
break;
}
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
} }
}
} }
ServerService
package com.gh.Sever;
/**
* 登录验证,以后加数据库在写
* @author ganhang
*/
public class UserLogin {
public static boolean islogin(String name ,String pwd){
return true;
}
}
UserLogin
对象模型
package com.gh.model; import java.io.Serializable; import com.gh.util.EnumInfoType;
/**
* 消息对象
* @author ganhang
*
*/
public class Info implements Serializable{
private String fromUser;//信息来自哪
private String toUser;//发给谁
private String content;//内容
private EnumInfoType infoType;//信息类型
public String getFromUser() {
return fromUser;
}
public void setFromUser(String fromUser) {
this.fromUser = fromUser;
}
public String getToUser() {
return toUser;
}
public void setToUser(String toUser) {
this.toUser = toUser;
} public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public EnumInfoType getInfoType() {
return infoType;
}
public void setInfoType(EnumInfoType infoType) {
this.infoType = infoType;
}
public Info(String fromUser, String toUser, String sendTime, String content, EnumInfoType infoType) {
super();
this.fromUser = fromUser;
this.toUser = toUser; this.content = content;
this.infoType = infoType;
}
@Override
public String toString() {
return "Info [fromUser=" + fromUser + ", toUser=" + toUser + ", content=" + content
+ ", infoType=" + infoType + "]";
}
public Info() {
super();
} }
Info
package com.gh.model; import java.io.Serializable; @SuppressWarnings("serial")
public class LoginInfo implements Serializable{
private String name;
private String pwd;
private boolean flag=false;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public LoginInfo(String name, String pwd) {
super();
this.name = name;
this.pwd = pwd;
}
public LoginInfo() {
super();
}
@Override
public String toString() {
return "LoginInfo [name=" + name + ", pwd=" + pwd + ", flag=" + flag + "]";
} }
LoginInfo
package com.gh.model;
/**
* 用户模型
* @author ganhang
* 暂时没用,因为没涉及数据库和一些用户管理功能
*/
public class User {
private String name;
private String age;
private String pwd;
private String email;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ", pwd=" + pwd + ", email=" + email + "]";
}
public User(String name, String age, String pwd, String email) {
super();
this.name = name;
this.age = age;
this.pwd = pwd;
this.email = email;
}
public User() {
super();
}
public User(String name, String pwd) {
super();
this.name = name;
this.pwd = pwd;
}
public User(String name) {
super();
this.name = name;
} }
User
整个代码:https://github.com/ganhang/My-ChatSystem
java学习之即时通信项目实战的更多相关文章
- 基于开源SuperSocket实现客户端和服务端通信项目实战
一.课程介绍 本期带给大家分享的是基于SuperSocket的项目实战,阿笨在实际工作中遇到的真实业务场景,请跟随阿笨的视角去如何实现打通B/S与C/S网络通讯,如果您对本期的<基于开源Supe ...
- 【原】iOS学习43即时通信之XMPP(2)
本篇是 即时通信之XMPP(2) 接上次 即时通信之XMPP(1) 1. 好友列表 1> 初始化好友花名册 // 获取管理好友的单例对象 XMPPRosterCoreDataStorage *r ...
- 【原】iOS学习42即时通信之XMPP(1)
1. 即时通信 1> 概述 即时通讯(Instant Messaging)是目前Internet上最为流行的通讯方式,各种各样的即时通讯软件也层出不穷,服务提供商也提供了越来越丰富的通讯服务功能 ...
- Java面向对象和高级特性 项目实战(一)
一.项目简介 项目名:嗖嗖移动业务大厅 技能点: 二.技能点 三.系统概述 四.整体开发思路 五.实体类和接口开发 六. 创建工具类 七.使用集合存储数据 八.开发计划 九.代码实现 1.项目目录 2 ...
- Java学习之线程通信(多线程(Lock))--生产者消费者
SDK1.5版本以后对synchronized的升级,其思想是:将同步和锁封装成对象,其对象中包含操作锁的动作. 代码: //1.导入包 import java.util.concurrent.loc ...
- Java学习之线程通信(多线程(synchronized))--生产者消费者
分析线程经典案例生产者消费者 /** 共享数据 */ class Resource { private String name; private int count=1; private boolea ...
- 深度学习之Tensorflow 工程化项目实战 配套资源
https://www.aianaconda.com/index/CodeProject
- Java学习路径及练手项目合集
Java 在编程语言排行榜中一直位列前排,可知 Java 语言的受欢迎程度了. 实验楼上的[Java 学习路径]中将首先完成 Java基础.JDK.JDBC.正则表达式等基础实验,然后进阶到 J2SE ...
- Java 18套JAVA企业级大型项目实战分布式架构高并发高可用微服务电商项目实战架构
Java 开发环境:idea https://www.jianshu.com/p/7a824fea1ce7 从无到有构建大型电商微服务架构三个阶段SpringBoot+SpringCloud+Solr ...
随机推荐
- openwrt看IP流量
可以利用iptable来实现的,看附件的脚本. 把他放到路由器里面,然后运行. #!/bin/sh echo "Collecting data..." echo "&qu ...
- 搭建单节点Hadoop应用环境
虚拟机: VirtualBox 5 Server操作系统: Ubuntu Server 14.04.3 LTS 如果对虚拟机空间和性能不做考虑, 且不习惯用Linux命令, 你也可以使用Ubuntu ...
- hadoop搭建杂记:Linux下JDK环境变量的设置(三种配置环境变量的方法)
Linux下JDK环境变量的设置(三种配置环境变量的方法) Linux下JDK环境变量的设置(三种配置环境变量的方法) ①修改/etc/profile文件 如果你的计算机仅仅作为开发使用时推荐使用这种 ...
- codeforces 600E. Lomsat gelral 启发式合并
题目链接 给一颗树, 每个节点有初始的颜色值. 1为根节点.定义一个节点的值为, 它的子树中出现最多的颜色的值, 如果有多种颜色出现的次数相同, 那么值为所有颜色的值的和. 每一个叶子节点是一个map ...
- 在输出视图中使用使用html注释会导致在Chrome中css失效
在做SportsStore例子时,在视图List.cshtml中使用了html注释,即 <!-- 注释 --> 结果在加载css时,chrome浏览器中所有css效果都失效.IE不受影响. ...
- 解决libc.so.6: version `GLIBC_2.14' not found问题, 升级glibc,glibc-2.15
0.以下在系统CentOS 6.3 x86_64上操作 1.试图运行程序,提示"libc.so.6: version `GLIBC_2.14' not found",原因是系统的g ...
- BZOJ 2253: [2010 Beijing wc]纸箱堆叠
题目 2253: [2010 Beijing wc]纸箱堆叠 Time Limit: 30 Sec Memory Limit: 256 MBSubmit: 239 Solved: 94 Descr ...
- POJ训练计划1459_Power Network(网络流最大流/Dinic)
解题报告 这题建模实在是好建.,,好贱.., 给前向星给跪了,纯dinic的前向星居然TLE,sad.,,回头看看优化,.. 矩阵跑过了.2A,sad,,, /******************** ...
- setInterval()与clearInterval()的用法
setInterval() 方法可按照指定的周期来调用函数或计算表达式. --简单地说就是过一段时间调用一次该函数 setInterval() 方法会不停地调用函数,直到 clearInterval ...
- ongl 表达式
struts.xml简单配置 <!-- (默认false)设置ognl表达式是否支持静态方法 --> <constant name="struts.ognl.allowSt ...