前言`
bio即时通讯小案例
一、即时通讯案例
1.服务端
代码如下(示例):
package com.xuexi.testSocket.jiShiTongXun;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ServerChat {
// 定义集合存放所有在线socket, key:socket,value:socket客户端的名称
public static Map<Socket,String> onLineSockets = new HashMap<Socket,String>();
public static void main(String[] args) {
try{
// 注册端口
ServerSocket serverSocket = new ServerSocket(9999);
// 循环等待所有可能的客户端连接
while (true){
Socket socket = serverSocket.accept();
// 把客户端的socket管道单独配置一个线程来处理
new ServerReader(socket).start();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
package com.xuexi.testSocket.jiShiTongXun;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ConnectException;
import java.net.Socket;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Set;
public class ServerReader extends Thread{
private Socket socket;
public ServerReader(Socket socket){
this.socket = socket;
}
public void run(){
DataInputStream dis = null;
try{
dis = new DataInputStream(socket.getInputStream());
// 循环等待客户端的消息
while(true){
// 获取消息类型:登录,群发,私聊,@消息
int flag = dis.readInt();
if(flag == 1){
// 先将当前登录的客户端socket存放到在线人数的socket集合中
String name = dis.readUTF();
System.out.println(name + "----->"+socket.getRemoteSocketAddress());
ServerChat.onLineSockets.put(socket,name);
}
writeMsg(flag,dis);
}
}catch (SocketException ce){
System.out.println("-- \""+ServerChat.onLineSockets.get(socket)+"\"下线了 --");
// 从在线集合中删除
ServerChat.onLineSockets.remove(socket);
try{
writeMsg(1,dis);
}catch (Exception e){
e.printStackTrace();
}
}catch(Exception e){
e.printStackTrace();
}
}
public void writeMsg(int flag, DataInputStream dis) throws IOException {
// 拿到所有的在线socket通道,给这些通道写出消息
String msg = null;
if(flag == 1){
// 读取所有在线人数发给客户端去更新所有列表在线人数
StringBuilder rs = new StringBuilder();
Collection<String> onLineNmaes = ServerChat.onLineSockets.values();
// 判断是否存在在线人数
if(onLineNmaes != null && onLineNmaes.size() > 0){
for(String name : onLineNmaes){
rs.append(name + ",");
}
// 去掉最后一个分隔符
msg = rs.substring(0,rs.lastIndexOf(","));
// 将消息发送给客户端
sendMsgToAll(flag,msg);
}
}else if(flag == 2 || flag == 3){
// 读到群发消息或者@消息
String newMsg = dis.readUTF();
// 获取发件人
String sendName = ServerChat.onLineSockets.get(socket);
// 内容
StringBuilder msgFinal = new StringBuilder();
// 时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEE");
if(flag == 2){ // 群发或@消息
msgFinal.append(sendName).append(" ").append(sdf.format(new Date())).append("\n");
msgFinal.append(" ").append(newMsg).append("\n");
sendMsgToAll(flag,msgFinal.toString());
}else if(flag == 3){// 私发
msgFinal.append(sendName).append(" ").append(sdf.format(new Date())).append("对您私发\n");
msgFinal.append(" ").append(newMsg).append("\n");
String destName = dis.readUTF();
sendMsgToOne(destName,msgFinal.toString());
}
}
}
private void sendMsgToOne(String destName, String msg) throws IOException {
Set<Socket> allOnLineSockets = ServerChat.onLineSockets.keySet();
for(Socket sk : allOnLineSockets){
if(ServerChat.onLineSockets.get(sk).trim().equals(destName)){
DataOutputStream dos = new DataOutputStream(sk.getOutputStream());
dos.writeInt(2); // 消息类型
dos.writeUTF(msg);
dos.flush();
}
}
}
private void sendMsgToAll(int flag, String msg) throws IOException {
// 拿到所有在线的socket管道,给这些管道写出消息
Set<Socket> allOnLineSockets = ServerChat.onLineSockets.keySet();
for(Socket sk : allOnLineSockets){
DataOutputStream dos = new DataOutputStream(sk.getOutputStream());
dos.writeInt(flag); // 消息类型
dos.writeUTF(msg);
dos.flush();
}
}
}
2.客户端
代码如下(示例):
package com.xuexi.testSocket.jiShiTongXun;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataOutputStream;
import java.net.Socket;
public class ClientChat implements ActionListener {
// 设计界面
private JFrame win = new JFrame();
// 消息内容框架
public JTextArea smsContent = new JTextArea(23,50);
// 发送消息框
private JTextArea smsSend = new JTextArea(4,40);
// 在线人数的区域
// 存放人数数据
// 展示在线人数的窗口
public JList<String> onLineUsers = new JList<String>();
// 是否私聊按钮
private JCheckBox isPrivateBn = new JCheckBox("私聊");
// 消息按钮
private JButton sendBn = new JButton("发送");
// 登录界面
private JFrame loginView;
private JTextField ipEt, nameEt, idEt;
private Socket socket;
public static void main(String[] args) {
new ClientChat().initView();
}
private void initView(){
// 初始化聊天窗口界面
win.setSize(650,600);
// 展示登录界面
displayLoginView();
}
private void displayChatView(){
JPanel bottomPanel = new JPanel( new BorderLayout());
// 将消息框和按钮添加到窗口底部
win.add(bottomPanel,BorderLayout.SOUTH);
bottomPanel.add(smsSend);
JPanel btns = new JPanel(new FlowLayout(FlowLayout.LEFT));
btns.add(sendBn);
btns.add(isPrivateBn);
bottomPanel.add(btns,BorderLayout.EAST);
// ---------------------------------------------
// 给按钮添加事件监听器
// 将展示区添加到窗口中间
smsContent.setBackground(new Color(0xdd,0xdd,0xdd));
// 让展示消息可滚动
win.add(new JScrollPane(smsContent),BorderLayout.CENTER);
smsContent.setEditable(false);
// ---------------------------------------------
//用户列表和是否私聊在窗口最右边
Box rightBox = new Box(BoxLayout.Y_AXIS);
onLineUsers.setFixedCellWidth(120);
onLineUsers.setVisibleRowCount(13);
rightBox.add(new JScrollPane(onLineUsers));
win.add(rightBox,BorderLayout.EAST);
// ---------------------------------------------
//关闭窗口推出当前程序
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.pack();// 关闭窗口功能
// 设置窗口居中,显示窗口
setWindowCenter(win,650,600,true);
// 发送按钮绑定点击事件
sendBn.addActionListener(this);
}
private void displayLoginView(){
/** 登录
* 服务器ip
* 用户名
* id
*/
// 显示登录框
loginView = new JFrame("登录");
loginView.setLayout(new GridLayout(3,1));
loginView.setSize(400,230);
JPanel ip = new JPanel();
JLabel label = new JLabel(" IP:");
ip.add(label);
ipEt = new JTextField(20);
ip.add(ipEt);
loginView.add(ip);
JPanel name = new JPanel();
JLabel label1 = new JLabel("姓名:");
name.add(label1);
nameEt = new JTextField(20);
name.add(nameEt);
loginView.add(name);
JPanel btnView = new JPanel();
JButton login = new JButton("登录");
btnView.add(login);
JButton cancle = new JButton("取消");
btnView.add(cancle);
loginView.add(btnView);
// 关闭窗口退出当前程序
loginView.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setWindowCenter(loginView, 400, 260, true);
// 给登录取消绑定事件
login.addActionListener(this);
cancle.addActionListener(this);
}
private static void setWindowCenter(JFrame frame, int width, int height, boolean flag){
// 获取所在系统的屏幕宽高
Dimension ds = frame.getToolkit().getScreenSize();
// 电脑屏幕宽
int width1 = ds.width;
// 电脑屏幕高
int height1 = ds.height;
System.out.println(width1 + "*" + height1);
// 设置窗口左上角坐标
frame.setLocation(width1/2 - width/2, height1/2 - height/2);
frame.setVisible(flag);
}
@Override
public void actionPerformed(ActionEvent e) {
// 获取点击事件
JButton btn = (JButton) e.getSource();
switch (btn.getText()){
case "登录":
String ip = ipEt.getText().toString();
String name = nameEt.getText().toString();
// 校验参数是否为空
// 提示错误消息
String msg = "";
if(ip == null || !ip.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")){
msg = "请输入合法ip";
}else if(name == null || !name.matches("\\S{1,}")){
msg = "姓名不少于1个字符";
}
if(!msg.equals("")){
JOptionPane.showMessageDialog(loginView,msg);
}else {
try{
// 显示当前用户名称
win.setTitle(name);
// 连接一个socket管道
socket = new Socket(ip,9999);
// 分配线程,处理请求
new ClientReader(this,socket).start();
// 组装发送数据
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeInt(1);// 消息类型
dos.writeUTF(name.trim());
dos.flush();
// 关闭当前窗口,弹出聊天界面
loginView.dispose();// 登录窗口销毁
displayChatView();//展示聊天窗口
}catch (Exception exc){
exc.printStackTrace();
}
}
break;
case "取消":
// 推出系统
System.exit(0);
break;
case "发送":
// 得到发送消息的内容
String msgSend = smsSend.getText().toString();
if(!msgSend.trim().equals("")){
try{
// 判断是否对谁发消息
String selectName = onLineUsers.getSelectedValue();
int flag = 2;
if(selectName != null && !selectName.equals("")){
msgSend = ("@"+selectName+","+msgSend);
// 判断是否选中了私发
if(isPrivateBn.isSelected()){
flag = 3;
}
}
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeInt(flag);// 消息类型
dos.writeUTF(msgSend);
if(flag == 3){
dos.writeUTF(selectName.trim());
}
dos.flush();
}catch(Exception exc){
exc.printStackTrace();
}
}
smsSend.setText(null);
break;
}
}
}
package com.xuexi.testSocket.jiShiTongXun;
import java.io.DataInputStream;
import java.io.OutputStream;
import java.net.Socket;
public class ClientReader extends Thread{
private Socket socket;
private ClientChat clientChat;
public ClientReader(ClientChat clientChat,Socket socket){
this.clientChat = clientChat;
this.socket = socket;
}
public void run(){
try{
DataInputStream dis = new DataInputStream(socket.getInputStream());
while(true){
// 获取当前消息类型:登录,群发,私聊,@消息
int flag = dis.readInt();
if(flag ==1){
// 在线人数消息回来了
String nameDatas = dis.readUTF();
// 展示到在线人数的界面
String[] names = nameDatas.split(",");
clientChat.onLineUsers.setListData(names);
}else if(flag ==2){
// 获取消息内容
String msg = dis.readUTF();
clientChat.smsContent.append(msg);
// 让消息界面滚动到底端
clientChat.smsContent.setCaretPosition(clientChat.smsContent.getText().length());
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}