JAVA简单聊天室的实现

JAVA简单聊天室的实现 局域网内以及跨局域网通信

期末的课程设计,做的并不够好,写在这里记录一下自己的成长,也欢迎大家给出宝贵意见
下面程序只能在局域网内通信,要想跨网通信需要有公有IP,文末有介绍。
先放效果图:
客户端主界面

客户端注册界面

客户端聊天界面

服务器界面

# 主要知识点

网络编程 GUI(图形用户界面) 多线程

#实现功能
客户端:注册 登录 群聊 私聊
服务器端:显示用户上线/下线 登录日志及聊天信息的保存及查看

主要思路:

  1. 登录及注册功能的实现
    客户端先与服务器端建立TCP/IP连接,从文本框获取用户名、密码,并按照一定格式分隔开,发送给服务器,后者读取本地磁盘中保存的用户信息,并与之比对,存在该用户名则返回“1”表示登录成功,否则返回“0”。
  2. 群聊与私聊
    服务器端保存所有已建立套接的用户,(每当有新用户上线,服务器都会返回当前所有在线用户的昵称,客户端收到后更新自己的列表)收到群发消息后就一次发给所有在线用户,私聊信息就只发送给特定用户。这又会产生两个问题:
    (1)怎么判断群聊还是私聊
    这个可以用“封装”的思想来解决。比如用户“花花草”选择“发送给 所有人”,并发送“大家好”,那么客户端会对其进行封装成:“1|花花草->所有人:大家好”,再发送给服务器,服务器收到后进行简单判断,“1”表示发给所有人,然后再截取“|”之后的内容转发给所有客户端。(前面说的更新在线用户列表,客户端也是用这种方法来判断服务器发来的是消息还是在线用户名)
    (2)怎么发送给特定的人
    由于当时临近验收,我用的是很投机取巧的方法。由于不断更新,客户端和服务器的在线用户列表是完全一致的,所以客户端选择发送给某个人时程序获取该用户在列表中的位置i,然后把这个值放在“|”的前面,这样服务器在收到消息后直接发给第i个用户。(如果通过用户账号来判断应该发给谁可能会更稳妥)

下面附上源码(代码写的很冗杂,变量名很迷,本来想重构一下,可是我懒/捂脸)

客户端 主类

package chat;

public class Client {

	public static void main(String[] args) {
		LogIn l = new LogIn();
		l.log();
	}
}

客户端 LogIn类(实现注册登录)

package chat;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.awt.*;
import javax.swing.*;

public class LogIn extends JFrame implements ActionListener{

	Socket client;
	ServerSocket server;
	InputStream in;
	OutputStream out;

	//为了成为全局变量
	String si = new String();								//id
	String sp = new String();								//password
	String name = new String();								//昵称
	JTextField [] t = { new JTextField(15),
						new JTextField(15)
	};														//账号和密码的输入行
	JTextField [] rt = { new JTextField(15),
						 new JTextField(15),
						 new JTextField(15)
	};														//注册界面的输入行
	JButton nb = new JButton("新用户");
	JButton lb = new JButton("登   录");
	JButton rs = new JButton("确   定");
	JButton rr = new JButton("返   回");
	LogIn f;												//登录窗口
	LogIn rf;												//注册窗口
	
	public LogIn (String str) {
		super(str);
	}
	public LogIn() {};
	
	//显示登录界面,提示用户输入账号密码
	public  void log() 
	{
		f = new LogIn("聊天室");								//标题
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);	//设置为可关闭
		f.setSize(260,260);									//大小
		f.setLocation(520,300);								//窗口居中
		
		//放置两个单行文本框,用来输入账号密码
		Container c = f.getContentPane();
		c.setLayout(new FlowLayout());
		c.setBackground(Color.orange);						//背景色
		c.add(new JLabel("用户名:"));
		c.add(t[0]);
		c.add(new JLabel("密    码:"));
		c.add(t[1]);

		//添加新用户和登录按钮
		c.add(nb);
		c.add(lb);
		f.setVisible(true);									//设置为可见
		
