如果之前没有接触过通信方面的知识,请移步https://blog.csdn.net/S_G_G/article/details/113482830。
在有了初步尝试之后,我们可以试着写一个聊天室。
其实只多了一个问题要解决:并发。
我们的基本思路是:
1、服务器端接受客户部分另写一个继承于Thread类的Receiver类来实现。
2、服务器端,对于每个客户,都建立一个NetConn对象(也继承于Thread类)来实现与客户的通信。
3、每个客户端也建立一个NetConn对象(与服务器端的NetConn对象不同)来实现与服务器端的通信。
这样就能保证服务器端接受客户和传输数据不冲突,客户之间与服务器通信也不冲突。
由于服务器端发送数据时要发送给所有客户,所以我用一个ArrayList来存所有的NetConn对象,并且将这个ArrayList专门放在ClientList类中。
用swing的JTextField组件来实现编辑和获取要发送的消息的功能。
用swing的JTextArea组件来实现显示接收到的消息的功能。
服务器端的流程图如下:
服务器端Server类(主类)代码
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.ServerSocket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class Server{
private ServerSocket ss;
private JTextArea ta;
private Server(){ //服务器端界面初始化
JFrame jf=new JFrame("聊天室-服务器");
jf.setSize(500, 500);
JButton btn=new JButton("发送");
jf.add(btn);
JTextField tf=new JTextField(20);
ta=new JTextArea(20,30);
jf.add(tf);
jf.add(ta);
jf.setDefaultCloseOperation(3);
jf.setLayout(new FlowLayout());
jf.setVisible(true);
btn.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
String msg=tf.getText();
ClientList.send(msg);
tf.setText("");
}
});
this.work();
}
private void work(){ //核心代码
try {
ss=new ServerSocket(9999);
Receiver r=new Receiver(ss,ta);
r.start();
} catch (IOException e) {e.printStackTrace();}
}
public static void main(String[] args){
Server s1=new Server();
}
}
服务器端Receiver类代码
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JTextArea;
public class Receiver extends Thread{
private ServerSocket ss;
private JTextArea ta;
Receiver(ServerSocket s,JTextArea t){
ss=s;
ta=t;
}
public void run(){
while(true){ //不停地接收客户,建立NetConn对象并加入ArrayList中
try {
Socket client=ss.accept();
NetConn nc=new NetConn(client,ta);
nc.start();
ClientList.list.add(nc);
}
catch (IOException e) { e.printStackTrace();}
}
}
}
服务器端NetConn类代码
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import javax.swing.JTextArea;
public class NetConn extends Thread{
private Socket client;
private JTextArea ta;
private DataInputStream dis;
private DataOutputStream dos;
NetConn(Socket s,JTextArea t){
client=s;
ta=t;
}
public void send(String msg){ //向该客户发送消息
byte[] data=msg.getBytes();
int l=data.length;
try {
dos.writeInt(l);
dos.write(data);
}
catch (IOException e) {e.printStackTrace();}
}
public void run(){ //接收该客户发来的消息
try {
dis=new DataInputStream(client.getInputStream());
dos=new DataOutputStream(client.getOutputStream());
while (true){
int l=dis.readInt();
byte[] data=new byte[l];
dis.read(data);
String msg=new String(data);
ta.append(msg+"\r\n");
}
}
catch (IOException e) {e.printStackTrace();}
}
}
服务器端ClientList类代码
import java.util.ArrayList;
public class ClientList {
public static ArrayList<NetConn> list=new ArrayList();
public static void send(String s){
for (int i=list.size()-1;i>=0;i--){
NetConn nc=list.get(i);
nc.send(s);
}
}
}
客户端Client类(主类)代码
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class Client {
private void work(){ //客户端界面初始化
JFrame jf=new JFrame("聊天室-客户端");
jf.setSize(500, 500);
JButton btn=new JButton("发送");
jf.add(btn);
JTextField tf=new JTextField(20);
JTextArea ta=new JTextArea(20,30);
jf.add(tf);
jf.add(ta);
jf.setDefaultCloseOperation(3);
jf.setLayout(new FlowLayout());
jf.setVisible(true);
NetConn nc=new NetConn(ta); //建立该客户对应的NetConn对象
if (!nc.connect()) return; //将连接服务器部分写在NetConn类里便于判断要不要继续执行下面的代码
nc.start();
btn.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
String msg=tf.getText();
nc.send(msg);
tf.setText("");
}
});
}
public static void main(String[] args){
Client c=new Client();
c.work();
}
}
客户端NetConn类代码
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.JTextArea;
public class NetConn extends Thread{
private Socket s;
private JTextArea ta;
private DataInputStream dis;
private DataOutputStream dos;
NetConn(JTextArea t){
ta=t;
}
public void send(String msg){ //向服务器发送消息
byte[] data=msg.getBytes();
int l=data.length;
try {
dos.writeInt(l);
dos.write(data);
}
catch (IOException e) {e.printStackTrace();}
}
public void run(){ //接收服务器发来的消息
try {
while (true){
int l=dis.readInt();
byte[] data=new byte[l];
dis.read(data);
String msg=new String(data);
ta.append(msg+"\r\n");
}
}
catch (IOException e) {e.printStackTrace();}
}
public boolean connect(){
try {
s=new Socket("192.168.0.102",9999);
dis=new DataInputStream(s.getInputStream());
dos=new DataOutputStream(s.getOutputStream());
return true;
}
catch (UnknownHostException e) {e.printStackTrace();}
catch (IOException e) {e.printStackTrace();}
return false;
}
}