前面间学习了java io的编程,后来感觉光只是看这些收获甚微,于是就自己做了一个简单的聊天室。基于java socket的聊天室,实现了客户端,实现了[b]群聊,私聊,发送表情,互相发送文件,查看聊天记录,以及一些相关的设置[/b];服务器端实现了[b]发送系统公告,以及踢人[/b]的功能。具体的代码,我上传在附件里面。


首先,介绍一个系统的总体架构,基于c/s模式的java聊天室,采用socket编程,当然,传输数据,我采用的是tcp协议,在服务器端进行监听,每次加入一个新的用户,就开辟一个新的线程用于监听这个用户的动态。当然,这种方式可能不是很科学,但由于知识有限,只能简单的应用。

/**
     * 功能:启动服务器
     */
    public static void startServer() {
        try {
            server = new ServerSocket(port);
            flag = true;
            System.err.print("服务器已经启动,正在监听用户线程.....");
        } catch (Exception e) {
            System.out.println("异常");
            System.err.println(e);
        }
        while (flag) { // 死循环
            Socket socket = null;
            try {
                socket = server.accept();
                ServerThread cp = new ServerThread(socket); // 启动一个用户线程
                System.out.println("新进入一个用户");
                cp.setIndex(count + 1);
                Thread ct = new Thread(cp);
                ct.start();
                addConnection(socket, cp);
                count++;
            } catch (IOException e) {
                System.err.println(e);
                try {
                    socket.close();
                } catch (IOException e1) {

                    e1.printStackTrace();
                }
            }
        }
    }





另外还有一个服务器新建的线程类,ServerThread 由于过长,就不粘贴了。ServerThread 是实现Runnable接口的,里面含有,线程的名称(我这里用的用户的昵称作为参数),线程的索引(也就是在用户列表中的位置),以及ip地址(为了实现俩个聊天用户发送文件),以及socket。




[b]需要说明的我的整体思路[/b]



用户端能够进行登录,注册,以及其他一些功能,ServerThread用户监听用户发来的数据。我在这里采用的字符流来进行发送(开始的时候,我采用的是ObjectStream,后来发现这样的传输效率很低,而且经常容易出错,这里不知道为什么),所以后来我就采用了字符流来进行传输数据。



当然,java 是面向对象的思想,我还是将一些东西创建了实体类,如MessageModel




/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package chatroom.com.entity;
/**
 *
 * @author Administrator
 */
import java.io.Serializable;
import java.util.Date;

public class MessageModel{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private int id;
	private String fromWho;// 谁说的
	private String toWho;// 对谁说
	private String dongzuo;// 动作是什么
	private int biaoqing;// 表情是什么
	private String ziti;// 字体
	private String yanse;// 颜色
	private String daxiao;// 大小
	private String yangshi;// 样式

	private String content;// 内容
	private int isSiliao;// 是否私聊
	private Date sendTime;

        /**
         * 功能:将字符串转换后MessageModel对象
         * @param str
         * @return 
         */
	public static MessageModel getMsgByString(String str) {
		String rs[] = str.split("&");
		MessageModel msg = new MessageModel();
		msg.setFromWho(rs[1]);
		msg.setToWho(rs[2]);
		msg.setDongzuo(rs[3]);
		msg.setBiaoqing(Integer.parseInt(rs[4]));
		msg.setZiti(rs[5]);
		msg.setYanse(rs[6]);
		msg.setDaxiao(rs[7]);
		msg.setYangshi(rs[8]);
		msg.setContent(rs[9]);
		msg.setIsSiliao(Integer.parseInt(rs[10]));
		return msg;

	}

        /**
         * 功能:将MessageModel对象转换成字符串
         * @param msg
         * @return 
         */
	public static String getStringByMessage(MessageModel msg) {
		return msg.getFromWho() + "&" + msg.getToWho() + "&" + msg.getDongzuo()
				+ "&" + msg.getBiaoqing() + "&" + msg.getZiti() + "&"
				+ msg.getYanse() + "&" + msg.getDaxiao() + "&"
				+ msg.getYangshi() + "&" + msg.getContent() + "&"
				+ msg.getIsSiliao();
	}

	@Override
	public String toString() {
		return "MessageModel [id=" + id + ", fromWho=" + fromWho + ", toWho="
				+ toWho + ", dongzuo=" + dongzuo + ", biaoqing=" + biaoqing
				+ ", ziti=" + ziti + ", yanse=" + yanse + ", daxiao=" + daxiao
				+ ", yangshi=" + yangshi + ", content=" + content
				+ ", isSiliao=" + isSiliao + ", sendTime=" + sendTime + "]";
	}

还有一些setter,geter方法

}





