Java最后一道作业题竟然是写个多线程聊天工具。以前在一次实训中做过linux版的。当时就有人说Java版的实现比较简单,如今看来确实有理。尤其是在做UI上,不用像gtk那么麻烦。先将我搜到的资源与大家共享(亲测可用):
该网络聊天程序大致分为三个主要部分:客户端、服务器端和用户图形界面。各个部分的初步设计思想、流程及存储结构如下:
1. 程序整体框架:主程序监听一端口,等待客户接入;同时构造一个线程类,准备接管会话。当一个Socket会话产生后,将这个会话交给线程处理,然后主程序继续监听。
打开Socket |
命 名 |
监听端口 |
建立连接 |
收发消息 |
关闭连接 |
打开Socket
|
连接服务器 |
收发消息 |
关闭连接 |
服务器端程序 |
客户端程序 |
2. 客户端(Client)
客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。客户端不需要指定打开的端口,通常临时的、动态的分配一个端口。
3. 服务器端(Server)
服务器端,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。
4. 用户图形界面
用户图形界面方便程序与用户的交互,多个用户参加,完成会话功能,具体的设计要方便用户的使用,直观清晰,简洁明了,友好美观。
5. 存储结构
下面列出主要存储结构或变量:
存储结构、变量、对象 | 类型 | 说明 |
post | InetAddress | 标识IP地址 |
Port | int | 标识端口 |
Server [ ] | ServerThread | 服务器端连接数 |
Client [ ] | Socket | 客户端连接数 |
Client(String ip,int p,Face chat) | public | Client类成员函数 |
Public void run() | Void | Client、Server类成员函数 |
Server(int port,Face chat) | public | Server类成员函数 |
Face() | Public | Face类成员函数 |
1.服务器端
服务器端主要是使用ServerSocket类,相当于服务器Socket,用来监听试图进入的连接,当新的连接建立后,该类为他们实例化一个Socket对象,同时得到输入输出流,调用相应方法完成会话。
具体代码如下:
- package nupt.java.socket;
- import java.awt.*;
- import java.net.*;
- import java.io.*;
- public class Server extends Thread {
- ServerSocket skt; // ServerSocket类监听进入的连接,为每个新的连接产生一个Socket对象
- Socket Client[ ]=new Socket[10];
- Socket Client1=null;
- int i = 0;
- TextArea in;
- int port,k=0,l=0;
- PrintStream theOutputStream;
- Face chat;
- public Server(int port, Face chat) {
- try {
- this.port = port;
- skt = new ServerSocket(port);
- this.chat = chat;
- } catch (IOException e) {
- chat.ta.append(e.toString());
- }
- }
- public void run() {
- chat.ta.append("等待连线......");
- while (true) {
- try {
- Client[k] = skt.accept();
- //当有客户端连接时就新建一个子线程
- if (i < 2) {
- ServerThread server[] = new ServerThread[10];
- server[k]= new ServerThread(Client[k], this.chat, i);
- l=server.length;
- server[k].start();
- chat.ta.append(“客户端“+ Client[k].getInetAddress() + "已连线\n");
- //for(int j=0;j<server.length;j++)
- theOutputStream = new PrintStream(server[k].getClient().getOutputStream());
- i = server[k].getI();
- k++;
- } else {
- //theOutputStream = new PrintStream(null);
- }
- } catch (SocketException e) {
- } catch (IOException e) {
- chat.ta.append(e.toString());
- }
- }
- }
- public void dataout(String data) {
- //for(int j=0;j<l;j++)
- theOutputStream.println(data);
- }
- }
- class ServerThread extends Thread {
- ServerSocket skt;
- Socket Client;
- TextArea in;
- int port,i;
- BufferedReader theInputStream;
- PrintStream theOutputStream;
- String readin;
- Face chat;
- //服务端子线程
- public ServerThread(Socket s, Face chat, int i) {
- this.i = ++i;
- Client = s;
- this.chat = chat;
- }
- public int getI() {
- return this.i;
- }
- public Socket getClient() {
- return this.Client;
- }
- public void run() {
- try {
- theInputStream = new BufferedReader(new InputStreamReader(Client
- .getInputStream()));
- theOutputStream = new PrintStream(Client.getOutputStream());
- while (true) {
- readin = theInputStream.readLine();
- chat.ta.append(readin + "\n");
- }
- } catch (SocketException e) {
- chat.ta.append("连线中断!\n");
- // 设置组件可用性
- chat.clientBtn.setEnabled(true);
- chat.serverBtn.setEnabled(true);
- chat.tfaddress.setEnabled(true);
- chat.tfport.setEnabled(true);
- try {
- i - -;
- skt.close();
- Client.close();
- } catch (IOException err) {
- chat.ta.append(err.toString());
- }
- } catch (IOException e) {
- chat.ta.append(e.toString());
- }
- }
- public void dataout(String data) {
- theOutputStream.println(data);
- }
- }
package nupt.java.socket;
import java.awt.*;
import java.net.*;
import java.io.*;
public class Server extends Thread {
ServerSocket skt; // ServerSocket类监听进入的连接,为每个新的连接产生一个Socket对象
Socket Client[ ]=new Socket[10];
Socket Client1=null;
int i = 0;
TextArea in;
int port,k=0,l=0;
PrintStream theOutputStream;
Face chat;
public Server(int port, Face chat) {
try {
this.port = port;
skt = new ServerSocket(port);
this.chat = chat;
} catch (IOException e) {
chat.ta.append(e.toString());
}
}
public void run() {
chat.ta.append("等待连线......");
while (true) {
try {
Client[k] = skt.accept();
//当有客户端连接时就新建一个子线程
if (i < 2) {
ServerThread server[] = new ServerThread[10];
server[k]= new ServerThread(Client[k], this.chat, i);
l=server.length;
server[k].start();
chat.ta.append(“客户端“+ Client[k].getInetAddress() + "已连线\n");
//for(int j=0;j<server.length;j++)
theOutputStream = new PrintStream(server[k].getClient().getOutputStream());
i = server[k].getI();
k++;
} else {
//theOutputStream = new PrintStream(null);
}
} catch (SocketException e) {
} catch (IOException e) {
chat.ta.append(e.toString());
}
}
}
public void dataout(String data) {
//for(int j=0;j<l;j++)
theOutputStream.println(data);
}
}
class ServerThread extends Thread {
ServerSocket skt;
Socket Client;
TextArea in;
int port,i;
BufferedReader theInputStream;
PrintStream theOutputStream;
String readin;
Face chat;
//服务端子线程
public ServerThread(Socket s, Face chat, int i) {
this.i = ++i;
Client = s;
this.chat = chat;
}
public int getI() {
return this.i;
}
public Socket getClient() {
return this.Client;
}
public void run() {
try {
theInputStream = new BufferedReader(new InputStreamReader(Client
.getInputStream()));
theOutputStream = new PrintStream(Client.getOutputStream());
while (true) {
readin = theInputStream.readLine();
chat.ta.append(readin + "\n");
}
} catch (SocketException e) {
chat.ta.append("连线中断!\n");
// 设置组件可用性
chat.clientBtn.setEnabled(true);
chat.serverBtn.setEnabled(true);
chat.tfaddress.setEnabled(true);
chat.tfport.setEnabled(true);
try {
i - -;
skt.close();
Client.close();
} catch (IOException err) {
chat.ta.append(err.toString());
}
} catch (IOException e) {
chat.ta.append(e.toString());
}
}
public void dataout(String data) {
theOutputStream.println(data);
}
}
2.客户端
客户端主要是使用Socket类,该类是JAVA实现网络编程重要的基础类,实现程序间双向的面向连接的通信。调用public Socket(String host,int port)方法设定IP和端口。建好连接后,用户通过得到Socket的输入输出流对象后,利用流的方法实现数据的传输。调用public InputStream getInputStream()和public OutputStream getOutputStream()方法,分别得到Socket对象的输入输出流;
具体实现代码如下:
- package nupt.java.socket;
- import java.net.*;
- import java.io.*;
- import javax.swing.Timer;
- public class Client extends Thread {
- Socket skt; // 用于客户端的连接
- InetAddress host; // 主机地址
- int port; // 端口号
- BufferedReader theInputStream;
- PrintStream theOutputStream;
- String readin;
- Face chat;
- public Client(String ip, int p, Face chat) {
- try {
- host = InetAddress.getByName(ip); // 获取IP地址
- port = p; // 获取端口号
- this.chat = chat;
- } catch (IOException e) {
- chat.ta.append(e.toString());
- }
- }
- public void run() {
- try {
- chat.ta.append("准备连线,稍后!");
- skt = new Socket(host, port); // 新建Socket对象
- chat.ta.append("成功\n"); // 缓冲区末尾添加字符串
- theInputStream = new BufferedReader(new InputStreamReader(skt.getInputStream()));
- theOutputStream = new PrintStream(skt.getOutputStream());
- while (true) {
- readin = theInputStream.readLine();
- chat.ta.append(readin + "\n");
- }
- } catch (SocketException e) {
- chat.ta.append("未连上!\n");
- chat.clientBtn.setEnabled(true);
- chat.serverBtn.setEnabled(true);
- chat.tfaddress.setEnabled(true);
- chat.tfport.setEnabled(true);
- try {
- skt.close();
- } catch (IOException err) {
- chat.ta.append(err.toString());
- }
- } catch (IOException e) {
- chat.ta.append(e.toString());
- }
- }
- public void dataout(String data) {
- theOutputStream.println(data);
- }
- }
package nupt.java.socket;
import java.net.*;
import java.io.*;
import javax.swing.Timer;
public class Client extends Thread {
Socket skt; // 用于客户端的连接
InetAddress host; // 主机地址
int port; // 端口号
BufferedReader theInputStream;
PrintStream theOutputStream;
String readin;
Face chat;
public Client(String ip, int p, Face chat) {
try {
host = InetAddress.getByName(ip); // 获取IP地址
port = p; // 获取端口号
this.chat = chat;
} catch (IOException e) {
chat.ta.append(e.toString());
}
}
public void run() {
try {
chat.ta.append("准备连线,稍后!");
skt = new Socket(host, port); // 新建Socket对象
chat.ta.append("成功\n"); // 缓冲区末尾添加字符串
theInputStream = new BufferedReader(new InputStreamReader(skt.getInputStream()));
theOutputStream = new PrintStream(skt.getOutputStream());
while (true) {
readin = theInputStream.readLine();
chat.ta.append(readin + "\n");
}
} catch (SocketException e) {
chat.ta.append("未连上!\n");
chat.clientBtn.setEnabled(true);
chat.serverBtn.setEnabled(true);
chat.tfaddress.setEnabled(true);
chat.tfport.setEnabled(true);
try {
skt.close();
} catch (IOException err) {
chat.ta.append(err.toString());
}
} catch (IOException e) {
chat.ta.append(e.toString());
}
}
public void dataout(String data) {
theOutputStream.println(data);
}
}
3.用户图形界面
该部分主要是完成界面的初始化,合理布局组件,方便用户交互。主要是JAVA按钮,文本域,标签,布局管理器的使用。主要处理了键盘Enter消息接受,下面是实现代码:- package nupt.java.socket;
- import java.awt.*;
- import java.awt.event.*;
- public class Face extends Frame {
- private static final long serialVersionUID = 1L;
- Button clientBtn, serverBtn;
- TextArea ta;
- TextField tfaddress, tfport, tftype;
- Label lbl1,lbl2,lbl3;
- int port;
- Client client;
- Server server;
- boolean iamserver;
- static Face frm;
- public Face() {
- // 实例化组件
- clientBtn = new Button("客户端");
- serverBtn = new Button("服务器");
- ta = new TextArea("", 10, 50, TextArea.SCROLLBARS_BOTH);
- lbl1 = new Label("IP地址:");
- tfaddress = new TextField("192.168.1.104", 10);
- lbl2 = new Label("端口:");
- tfport = new TextField("8080");
- lbl3 = new Label("发送内容:");
- tftype = new TextField(40);
- tftype.addKeyListener(new TFListener());
- ta.setEditable(false);
- //向容器中加入以上组件
- setLayout(new FlowLayout());
- add(lbl1);
- add(tfaddress);
- add(lbl2);
- add(tfport);
- add(clientBtn);
- add(serverBtn);
- add(ta);
- add(lbl3);
- add(tftype);
- //设置格式
- setLocation(400, 250); //窗口显示再屏幕的位置坐标
- setSize(400, 300); //设置窗体大小
- setTitle("基于Socket和多线程编程的聊天程序");
- this.setVisible(true); //设置窗体可见
- //事件响应
- clientBtn.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- port = Integer.parseInt(tfport.getText());
- client = new Client(tfaddress.getText(), port, frm);
- client.start();
- tfaddress.setEnabled(false);
- tfport.setEnabled(false);
- serverBtn.setEnabled(false);
- clientBtn.setEnabled(false);
- }
- });
- serverBtn.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- port = Integer.parseInt(tfport.getText());
- server = new Server(port, frm);
- server.start();
- iamserver = true;
- tfaddress.setText("成为服务器");
- tfaddress.setEnabled(false);
- tfport.setEnabled(false);
- serverBtn.setEnabled(false);
- clientBtn.setEnabled(false);
- }
- });
- addWindowListener(new WindowAdapter() {
- public void windowClosing(WindowEvent e) {
- System.exit(0);
- }
- });
- }
- public static void main(String[] args) { //主方法
- // TODO Auto-generated method stub
- frm = new Face();
- }
- private class TFListener implements KeyListener {
- public void keyPressed(KeyEvent e) {
- if (e.getKeyCode() == KeyEvent.VK_ENTER) { //按Enter输出显示聊天内容
- ta.append(">" + tftype.getText() + "\n");
- if (iamserver)
- server.dataout(tftype.getText());
- else
- client.dataout(tftype.getText());
- tftype.setText("");
- }
- }
- public void keyTyped(KeyEvent e) {
- }
- public void keyReleased(KeyEvent e) {
- }
- }
- }
package nupt.java.socket;
import java.awt.*;
import java.awt.event.*;
public class Face extends Frame {
private static final long serialVersionUID = 1L;
Button clientBtn, serverBtn;
TextArea ta;
TextField tfaddress, tfport, tftype;
Label lbl1,lbl2,lbl3;
int port;
Client client;
Server server;
boolean iamserver;
static Face frm;
public Face() {
// 实例化组件
clientBtn = new Button("客户端");
serverBtn = new Button("服务器");
ta = new TextArea("", 10, 50, TextArea.SCROLLBARS_BOTH);
lbl1 = new Label("IP地址:");
tfaddress = new TextField("192.168.1.104", 10);
lbl2 = new Label("端口:");
tfport = new TextField("8080");
lbl3 = new Label("发送内容:");
tftype = new TextField(40);
tftype.addKeyListener(new TFListener());
ta.setEditable(false);
//向容器中加入以上组件
setLayout(new FlowLayout());
add(lbl1);
add(tfaddress);
add(lbl2);
add(tfport);
add(clientBtn);
add(serverBtn);
add(ta);
add(lbl3);
add(tftype);
//设置格式
setLocation(400, 250); //窗口显示再屏幕的位置坐标
setSize(400, 300); //设置窗体大小
setTitle("基于Socket和多线程编程的聊天程序");
this.setVisible(true); //设置窗体可见
//事件响应
clientBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
port = Integer.parseInt(tfport.getText());
client = new Client(tfaddress.getText(), port, frm);
client.start();
tfaddress.setEnabled(false);
tfport.setEnabled(false);
serverBtn.setEnabled(false);
clientBtn.setEnabled(false);
}
});
serverBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
port = Integer.parseInt(tfport.getText());
server = new Server(port, frm);
server.start();
iamserver = true;
tfaddress.setText("成为服务器");
tfaddress.setEnabled(false);
tfport.setEnabled(false);
serverBtn.setEnabled(false);
clientBtn.setEnabled(false);
}
});
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public static void main(String[] args) { //主方法
// TODO Auto-generated method stub
frm = new Face();
}
private class TFListener implements KeyListener {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) { //按Enter输出显示聊天内容
ta.append(">" + tftype.getText() + "\n");
if (iamserver)
server.dataout(tftype.getText());
else
client.dataout(tftype.getText());
tftype.setText("");
}
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
}
如有不对的地方或者更好的方法,欢迎大家批评指正,交流学习。