通信之c/s简单交互

  

     就目前我们做的c/s的简单交互而言主要用到了三个类:客户端线程类ClientThread    服务端监听类ChatServer  服务端线程类ChatThread

     简单理解:通过socket建立客户端与服务器的连接,在客户端和服务器分别用输入输出流来获得和写出消息,通过消息的传递达到交互的目的。

ps: socket 实现客户端套接字  套接字是两台机器间通信的端点)

打开输入输出流(用字符流封装)
       InputStream in = socket.getInputStream();
       OutputStream out = socket.getOutputStream();
 //将字节流转成字符流                 
       reader = new BufferedReader(new InputStreamReader(in, "GB2312"));
       writer = new BufferedWriter(new OutputStreamWriter(out, "GB2312"));
 
//读消息(线程循环读取)
            while(line != null){
                 line  = reader.readLine();
                         }
 
//发送消息
          writer.write(msg); writer.flush();

 

 

     客户端  登陆界面类LoginUI  客户消息处理线程类ClientThread   聊天界面类ClientUI

 

(1)登陆界面:主要是用来创建一个登陆的窗口,获得用户名以及密码

 

package 1;
/**
 * (1)
 */
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.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
 
public class LoginUI extends JFrame{
     
      private ClientThread ct;
     
      //入口函数
      public static void main(String args[]){
             LoginUI loginUI = new LoginUI();
             loginUI.init();
      }
     
      public void init(){
              //创建一个登陆界面
             this.setTitle("登陆界面");
             this.setSize(200,200);
             this.setResizable(false);
             this.setDefaultCloseOperation(3);
             this.setLayout(new FlowLayout());
            
             //创建标签
             JLabel jb1 = new JLabel("用户名:");
             this.add(jb1);
            
             //创建文本输入框
             final JTextField username = new JTextField(10);
             this.add(username);
            
             JLabel jb2 = new JLabel("密    码:");
             this.add(jb2);
            
             final JPasswordField  passwd = new JPasswordField(10);
             this.add(passwd);
            
             //创建一个按钮对象
             JButton jb3 = new JButton("登陆");
             this.add(jb3);
             jb3.setActionCommand("login");
             //给按钮添加监听器(内部类)
             jb3.addActionListener(new ActionListener(){
                    
                     public void actionPerformed(ActionEvent e) {
                              //创建一个用户线程,在构造函数中传入界面对象(LoginUI.this)!!!!!!!!!!!!! 、ip、端口号、用户名、密码
                             ct = new ClientThread(LoginUI.this,"127.0.0.1",8008,username.getText(),passwd.getText());
                             //启动线程
                             Thread th = new Thread(ct);
                             th.start();
                     }
             });
            
         this.setVisible(true);     
      }
 
      public void LoginResult(boolean result){
             if(result == false){
                     //JOptionPane 有助于方便地弹出要求用户提供值或向其发出通知的标准对话框   (静态方法直接用类名调用)
                     JOptionPane.showMessageDialog(this,"登陆失败!!哈哈哈哈。。。");
             }else{
                     //登陆界面消失
                     this.dispose();
                     //创建一个聊天窗口
                     ClientUI  ui = new ClientUI(ct);
                     ui.init();
                     //将窗口作为参数传给用户处理线程
                     ct.setClientUI(ui);
             }
      }
}

 

扫描二维码关注公众号,回复: 1246059 查看本文章

 

(2)线程类:将登陆结果返回给登陆界面、将服务器传送来的消息发给聊天界面对象

package  1;
 
/**
 * 用户登陆处理线程    (2)
 
 */
 
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.UnknownHostException;
 
import javax.swing.JFrame;
 
public class ClientThread extends JFrame implements Runnable{
      private LoginUI loginUI;
      private String ip;
      private int port;
      private String username;
      private String passwd;
      private ClientUI ui;
     
      private Socket socket;
     
      private BufferedWriter writer;
      private BufferedReader reader;
      private boolean runFlag = true;
      private boolean isLogin = false;
     