		lb.addActionListener(this);							//登录按钮监听
		nb.addActionListener(this); 						//新用户按钮监听
	}
	
	//监听方法的实现
	public void actionPerformed (ActionEvent e) 
	{
		if(e.getSource()==lb)		//登录
		{
			si = t[0].getText();
			sp = t[1].getText();
			if(si.equals("") || sp.equals(""))
				JOptionPane.showMessageDialog(null,
						"用户名或密码不能为空", "提示",
					JOptionPane.INFORMATION_MESSAGE);
			else
			{
				UserIn u = new UserIn(si,sp);
				try {
					client = new Socket("127.0.0.1",6666);		//与本机6666端口建立套接
					in = client.getInputStream();
					out = client.getOutputStream();
					byte [] i = si.getBytes();					//id
					byte [] space = {'/'};						//分隔符
					byte [] p = sp.getBytes();					//密码
					byte [] buf = new byte[i.length+p.length+1];
					System.arraycopy(i, 0, buf, 0, i.length);
					System.arraycopy(space, 0, buf, i.length, 1);
					System.arraycopy(p, 0, buf, i.length+1, p.length);
					out.write(buf);								//把账号密码发给服务器判断
					byte [] check = new byte[100];
					in.read(check);								//接受服务器返回的结构
					String che = new String(check);
					String c1 = new String("1");
					if(che.substring(0, 1).equals(c1))			//若返回1加用户昵称
					{
						u.name = che.substring(1,che.length());	
						f.dispose();;							//删除登录窗口
						Chat c = new Chat(u,client);			//开始聊天
					}
					else										//否则提示账号密码错误
						JOptionPane.showMessageDialog(null,
											"用户名或密码错误", "提示",
											JOptionPane.INFORMATION_MESSAGE);
				}catch(IOException e1) {
					JOptionPane.showMessageDialog(null,
							"服务器故障,登录失败", "提示",
							JOptionPane.ERROR_MESSAGE);
				}
			}

		}
		else if(e.getSource()==nb)			//新用户
		{
			f.setVisible(false);
			register();
		}
		else if(e.getSource()==rs)			//注册界面的确定
		{
			si = rt[0].getText();
			sp = rt[1].getText();
			name = rt[2].getText();
			if(si.equals("") || sp.equals(""))
				JOptionPane.showMessageDialog(null,
						"用户名或密码不能为空", "提示",
					JOptionPane.INFORMATION_MESSAGE);
			else {
				try {
					client = new Socket("111.231.111.50",6666);	//与本机6666端口建立套接
					in = client.getInputStream();
					out = client.getOutputStream();
					
					byte [] tempsi = si.getBytes();					//id
					byte [] space1 = {'/'};							//分隔符
					byte [] tempsp = sp.getBytes();					//密码
					byte [] tempname = name.getBytes();				//昵称
					byte [] tempbuf = new byte[tempsi.length+tempsp.length+tempname.length+3];
					System.arraycopy(space1, 0, tempbuf, 0, 1);
					System.arraycopy(tempsi, 0, tempbuf, 1, tempsi.length);
					System.arraycopy(space1, 0, tempbuf, tempsi.length+1, 1);
					System.arraycopy(tempsp, 0, tempbuf, tempsi.length+2, tempsp.length);
					System.arraycopy(space1, 0, tempbuf, tempsi.length+tempsp.length+2, 1);
					System.arraycopy(tempname, 0, tempbuf, tempsi.length+tempsp.length+3, tempname.length);
					out.write(tempbuf);								//把账号密码发给服务器判断
					byte [] check = new byte[100];
					in.read(check);									//接受服务器返回的结构
					String che = new String(check);
					String c1 = new String("1");
					if(che.substring(0, 1).equals(c1))				//若返回1加用户昵称
					{
						client.close();
						rf.dispose();
						f.setVisible(true);
						JOptionPane.showMessageDialog(null,
								"注册成功,请登录", "提示",
							JOptionPane.INFORMATION_MESSAGE);
					}
					else {
						client.close();
						JOptionPane.showMessageDialog(null,
								"该账号已被占用!", "提示",
							JOptionPane.INFORMATION_MESSAGE);
					}
					
				}catch(IOException e1) {
					JOptionPane.showMessageDialog(null,
							"服务器异常,无法注册", "提示",
						JOptionPane.ERROR_MESSAGE);
				}
				
			}
		}
		else if (e.getSource()==rr)			//返回按钮
		{
			rf.setVisible(false);
			f.setVisible(true);
		}
	}
	
	//注册方法
	public void register() {
		rf = new LogIn("注册");
		rf.setVisible(true);
		rf.setSize(260, 300);
		rf.setLocation(520, 250);
		rf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		Container c = rf.getContentPane();
		c.setLayout(new FlowLayout());
		c.setBackground(Color.pink);				//背景色
		c.add(new JLabel("用户名:"));
		c.add(rt[0]);
		c.add(new JLabel("密    码:"));
		c.add(rt[1]);
		c.add(new JLabel("昵     称:"));
		c.add(rt[2]);
		c.add(rs);
		c.add(rr);
		
		rs.addActionListener(this);					//确定注册按钮监听
		rr.addActionListener(this);					//返回按钮监听
	}
}


