网络聊天室
服务器:
1.启动服务器,在服务器端循环监听客户端的连接
java
try {
ServerSocket serverSocket=new ServerSocket(6622);
System.out.println("服务器已启动");
while(true){//把客户端实例添加到sockets里
Socket socket=serverSocket.accept();
sockets.add(socket);
System.out.println("当前连接到客户端数量:"+sockets.size());
//为每一个链接过来的客户端开一个线程
new SocketThread(socket).start();
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("服务器创建失败");
}
2.把循环接收到的多个客户端Socket对象存储起来(集合)
java
ArrayList<Socket> sockets=new ArrayList<>();
3.在服务器端每个Socket都要监听各自客户端发来的消息
java
while(true){
try {
String msg=dataInputStream.readUTF();
jTextAreamsg.append(msg+"\n");//在服务器端显示msg
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("客户端下线了");
}
}
4.一旦某个客户端发送了消息,那么在服务器端,就将此消息转发给其他的客户
java
//向不同客户端转发消息
//遍历sockets
for (Socket socket:sockets){//客户端1 客户端2 客户端3
DataOutputStream dataOutputStream=new DataOutputStream(socket.getOutputStream());
dataOutputStream.writeUTF(msg);
}
客户端:
1.用户登录,创建Socket
java
Socket socket = new Socket("xxxxxxxxx", 6622);
2.如果Socket创建成功,打开聊天窗口
java
new ChatWindow(jTextaccount.getText(), socket);//打开聊天窗口
3.输入内容,点击发送按钮发送消息
4.在客户端监听服务器发送回来的消息,并把消息显示出来
java
class ClientThread extends Thread{
DataInputStream dataInputStream;
public ClientThread(Socket socket) throws IOException {
dataInputStream=new DataInputStream(socket.getInputStream());
}
@Override
public void run() {
while(true){
try {
String msg=dataInputStream.readUTF();
jTextArea.append(msg+"\n");//可以显示聊天内容并且保持原先内容
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
}
完整代码:
ChatWindow:
java
public class ChatWindow extends JFrame {
JTextArea jTextArea;
public ChatWindow(String name, Socket socket) throws IOException {
DataOutputStream dos=new DataOutputStream(socket.getOutputStream());
this.setTitle("欢迎"+name+"登录");
this.setSize(500,500);
this.setLocationRelativeTo(null);//居中
this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);//关闭窗口时退出程序
this.setResizable(false);//禁止窗口拖拽
//创建面板
JPanel jPanel=new JPanel();
jTextArea=new JTextArea(22,43);//显示文本域
jTextArea.setEditable(false);//不可修改的
JTextArea jTextArea2=new JTextArea(5,33);//发送文本域
jTextArea2.setLineWrap(true);//强制换行
JScrollPane j=new JScrollPane(jTextArea2);//滚动条
JButton jButtonsend=new JButton("发送");
jPanel.add(jTextArea);
jPanel.add(j);
jPanel.add(jButtonsend);
this.add(jPanel);
this.setVisible(true);
//来到聊天窗口
jButtonsend.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String message=jTextArea2.getText();
if(message.length()==0){//输入为空
JOptionPane.showMessageDialog(null,"发送内容不能为空");
return;
}
else{
//不为空,向服务器发送消息
String msg=name+" "+new SimpleDateFormat("HH:mm:ss").format(new Date());
msg=msg+"\n"+message;
try {
dos.writeUTF(msg);
//发送成功清空发送内容
jTextArea2.setText("");
} catch (IOException ioException) {
ioException.printStackTrace();
JOptionPane.showMessageDialog(null,"内容发送失败,请检查网络连接");
}
}
}
});
new ClientThread(socket).start();
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
int res=JOptionPane.showConfirmDialog(null,"你确定要退出聊天吗?","警告",JOptionPane.OK_CANCEL_OPTION);
if(res==0){
//点击确定
new LoginWindow();
dispose();
}
}
});
}
//监听服务器发的消息(即客户端123的消息)
class ClientThread extends Thread{
DataInputStream dataInputStream;
public ClientThread(Socket socket) throws IOException {
dataInputStream=new DataInputStream(socket.getInputStream());
}
@Override
public void run() {
while(true){
try {
String msg=dataInputStream.readUTF();
jTextArea.append(msg+"\n");//可以显示聊天内容并且保持原先内容
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
}
}
LoginWindow:
java
public class LoginWindow extends JFrame {
public LoginWindow(){
this.setTitle("欢迎登录");
this.setSize(500,350);
this.setLocationRelativeTo(null);//居中
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭窗口时退出程序
this.setResizable(false);//禁止窗口拖拽
JPanel jPanel=new JPanel();
JLabel jLabel=new JLabel("欢迎登录");
jLabel.setFont(new Font("横体", Font.BOLD,30));
jPanel.add(jLabel);//欢迎登录
//中间第一个空标签
JLabel jLabel1=new JLabel();//欢迎登录下面空白部分
jLabel1.setPreferredSize(new Dimension(500,50));
//账号
JLabel jLabelAccount=new JLabel("账号");//账号标签
jLabelAccount.setPreferredSize(new Dimension(30,30));
JTextField jTextaccount=new JTextField(15);//账号文本域
//中间第二个空标签
JLabel jLabel2=new JLabel();//欢迎登录下面空白部分
jLabel2.setPreferredSize(new Dimension(500,20));
//密码
JLabel jLabelPassword=new JLabel("密码 ");//账号标签
jLabelPassword.setPreferredSize(new Dimension(30,30));
JPasswordField jPasswordField=new JPasswordField(15);
//中间第二个空标签
JLabel jLabel3=new JLabel();//欢迎登录下面空白部分
jLabel3.setPreferredSize(new Dimension(500,20));
//按钮
JButton ButtonLogin=new JButton("登录");
JButton ButtonSign=new JButton("注册");
jPanel.add(jLabel1);
jPanel.add(jLabelAccount);
jPanel.add(jTextaccount);
jPanel.add(jLabel2);
jPanel.add(jLabelPassword);
jPanel.add(jPasswordField);
jPanel.add(jLabel3);
jPanel.add(ButtonLogin);
jPanel.add(ButtonSign);
this.add(jPanel);
this.setVisible(true);
//按钮事件
ButtonLogin.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(jTextaccount.getText().equals("")||jPasswordField.getText().equals("")){//当账号或密码为空时,弹出警告
JOptionPane.showMessageDialog(null,"账号和密码不能为空","警告",JOptionPane.ERROR_MESSAGE);
return;
}
else if(!((jTextaccount.getText().matches("\\w{1,100}"))&&(jPasswordField.getText().matches("\\w{1,100}")))){//当1-100范围内不是字母和数字时弹出警告
JOptionPane.showMessageDialog(null,"账号密码只能由数字,字母组成","警告",JOptionPane.ERROR_MESSAGE);
return;
}
//创建Socket
else {
try {
Socket socket = new Socket("10.13.0.67", 6622);
new ChatWindow(jTextaccount.getText(), socket);//打开聊天窗口
dispose();//释放窗口
} catch (IOException ioException) {
ioException.printStackTrace();
JOptionPane.showMessageDialog(null, "服务器连接失败,请稍后再试");
}
}
}
});
}
}
Run:
public class Run {
public static void main(String[] args) {
new LoginWindow();
}
}
Server:
java
public class Server extends JFrame {
JTextArea jTextAreamsg;//放到成员变量便于在内部类中也可以使用
//客户端连接的数量
ArrayList<Socket> sockets=new ArrayList<>();
public void ServerStart(){
this.setTitle("我是服务器");
this.setSize(500,500);
this.setLocationRelativeTo(null);//居中
this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);//关闭窗口时退出程序
this.setResizable(false);//禁止窗口拖拽
JPanel jPanel=new JPanel();
jTextAreamsg=new JTextArea(28,43);
jTextAreamsg.setLineWrap(true);//强制换行
JScrollPane jScrollPane=new JScrollPane();
jTextAreamsg.setEditable(false);//不可修改的
jPanel.add(jTextAreamsg);
jPanel.add(jScrollPane);
this.add(jPanel);
this.setVisible(true);
//关闭窗口
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
int res=JOptionPane.showConfirmDialog(null,"你确定要关闭服务器吗?","警告",JOptionPane.OK_CANCEL_OPTION);
if(res==0){//点击确定
dispose();
}
}
});
try {
ServerSocket serverSocket=new ServerSocket(6622);
System.out.println("服务器已启动");
while(true){//把客户端实例添加到sockets里
Socket socket=serverSocket.accept();
sockets.add(socket);
System.out.println("当前连接到客户端数量:"+sockets.size());
//为每一个链接过来的客户端开一个线程
new SocketThread(socket).start();
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("服务器创建失败");
}
}
//内部类,用来监听自己客户端有没有发消息
class SocketThread extends Thread{
Socket socket;//用来引入到run方法
DataInputStream dataInputStream;
public SocketThread(Socket socket) throws IOException {
this.socket=socket;//把成员变量socket赋值为传入的socket参数,以便于客户端下线后可以从sockets里移除客户端
dataInputStream=new DataInputStream(socket.getInputStream());
}
@Override
public void run() {
//循环监听客户端自己发送消息
while(true){
try {
String msg=dataInputStream.readUTF();
jTextAreamsg.append(msg+"\n");//在服务器端显示msg
//向不同客户端转发消息
//遍历sockets
for (Socket socket:sockets){//客户端1 客户端2 客户端3
DataOutputStream dataOutputStream=new DataOutputStream(socket.getOutputStream());
dataOutputStream.writeUTF(msg);
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("客户端下线了");
sockets.remove(socket);
break;
}
}
}
}
}
ServerRun:
java
public class ServerRun {
public static void main(String[] args) {
new Server().ServerStart();
}
}
在这个过程中监听服务器消息和监听客户端消息都用到了内部类,这样的好处是可以之间在内部类中使用大类里面的jTextArea和sockets