      //重载构造函数
      public ClientThread(LoginUI loginUI,String ip,int port,String username,String passwd){
             this.loginUI = loginUI;
             this.ip = ip;
             this.port = port;
             this.username = username;
             this.passwd = passwd;
      }
     
   //得到用户聊天界面对象
      public void setClientUI(ClientUI ui){
             this.ui = ui;
      }
     
      public boolean connectServer(){
             try {
                     //创建一个套接字
                     socket = new Socket(ip,port);
                    
                     //得到输入输出流对象
                     InputStream in = socket.getInputStream();
                     OutputStream out = socket.getOutputStream();
                    
                     //将字节流转成字符流
                     reader = new BufferedReader(new InputStreamReader(in, "GB2312"));
                     writer = new BufferedWriter(new OutputStreamWriter(out, "GB2312"));
                     return true;
             } catch (UnknownHostException e) {
                     e.printStackTrace();
             } catch (IOException e) {
                     e.printStackTrace();
             }
                 return false;
      }
     
      public void run(){
             if(connectServer() == false){
                     System.out.println("登陆服务器失败!");
                     return;
             }
            
             login();
            
             if(isLogin == false){
                     return;
             }
            
            
             String msg;
             try {
                     //通过读入流获得消息
                     msg = reader.readLine();
                     while(runFlag && msg != null){
                             //将消息发送给聊天界面
                             ui.onMsg(msg);
                             msg = reader.readLine();
                     }
             } catch (IOException e) {
                     e.printStackTrace();
             }
             System.out.println("ClientThread退出来了");
             }
     
     
      public void sendMsg(String msg){
             try {
                     writer.write(msg+"\r\n");
                     writer.flush();
             } catch (IOException e) {
                     e.printStackTrace();
             }
      }
     
      public void login(){
             try {
                    
                     writer.write(username+"\r\n");
                     writer.write(passwd+"\r\n");
                     writer.flush();
                    
                     String result = reader.readLine();
                     if("LoginOK".equals(result)){
                             System.out.println("登陆成功!");
                             isLogin = true;
                             //创建一个新的聊天界面
                             loginUI.LoginResult(true);
                     }else{
                             System.out.println(result);
                             //弹出一个窗口显示登陆失败了
                             loginUI.LoginResult(false);
                     }
                    
             } catch (IOException e) {
                     e.printStackTrace();
             }
            
      }
      }
 
 

 

(3)聊天界面类:将消息显示在指定区域。

package  1;
 
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;
 
public class ClientUI extends JFrame{
             //声明一个线程
      private ClientThread ct;
       
      public ClientUI(ClientThread ct){
                     this.ct = ct;
      }
 
      //创建一个指定行列的空文本域   “聊区”
      JTextArea logArea = new JTextArea(13,40);
     
     
     
      public void init(){
             //创建聊天窗口
             this.setTitle("聊吧");
             this.setSize(500,400);
             this.setResizable(false);
             this.setDefaultCloseOperation(3);
             this.setLayout(new FlowLayout());
            
            
             this.add(logArea);
            
             //创建一个指定行列的文本域   “发送消息区”
             final JTextArea sendMsg = new JTextArea(4,40);
             this.add(sendMsg);
            
            
             //创建一个发送按钮
             JButton jb1 = new JButton("发送");
             this.add(jb1);
             
            
             jb1.addActionListener(new ActionListener(){
 
                     public void actionPerformed(ActionEvent e) {
                              //发送文本区的内容
                             String msg = sendMsg.getText().toString();
                             //线程将消息发送过来
                             ct.sendMsg(msg);
                             //将发送区清空
                             sendMsg.setText("");
                             }
             });
            
             this.setVisible(true);
            
      }
 
 
              //接受服务器发送过来的消息
             public  void onMsg(String msg ){
                     //将消息显示在“聊吧”界面的聊天区
                             logArea.setText(logArea.getText()+msg+"\r\n");
             }
}

 

 

    服务端 服务端监听类ChatServer  服务端线程类ChatThread  、用户存储队列ChatTools、用户数据类DateTools