//用户信息类
class UserIn {
	String id;
	String passWord;
	String name;
	
	UserIn(String i, String p){
		id = i;
		passWord = p;
	}
	UserIn(String i, String p,String n){
		id = i;
		passWord = p;
		name = n;
	}
	UserIn(){}
}


客户端 Chat类(实现聊天)

package chat;

import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
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 server.UserInfo;

public class Chat extends JFrame implements ActionListener {

	UserIn u = new UserIn();			//登录者
	JButton sent = new JButton("发送");		//发送按钮
	static JComboBox<String> selecttf = new JComboBox<String>();//选择框
	JTextField tf = new JTextField(20);		//输入框
	static JTextArea ta = new JTextArea();	//聊天记录框
	JScrollPane text = new JScrollPane(ta);	//可滚动
	
	JPanel panelh = new JPanel();			//面板对象上
	JPanel paneld = new JPanel();			//面板对象下
	
	Socket client;
	InputStream in;
	OutputStream out;
	
	public Chat() {};
	
	public Chat(UserIn u,Socket client) {
		super("客户端");
		this.u = u;
		this.client = client;
		
		setSize(350,530);					//窗口大小
		setLocation(520,200);				//窗口位置
		Container c = this.getContentPane();	
		
		JSplitPane sp = new JSplitPane(JSplitPane.VERTICAL_SPLIT,panelh,paneld);
		sp.setDividerSize(5);
		panelh.setLayout(null);
		panelh.add(text);
		text.setSize(340,425);
		
		paneld.setLayout(new FlowLayout());
		paneld.add(tf);
		paneld.add(sent);
		paneld.add(new JLabel("    发送给    "));
		paneld.add(selecttf);
		selecttf.addItem("所有人");
		c.add(sp);
		sent.addActionListener(this);		//对发送按钮监听
		
		ta.setEditable(false);				//聊天记录不可编辑
		setVisible(true);
		sp.setDividerLocation(0.8);			//上下比例9:1
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);		//可关闭
		
		try {
			ta.append("成功连接到服务器"+'\n');
			in = client.getInputStream();
			out = client.getOutputStream();
		}catch(IOException e1) {
			JOptionPane.showMessageDialog(null,
					"连接服务器失败", "提示",
				JOptionPane.ERROR_MESSAGE);
		}
		
		//收消息线程开始
		Receive receive = new Receive(client);
		Thread r = new Thread(receive);
		r.start();
	}

	//发消息监听方法实现
	public void actionPerformed (ActionEvent e) {
		String s = tf.getText();
		tf.setText("");
		try {
			String sele = (String)selecttf.getSelectedItem();
			int he = selecttf.getSelectedIndex();
			String hea = String.valueOf(he);
			String na = u.name;
			String me = s;
			String to = " -> ";
			String sp = " : ";
			String mess = hea+na+to+sele+sp+me;
			byte [] buf = mess.getBytes();
			out.write(buf);
			if(he != 0)
				Chat.ta.append(mess.substring(1,mess.length())+'\n');
		}catch(IOException e2) {
			e2.printStackTrace();
		}

	}
}


