我们在写一个聊天室的小项目时候,需要有客户端和服务器端;并且他们必须分开开发,不能有任何互相调用的语句出现!(并且运行的时候先运行服务端(只能一个),然后可以运行多个客户端)
一般我们先写好服务端:
package 聊天室服务端;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class MyServer {
public static List<Cthread> list=new ArrayList<Cthread>();
public static void main(String[] args) {
try {
ServerSocket serversocket=new ServerSocket(9090);//创建一个服务端,端口号待会客户端连接要用
System.out.println("服务端已经打开!等待连接....");
while(true){//这里是一个无限循环,不停的等待着客户端来连接
Socket socket=serversocket.accept();//这是一个堵塞方法,没人连接就不会往下面运行
Cthread ct = new Cthread(socket);//每连上一个客户端,我就给他创建一个线程,专门去接收它的信息,以及广播给其他人
ct.start();
list.add(ct);//把线程都添加到一个列表,方便服务端管理
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
服务端还需要接收消息,还有广播消息的功能,所以没当连上一个客户端的时候,我们就给新建一个线程来专门接收它的消息,以及广播给其他人(客户端不与其他客户端有联系,所有信息发送给服务端,然后由它广播)
package 聊天室服务端;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.SocketAddress;
/**
* ,专门去接收它的信息,以及广播给其他人
* @author yan
*
*/
public class Cthread extends Thread{
Socket socket;
private BufferedWriter bout;
private SocketAddress addr;
public Cthread(Socket socket){
this.socket=socket;
}
public void run(){
// 获得客户端地址
addr = socket.getRemoteSocketAddress();
System.out.println("连了一个客户端:" + addr);
// 通过Socket对象获得IO流
try {
OutputStream ops = socket.getOutputStream();
InputStream ips = socket.getInputStream();
// 包装成缓冲字符流
OutputStreamWriter osr = new OutputStreamWriter(ops);
bout = new BufferedWriter(osr);
InputStreamReader isr = new InputStreamReader(ips);
BufferedReader br = new BufferedReader(isr);
String msg = "欢迎光临!\r\n";
bout.write(msg);
bout.flush();
while (true) {
// 服务端读取客户端发来的消息
// 读取一行数据,读取到换行回车就不读取了
String str = br.readLine();
System.out.println( str);
// 收到一个消息,就将消息广播给其它socket
for (int i = 0; i < MyServer.list.size(); i++) {
Cthread ct = MyServer.list.get(i);
ct.sendMsg(str + "\r\n");
}
}
} catch (Exception e) {
System.out.println("客户端已经断开连接!");
}
}
// 通过socket给客户端发送消息
public void sendMsg(String msg) throws IOException {
this.bout.write(msg);
this.bout.flush();
}
}
服务端以及完成
----------------------------------------------------------------------------------------------
接下来便是客户端了:
Client类里面主要包含界面构建,事件的触发
连接服务端的语句:
socket = new Socket("127.0.0.1", 9090);//利用IP地址和端口号,来连接正在运行的服务器
package 聊天室客户端;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
public class Client extends JFrame implements ActionListener{
private Socket socket;
private BufferedWriter bout;
private BufferedReader br;
private JTextPane pane;
private JTextPane area;
private static JTextField jt;
private JButton btn1;
private static JTextPane pane3;//用来显示在线人数,所以让其他对象都公用(目前没有做完这个功能)
private boolean line=true;
public static void main(String[] args) {
Client c = new Client();
JOptionPane.showMessageDialog(null, "请先设置昵称!");
//如果不设置昵称,那么对话时就是显示的IP地址
}
public Client() {
this.setTitle("聊天室");
this.setBounds(200, 100, 800, 500);
this.setDefaultCloseOperation(3);
this.setLayout(null);
// 显示接受的消息内容
pane = new JTextPane();
pane.setEditable(false);
JScrollPane jsp = new JScrollPane(pane);
jsp.setBounds(10, 10, 550, 300);
jsp.setAutoscrolls(true);
this.add(jsp);
// 要发送的内容
area = new JTextPane();
JScrollPane jsp2 = new JScrollPane(area);
jsp2.setBounds(10, 320, 440, 130);
this.add(jsp2);
JLabel label = new JLabel();
label.setText("请设置昵称:");
label.setBounds(460, 310, 100, 30);
this.add(label);
jt = new JTextField();
jt.setText("");
jt.setBounds(460, 340, 100, 30);
this.add(jt);
btn1 = new JButton("连接");
btn1.setBounds(460, 380, 100, 30);
btn1.addActionListener(this);
this.add(btn1);
JButton btn2 = new JButton("发送");
btn2.setBounds(460, 420, 100, 30);
btn2.addActionListener(this);
this.add(btn2);
// 显示在线人(还没有完成)
JLabel label3 = new JLabel();
label3.setText("在线情况:");
label3.setBounds(580, 0, 100, 30);
this.add(label3);
pane3 = new JTextPane();
pane3.setEditable(false);
JScrollPane jsp3 = new JScrollPane(pane3);
jsp3.setBounds(580, 30,200, 420);
jsp3.setAutoscrolls(true);
this.add(jsp3);
this.setResizable(false);
this.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (command.equals("连接")) {
try {
// 和服务器建立连接
socket = new Socket("127.0.0.1", 9090);//利用IP地址和端口号,来连接正在运行的服务器
OutputStream ops = socket.getOutputStream();//通过socket对象获得输出流
// 包装成缓冲字符流
OutputStreamWriter osr = new OutputStreamWriter(ops);
bout = new BufferedWriter(osr);
InputStream ips = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(ips);
br = new BufferedReader(isr);
// 创建一个接受消息的线程,用来接受服务端发来的消息(把需要用到的参数传过去)
ReceiveThread rt = new ReceiveThread(br, pane,jt,pane3);
rt.start();
JOptionPane.showMessageDialog(null, "连接成功");
btn1.setText("已连接");
btn1.setEnabled(false);//设置点击连接后,连接按钮显示为不可以点击
// bout.write(jt.getText()+".929144493");//把名字发出去
} catch (Exception ef) {
JOptionPane.showMessageDialog(null, "连接失败");
}
} else if (command.equals("发送")) {
// 获得输入框中输入的消息内容
String msg = area.getText();
if (msg == null || msg.equals("")) {
JOptionPane.showMessageDialog(null, "必须输入消息内容!");
} else {
try {
if(!jt.getText().equals("")){
//获取名字和需要发送的信息,一起发送出去
bout.write(jt.getText()+" say:"+msg + "\r\n");
}else{
//没有名字的话,就获取IP地址和需要发送的信息,一起发送出去
bout.write(socket.getLocalAddress()+" 说:"+msg + "\r\n");
}
bout.flush();
area.setText("");//清空发送框
} catch (Exception ef) {
ef.printStackTrace();
}
}
}
}
}
当我们连接上服务端的时候,我们就新建一个线程类来接收服务端发来的消息(也就是服务端广播的消息),并且我们利用这个线程类来改变我们聊天界面的文字显示;
package 聊天室客户端;
import java.io.BufferedReader;
import javax.swing.JTextField;
import javax.swing.JTextPane;
/**
* 客户端接收消息的线程
*
* @author kowloon
*
*/
public class ReceiveThread extends Thread {
private BufferedReader br;
private JTextPane pane;// 显示消息的文本框
private JTextField jt;
private JTextPane pane3;
public ReceiveThread(BufferedReader br, JTextPane pane,JTextField jt,JTextPane pane3) {
this.br = br;
this.pane = pane;
this.jt=jt;
this.pane3=pane3;
}
@Override
public void run() {
try {
while (true) {//只要一创建,就一直在运行
// 读取到一个字符串
String msg = br.readLine() +"\r\n";//读取服务器广播过来的信息
// int i=msg.indexOf(".");
// String panduan=msg.substring(i,i+10);
// if(panduan.equals(".929144493")){
// //传名字
// String name=msg.substring(0,i);
// pane3.setText(pane3.getText()+name+"\r\n");
// System.out.println("名字收到!");
// }else{//信息
String text = pane.getText();//获得我自己的聊天框里面的信息
pane.setText(text + msg);//把信息添加到聊天框里面,不需要换行符,因为读取数据的时候已经有了换行符
// System.out.println("信息收到");
// }
}
} catch (Exception ef) {
ef.printStackTrace();
}
}
}
下面还有一些运行时候的图片,还有所有完整的项目压缩包!