(1)监听类的作用:监听端口 (监听一个端口号,等待用户的访和  创建一个服务端线程对象 

package  1;
/**
 * (1)
 */
import java.io.IOException;
 
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
 
public class ChatServer {
 
      public static void main(String[] args) {
            
             new ChatServer().setupServer(8008);
      }
     
     
      public void setupServer(int port) {
             try {
                     //监听一个端口号码,等待用户的访问
                     ServerSocket ss = new ServerSocket(port);
            
                     //循环等待客户端的访问
                     while(true){
                              //得到socket
                             Socket socket = ss.accept();
                              
                             //将socket传给服务端线程进行处理
                             ChatThread ct = new ChatThread(socket);
                             //启动线程
                             ct.start();
                          }         
                } catch (IOException e) {
                     e.printStackTrace();
                        }
        }
 
}

 

 

(2)线程类的作用:打开输入输出流(用字符流封装)  读消息(线程循环读取)、  处理消息、  发送消息给登录界面(不需要线程)、创建一个队列保存所有用户信息 

package  1;
/**
 * (2)
 */
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;
 
public class ChatThread extends Thread {
      private Socket socket;
      private String username;
      private boolean isLogin = false;
      private BufferedWriter writer;
      private BufferedReader reader;
     
      public ChatThread(Socket socket){
             this.socket = socket;
      }
     
      public void run() {
             try {
                     InputStream in = socket.getInputStream();
                     OutputStream out = socket.getOutputStream();
                    
                     //将字节流转成字符流
                     reader = new BufferedReader(new InputStreamReader(in, "GB2312"));
                     writer = new BufferedWriter(new OutputStreamWriter(out, "GB2312"));
                    
                     login();
                     //将登录成功的用户添加到在线用户队列
                     ChatTools.addClient(this);
                    
                     String line  = reader.readLine();
                     while(line != null){
                             ChatTools.castMsg(username +"说:"+line);
                             line  = reader.readLine();
                     }
                     close();
                      //ChatTools.castMsg(username +"走了");
                                    } catch (Exception e) {
                     close();
                     e.printStackTrace();
             }
            
      }
     
      private void login() throws IOException {
             String msg = null;
             while(isLogin == false){
                    
                     String username = reader.readLine();
                     String passwd = reader.readLine();
                      
                     //从数据库中取密码
                     String passwd2 = DateTools.getPasswd(username);
                     //如果为null,说明数据库里面没有该用户
                     if(passwd2 == null){
                             msg = "用户不存在\r\n";
                             writer.write(msg);
                             writer.flush();
                     } else {
                             if(passwd2.equals(passwd)){
                                    isLogin = true;
                                    this.username = username;
                             }
                     }
                     if(isLogin == false){
                             msg = "密码错误!\r\n";
                             writer.write(msg);
                             writer.flush();
                     }
             }
             //给用户登陆界面一个信号,决定其下一步是弹出“登录失败”还是一个新的聊天窗口
             msg = "LoginOK\r\n";
             writer.write(msg);
             writer.flush();
      }
     
      public void sendMsg(String msg) {
             try {
                     writer.write(msg);
                     writer.flush();
             } catch (IOException e) {
                     e.printStackTrace();
             }
            
      }
     
 
      public String getUsername() {
             return username;
      }
     
      public void close() {
             try {
                     ChatTools.remove(this);
                     socket.close();
             } catch (IOException e) {
                     e.printStackTrace();
             }
      }
}

 

 (4)用户存储队列ChatTools :将登录成功的用户加入到队列中(代码略)

     

 (5)用户数据类DateTools  创建一个哈希表(key,value),将指定值存入。(代码略)

     

 

猜你喜欢

转载自939647516.iteye.com/blog/1910015
今日推荐