[b]另外,对于聊天记录的读写,我采用的是文件的操作,主要是按行写入文件,按行读取文件,这里,我稍微做了一些改动,能够实现分页读取聊天记录。[/b]



/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package chatroom.com.util;

import chatroom.com.chatEvent.Chat;
import java.io.*;
import javax.swing.JTextPane;

/**
 *功能:将记录按行写入文件中, 另外可以实现从文本文件中按行读取记录,同时能够实现分页读取文本文件的内容
 * @author 李朋飞
 */
public class FileForJiluUtil {

    public static void createNewFile(String fileName) throws IOException {
        File myFilePath = new File(fileName);
        if (!myFilePath.exists()) {
            myFilePath.createNewFile();
        }
    }

    public static void appendJiluToFile(String fileName, String jilu) {
        try {
            createNewFile(fileName);
            // write string to file
            FileWriter writer = new FileWriter(fileName, true);
            BufferedWriter bw = new BufferedWriter(writer);
            bw.write(jilu);
//            bw.newLine();//如果是加了这个,会在文本文件里面显示了另外一行,不加的话,看到是连续的,也就是不是换行的,即使我的jilu字符串后面加了  \n
            bw.flush();
            bw.close();
            writer.close();

        } catch (IOException e) {
        }

    }
    private static int pageSize = 16;

    /**
     * anhang分页读取文件里面的内容
     *
     * @param pageNo
     * @param fileName
     */
    public static int readFileByPage(int pageNo, String fileName, JTextPane pane) {
        File file = new File(fileName);
        BufferedReader reader = null;
        try {
            System.out.println("以行为单位读取文件内容,一次读一整行:");
            reader = new BufferedReader(new FileReader(file));
            String tempString = null;
            int line = 0;
            int index = 1;
            //一次读入一行,直到读入null为文件结束,由于我在写入文本文件的时候,在每个字符串后面都加了 \n,所以查看的时候,会在基数行显示内容,偶数行显示空字符串。所以加了一个偶数行的判断
            while ((tempString = reader.readLine()) != null) {
                //显示行号
                if (index % 2 != 0) {
                    line++;
                    if (line > (pageNo - 1) * pageSize && line <= pageNo * pageSize) {
                        System.out.println("line " + line + ": " + tempString);
                        Chat.insertIntoHistoryPane(pane, line+":    " + tempString);
                    }
                }

                index = index + 1;
                if (line > pageNo * pageSize) {
                    break;
                }
            }
            reader.close();
        } catch (IOException e) {
        }
        return pageNo;
    }

    /**
     * 以行为单位读取文件,常用于读面向行的格式化文件
     *
     * @param fileName 文件名
     */
    public static void readFileByLines(String fileName) {
        File file = new File(fileName);
        BufferedReader reader = null;
        try {
            System.out.println("以行为单位读取文件内容,一次读一整行:");
            reader = new BufferedReader(new FileReader(file));
            String tempString = null;
            int line = 1;
            //一次读入一行,直到读入null为文件结束
            while ((tempString = reader.readLine()) != null) {
                //显示行号
                System.out.println("line " + line + ": " + tempString);
                line++;
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                }
            }
        }
    }
}





[b]关于发送表情,我这里发送的不是表情文件,而是发送表情对应的一些特殊的编号,这个可能有点搓,思路也很垃圾,但还能用[/b]




剩下最后一个问题,就是俩个聊天用户进行互相发送文件,由于对于java io编程以及java 网络编程,都不是很熟悉这个问题困扰了我一天,后来通过查阅资料,结合聊天室整个的流程,终于实现了用户互相传送文件,我这里的思路是当服务器端收到一个用户发来,我要发送文件的请求时,判断这个用户要发给谁,然后,使这俩个用户之间建立连接。发送文件的一方暂时作为服务器,接受方作为客户端。当文件传输成功之后,断开俩人的连接,具体的实现请参考附件源代码





今天终于有机会上传项目源文件了,另外里面包含了系统所需要的jar包。主要是一个mysql的jdbc驱动jar,和一个java mail的jar文件。为了节省空间,只上传了java mail的jar文件,另外mysql的驱动jar,网上太多。



项目运行主要是chatroom里面的server ,及client,先运行server。里面自己做了个找回密码的功能,找回密码是采用的发送邮件的方式