JAVA简单聊天室的实现 局域网内以及跨局域网通信
期末的课程设计,做的并不够好,写在这里记录一下自己的成长,也欢迎大家给出宝贵意见
下面程序只能在局域网内通信,要想跨网通信需要有公有IP,文末有介绍。
先放效果图:
# 主要知识点
网络编程 GUI(图形用户界面) 多线程
#实现功能
客户端:注册 登录 群聊 私聊
服务器端:显示用户上线/下线 登录日志及聊天信息的保存及查看
主要思路:
- 登录及注册功能的实现
客户端先与服务器端建立TCP/IP连接,从文本框获取用户名、密码,并按照一定格式分隔开,发送给服务器,后者读取本地磁盘中保存的用户信息,并与之比对,存在该用户名则返回“1”表示登录成功,否则返回“0”。 - 群聊与私聊
服务器端保存所有已建立套接的用户,(每当有新用户上线,服务器都会返回当前所有在线用户的昵称,客户端收到后更新自己的列表)收到群发消息后就一次发给所有在线用户,私聊信息就只发送给特定用户。这又会产生两个问题:
(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就行),最后成功的实现了和室友(那时是寒假,我们在不同省)跨省通信!哈哈哈,当时心情还是有点激动的。
整个实现过程我不再赘述,感兴趣的可以自己百度,下面附上几点我在做的过程中遇到的坑。
- 宝塔面板
宝塔面板可以对云服务器的磁盘文件进行可视化操作,还可以可视化安装各种常用软件,很直观、方便,对新手很友好。可我在安装的时候(通过putty连接,命令行操作)一直失败,最后通过重装带有宝塔面板的系统(腾讯云官网),感觉是成功了,可是用微软edge访问面板时显示无法访问。 遂换用qq浏览器,成功了。 - 云服务器端口
把java源文件上传到云服务器,然后也编译成功并运行了,可客户端就是无法连接到服务器。检查了安全组也放通了,心态差点要崩,最后百度发现云服务器还有一层防火墙,遂关闭防火墙,成功连接! - 中文乱码
java的一个字符(char)是两个字节,包括中文,按说不应该乱码,而且我在自己电脑上运行也没有乱码,为什么把服务器端程序放到云服务器上就乱码了呢(英文正常,中文乱码),百度后发现windows使用GBK编码,而云服务器的centos系统(linux的一种)并不是GBK,因此造成中文乱码。所以服务器程序强制使用GBK就好了。
byte[] array = str.getBytes("GBK");
Striing newstr = new String(array,"GBK");
- 把程序打包成exe文件
为了能让客户端程序脱离编译器和jre环境运行,我想把它打包成exe文件,百度了一下还真有这样的工具,按照教程打包成exe文件后我自己运行没问题,可在我同学的电脑上(没有安装jre)就是不行。后来发现还是缺少jre的问题,即使打包成了exe,还是要把jre放在exe同一目录下,才能正常运行。
实现跨局域网通信客户端只用改套接字的IP地址就行了,但服务器端程序做了一些改动(去除图形用户界面,把两个源文件合并成一个),如果有人想要的话我再发。