class Receive extends Thread{
	
	InputStream in;
	OutputStream out;
	
	public Receive(Socket client) {
		try {
			in = client.getInputStream();
			out = client.getOutputStream();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	//收消息线程方法实现
	public void run (){
		while(true) {
			try {
				byte [] buff = new byte[512];			//缓存数组,一次最多512个字节
				in.read(buff);
				String str = new String(buff);
				str = str.trim();
				if(str.charAt(0) == '5')				//5是收到正常的消息
					Chat.ta.append(str.substring(1, str.length())+'\n');
				else if(str.charAt(0) == '9') {			//9更新在线用户
					//System.out.println(str);
					Chat.selecttf.removeAllItems();
					Chat.selecttf.addItem("所有人");
					str = str.substring(1, str.length());
					int i = 0 ,j = 0;
					while(j < str.length()-1) {
						while(str.charAt(j) != '|')		//从'|'处断开
							j++;
						Chat.selecttf.addItem(str.substring(i, j));
						i = j+1;
						j++;
					}
				}	
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

服务器端 Server类(转发消息)

package server;

import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;

public class Server extends JFrame implements ActionListener{
	
	public static List<Socket> list = new ArrayList<Socket>();	//保存socket的集合
	public static List<UserInfo> userlist = new ArrayList<UserInfo>();//保存在线用户

	static JTextArea ta = new JTextArea();	//聊天记录框
	JScrollPane text = new JScrollPane(ta);	//可滚动
	JPanel panel = new JPanel();			//面板对象
	JPanel panel1 = new JPanel();			//用来放置按钮
	JButton jb1 = new JButton ("登录日志");
	JButton jb2 = new JButton ("聊天日志");
	
	ServerSocket server;
	Socket client;
	InputStream in;
	OutputStream out;
	
	public Server() {
		super("服务器");
		setSize(300,400);					//窗口大小
		setLocation(520,250);				//窗口位置
		Container c = getContentPane();
		
		JSplitPane sp = new JSplitPane(JSplitPane.VERTICAL_SPLIT,panel,panel1);	//分割界面用来放按钮
		sp.setDividerSize(5);
		
		panel.setLayout(null);
		panel.add(text);					//窗体上添加聊天记录文本框
		panel.setSize(300,400);				//面板大小
		panel.setLocation(0, 0);			//位置
		text.setSize(300,360);
		
		//JButton jb1 = new JButton ("登录日志");
		//JButton jb2 = new JButton ("聊天日志");
		panel1.setLayout(new FlowLayout());
		panel1.add(jb1);
		panel1.add(jb2);
		jb1.addActionListener(this);
		jb2.addActionListener(this);
		
		c.add(sp);							//窗体添加面板(输入框)
						
		ta.setVisible(true);;				//聊天记录可见
		ta.setEditable(false);				//聊天记录不可编辑
		setVisible(true);
		sp.setDividerLocation(0.9);			//上下9:1
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);		//可关闭
		
		try {
			server = new ServerSocket(6666);
			ta.append("服务器已启动"+'\n');
			while(true) {
				try {
					client = server.accept();					//等待连接
					in = client.getInputStream();
					out = client.getOutputStream();
					byte [] buff = new byte[100];				//缓存数组,一次最多512个字节
					in.read(buff);								//接收消息
					String str = new String(buff);
					str = str.trim();							//去掉多余的空格
					
					if(str.substring(0,1).equals("/"))			//!!用户注册!!
					{
						int i = 1,j = str.length()-1;
						while(str.charAt(i) != '/')		//从'/'处断开
							i++;						//分成三部分
						while(str.charAt(j) != '/')		//账号、密码、昵称
							j--;
						String id = str.substring(1, i);
						String pa = str.substring(i+1, j);
						String na = str.substring(j+1, str.length());
						UserInfo u = new UserInfo();
						UserInfo user = new UserInfo(id,pa,na);
						if(!u.checkid(user))			//注册成功
						{
							u.save(user);				//保存该用户信息
							byte [] re = {'1'};			//返回给客户端1
							out.write(re);
						}
						else							//注册失败
						{
							byte [] re = {'0'};			//返回给客户端0
							out.write(re);
						}
					}
					else										//!!!用户登录!!!
					{
						int item = str.indexOf("/");
						String id = str.substring(0,item);
						String pa = str.substring(item+1,str.length());
						UserInfo u = new UserInfo();
						UserInfo user = new UserInfo(id,pa);
						if(u.check(user))					//判断合法
						{
							Iterator it = list.iterator();
							int itera = 0;
							while(it.hasNext()) {
								it.next();
								itera++;
							}
							if(itera <= 10) {	//限制当前在线用户人数5人
							byte [] b1 = {'1'};
							byte [] b2 = user.name.getBytes();
							byte [] b3 = new byte[1+b2.length];
							System.arraycopy(b1, 0, b3, 0, 1);
							System.arraycopy(b2, 0, b3, 1, b2.length);
							out.write(b3);				//返回1加该用户昵称
							Server.list.add(client);
							Server.userlist.add(user);
							u.saveLog(user, client.getInetAddress().toString());	//用户登录信息的保存
							ta.append(user.name+"上线了"+'\n');
							
							new Sent(client, user).start();	//启动对该用户的转发消息线程
							}
						}
						else									//判断不合法
						{
							byte [] b4 = {'0'};
							out.write(b4);					//返回0(实际上客户端并不对0进行判断)
						}
					}
					
				}catch(IOException e) {
				}
			}
		}catch(IOException e) {
			
		}
	}

	//两个查看日志的按钮监听方法实现
	public void actionPerformed (ActionEvent e) {
		JTextArea rzta = new JTextArea();	//聊天记录框
		JScrollPane rztext = new JScrollPane(rzta);	//可滚动
		rzta.setVisible(true);
		rzta.setEditable(false);
		
		JFrame rz = new JFrame ("日志信息");
		rz.setSize(300,400);
		rz.setLocation(520,250);
		
		JPanel rzp = new JPanel();
		rzp.setSize(300,400);
		rzp.setVisible(true);
		rzp.setLayout(null);
		rzp.add(rztext);					//窗体上添加聊天记录文本框
		rzp.setLocation(0, 0);			    //位置
		rztext.setSize(300,365);
		
		rz.add(rzp);
		rz.setVisible(true);
		
		try {
			FileReader fr;
			if(e.getSource() == jb1)
				fr = new FileReader("D:\\java-photon\\uesrlog.txt");
			else 
				fr = new FileReader("D:\\java-photon\\uesrmessage.txt");
			BufferedReader br = new BufferedReader(fr);
			String str;
			while (br.ready()) {				//从文件中读取已有的账户
				str = br.readLine();
				rzta.append(str+'\n');
			}
			br.close();							//关闭字符流
		} catch (IOException e1) {
			JOptionPane.showMessageDialog(null,
					"找不到指定文件", "错误",
				JOptionPane.ERROR_MESSAGE);
		}
	}
	//服务器程序开始运行
	public static void main(String []args) {
		Server s = new Server();
	}
	
}

//转发消息线程
class Sent extends Thread{
	
	InputStream in;
	OutputStream out;
	Socket client;
	UserInfo user;
	public Sent(Socket client,UserInfo user) {
		super();
		this.client = client;
		this.user = user;
	}
	public void run() {
		try {
			String strin = "";
			for(UserInfo us:Server.userlist)
				strin = strin+us.name+"|";
			byte [] head = {'9'};
			byte [] nam = strin.getBytes();
			byte [] update = new byte[nam.length+1];
			System.arraycopy(head, 0, update, 0, 1);
			System.arraycopy(nam, 0, update, 1, nam.length);
			for(Socket s:Server.list)
			{
				out = s.getOutputStream();
				out.write(update);
			}
			while(true) {
				in = client.getInputStream();
				byte [] buff = new byte[512];		//缓存数组,一次最多512个字节
				in.read(buff);
				String str = new String(buff);
				str = str.trim();
				user.saveMessage(str.substring(1, str.length()));
				byte [] head1 = {'5'};
				byte [] mess = str.substring(1, str.length()).getBytes();
				System.arraycopy(head1, 0, buff, 0, 1);
				System.arraycopy(mess, 0, buff, 1, mess.length);
				if(str.charAt(0) == '0')
					for(Socket s:Server.list)
					{
						out = s.getOutputStream();
						out.write(buff);
					}
				else {
					int i = str.charAt(0);
					Socket s2 = Server.list.get(i-49);
					out = s2.getOutputStream();
					out.write(buff);
				}
			}
			} catch (IOException e) {
				Server.ta.append(user.name+"离开了\n");
				Server.list.remove(client);
				Server.userlist.remove(user);
				String strin1 = "";
				for(UserInfo us:Server.userlist)
					strin1 = strin1 + us.name+"|";
				byte [] head2 = {'9'};
				byte [] nam1 = strin1.getBytes();
				byte [] update1 = new byte[nam1.length+1];
				System.arraycopy(head2, 0, update1, 0, 1);
				System.arraycopy(nam1, 0, update1, 1, nam1.length);
				try {
					for(Socket s1:Server.list)
				{
					out = s1.getOutputStream();
					out.write(update1);
				}
				}catch(IOException e2) {
					
				}
			}
	}
	
}

服务器端 UserInfo类(保存用户信息及日志)

package server;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.Vector;

import javax.swing.JOptionPane;

public class UserInfo {

	String id;
	String passWord;
	String name;
	Vector<UserInfo> v = new Vector<UserInfo>();
	Calendar time;
	
	UserInfo(String i, String p){
		id = i;
		passWord = p;
	}
	UserInfo(String i, String p,String n){
		id = i;
		passWord = p;
		name = n;
	}
	public UserInfo(){}
	
	//判断用户名及对应密码是否存在
	public boolean check(UserInfo user) {
		try {
			FileReader fr = new FileReader("D:\\java-photon\\user.txt");
			BufferedReader br = new BufferedReader(fr);
			String str;
			while (br.ready()) {				//从文件中读取已有的账户
				str = br.readLine();
				String []s = str.split(" ");	//按照空格分成账号、密码、昵称三部分
				UserInfo u = new UserInfo(s[0],s[1],s[2]);
				v.add(u);
					
			}
			br.close();							//关闭字符流
		} catch (IOException e) {
			e.printStackTrace();
		}
		for(int i=0;i<v.size();i++) {
			if(user.id.equals(v.get(i).id) && user.passWord.equals(v.get(i).passWord))
			{
				user.name = v.get(i).name;
				return true;
			}
							
		}
		return false;
	}
	
	//判断用户名是否重复
	public boolean checkid (UserInfo user) {
		try {
			FileReader fr = new FileReader("D:\\java-photon\\user.txt");
			BufferedReader br = new BufferedReader(fr);
			String str;
			while (br.ready()) {				//从文件中读取已有的账户
				str = br.readLine();
				String []s = str.split(" ");	//分成三部分
				UserInfo u = new UserInfo(s[0],s[1],s[2]);
				v.add(u);
					
			}
			br.close();							//关闭字符流
		} catch (IOException e) {
			e.printStackTrace();
		}
		for(int i=0;i<v.size();i++) {
			if(user.id.equals(v.get(i).id))		//判断用户名是否重复
			{
				user.name = v.get(i).name;
				return true;
			}
							
		}
		return false;
	}
	
	//保存新用户信息
	public void save (UserInfo user) {
		v.add(user);
		try {
			FileWriter fr = new FileWriter("D:\\java-photon\\user.txt");
			BufferedWriter br = new BufferedWriter(fr);
			for(int i=0;i<v.size();i++)
			{
				br.write(v.get(i).id+' '+v.get(i).passWord+' '+v.get(i).name);
				br.newLine();
			}
			br.close();
		}catch(IOException e) {
			JOptionPane.showMessageDialog(null,
					"用户信息保存失败!", "提示",
				JOptionPane.ERROR_MESSAGE);
		}
	}
	
	//用户登录日志
	public void saveLog (UserInfo user,String ip) {
		try {
			Calendar c = Calendar.getInstance();
			String s = String.format("%1$ty-%1$tm-%1$td", c);
			FileWriter fr = new FileWriter("D:\\java-photon\\uesrlog.txt",true);	//在文件末尾追加写入
			BufferedWriter br = new BufferedWriter(fr);
			br.write("登录"+"          "+ip+"      "+user.name+"      "+user.id+"              "+s);
			br.newLine();
			br.close();
		}catch(IOException e) {
			JOptionPane.showMessageDialog(null,
					"用户登录日志保存失败!", "提示",
				JOptionPane.ERROR_MESSAGE);
		}
	}
	
	//保存用户发送的信息
	public void saveMessage (String message ) {
		try {
			Calendar c = Calendar.getInstance();
			String s = String.format("%1$ty-%1$tm-%1$td", c);
			FileWriter fr = new FileWriter("D:\\java-photon\\uesrmessage.txt",true);	//在文件末尾追加写入
			BufferedWriter br = new BufferedWriter(fr);
			br.write(message+"   "+s);
			br.newLine();
			br.close();
		}catch(IOException e) {
			JOptionPane.showMessageDialog(null,
					"用户消息保存失败!", "提示",
				JOptionPane.ERROR_MESSAGE);
		}
	}
}

以上为全部内容

因为ip地址的原因,这只能在同一个局域网下实现通信。但是我想让它实现全网通信,于是我以学生价租了一个月的腾讯云服务器,获得了一个公有IP,并将服务器端程序搬到云服务器上,客户端与云服务器套接(IP改成云服务器的IP就行),最后成功的实现了和室友(那时是寒假,我们在不同省)跨省通信!哈哈哈,当时心情还是有点激动的。

整个实现过程我不再赘述,感兴趣的可以自己百度,下面附上几点我在做的过程中遇到的坑。

  1. 宝塔面板
    宝塔面板可以对云服务器的磁盘文件进行可视化操作,还可以可视化安装各种常用软件,很直观、方便,对新手很友好。可我在安装的时候(通过putty连接,命令行操作)一直失败,最后通过重装带有宝塔面板的系统(腾讯云官网),感觉是成功了,可是用微软edge访问面板时显示无法访问。 遂换用qq浏览器,成功了。

  2. 云服务器端口
    把java源文件上传到云服务器,然后也编译成功并运行了,可客户端就是无法连接到服务器。检查了安全组也放通了,心态差点要崩,最后百度发现云服务器还有一层防火墙,遂关闭防火墙,成功连接!

  3. 中文乱码
    java的一个字符(char)是两个字节,包括中文,按说不应该乱码,而且我在自己电脑上运行也没有乱码,为什么把服务器端程序放到云服务器上就乱码了呢(英文正常,中文乱码),百度后发现windows使用GBK编码,而云服务器的centos系统(linux的一种)并不是GBK,因此造成中文乱码。所以服务器程序强制使用GBK就好了。

byte[] array = str.getBytes("GBK");
Striing newstr = new String(array,"GBK");
  1. 把程序打包成exe文件
    为了能让客户端程序脱离编译器和jre环境运行,我想把它打包成exe文件,百度了一下还真有这样的工具,按照教程打包成exe文件后我自己运行没问题,可在我同学的电脑上(没有安装jre)就是不行。后来发现还是缺少jre的问题,即使打包成了exe,还是要把jre放在exe同一目录下,才能正常运行。

实现跨局域网通信客户端只用改套接字的IP地址就行了,但服务器端程序做了一些改动(去除图形用户界面,把两个源文件合并成一个),如果有人想要的话我再发。

评论 11 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:书香水墨 设计师:CSDN官方博客 返回首页

打赏作者

南小呗

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值