在PC端IDEA IDE使用ServerSocket,在android端使用Socket,搭建简单的聊天室

利用多个activity使用一个Socket套接字,实现了每个用户在自己的应用程序中使用同样的Socket进行数据传输,使得数据传输不丢失(因为多个Socket导致)。

PC端共分为两个类:一个类中主要包含循环监听Socket的main方法。一个类(子线程类)主要包含对监听到的数据处理和向客户端的响应。

代码如下:

MyServer类:

import java.net.*;
import java.io.*;
import java.util.*;

public class MyServer {

   // 定义保存所有Socket的HashMap集合
   public static Map<String,Socket> userList = new HashMap<>();

    public static void main(String[] args) throws IOException {
       //得到服务器端地址
      InetAddress addr = InetAddress.getLocalHost();
      System.out.println("local host:"+addr);

      //创建serverSocket套接字对象
      ServerSocket ss = new ServerSocket(5569);

      while(true) {

         // 此行代码会阻塞,将一直等待别人的连接
         Socket s = ss.accept();
         //userList.add(s);

         //输出日志
         System.out.println(s);  //输出socket对象

         //增加用户的操作在ServerThread类中实现

         // 每当客户端连接后启动一条ServerThread线程为该客户端服务
         new Thread(new ServerThread(s)).start();
      }
    }

}

ServerThread类:

import java.io.*;
import java.net.*;
import java.util.*;

// 负责处理每个线程通信的线程类
public class ServerThread implements Runnable {

   // 定义当前线程所处理的Socket
   Socket s = null;

   // 该线程所处理的Socket所对应的输入流
   BufferedReader br = null;

   private String userName;

   public ServerThread(Socket s) throws IOException {
      this.s = s;
      // 初始化该Socket对应的输入流
      br = new BufferedReader(new InputStreamReader(s.getInputStream() , "utf-8"));   // ②
   }


   public void run() {

      try {
         String content = "";

         while((content = readFromClient()) != null){  //不断循环得到对应APP端数据

            System.out.println("得到APP端传来的消息 ---> " + content);

            String [] result = content.split(",");

            System.out.println("得到result数组长度是:" + result.length);


            //判断是用户登录还是用户发送消息
            if(result[0].equals("login")){ //表示使用户登录,保存用户信息
               String username = result[1];//得到用户登陆名
               userName = username;

               System.out.println("得到用户名:" + username);

               //如果用户没有登录
               if(!MyServer.userList.containsKey(username)){
                  MyServer.userList.put(username,s);//加入该user对象
               }

               //直接发送登录成功消息
               try{
                  OutputStream os = s.getOutputStream();
                  os.write(("登录成功" + "\n").getBytes("utf-8"));
                  System.out.println("登录成功消息发送从服务器发出");
               } catch(SocketException e) {
                  e.printStackTrace();
                  System.out.println(MyServer.userList);
               }

            }else if(result[0].equals("message")){

               System.out.println("用户聊天消息");


               //判断是否输入了用户名,没有输入则进行群发
               if(result[2].equals(" ")){//表示没有写发送给的用户对象名那么群发
                     // 遍历socketList中的每个Socket,
                     // 将读到的内容向每个Socket发送一次
                  for (Map.Entry<String,Socket> entry : MyServer.userList.entrySet()) {
                     try {
                        OutputStream os = entry.getValue().getOutputStream();
                        os.write((result[1] + "说:" + result[3] + "\n").getBytes("utf-8"));
                  }catch (SocketException e){
                     e.printStackTrace();
                     MyServer.userList.remove(entry.getKey());  //表示从userList数组中删除发送消息失败的用户对象
                     System.out.println(MyServer.userList);
                  }
                     }

               }else {//表示专门发送给某一个用户对象

                     String toUsername = result[2];

                     //如果用户数组包含需要发送给的用户
                     if(MyServer.userList.containsKey(toUsername)){
                        Socket socket = MyServer.userList.get(toUsername);
                        try{
                           OutputStream os = socket.getOutputStream();
                           os.write((result[1] + "说:" +result[3] + "\n").getBytes("utf-8"));
                        }catch (SocketException e){
                           e.printStackTrace();
                           MyServer.userList.remove(toUsername);
                        }

                     }else{

                        try{
                        OutputStream os = s.getOutputStream();  //获得当前用户的输入流
                        os.write("收消息用户尚不存在!\n".getBytes("utf-8")); //发送收消息的人不存在
                     }catch (SocketException e){
                           e.printStackTrace();
                        }

                     }

               }

               }

            }

      } catch (IOException e) {
         e.printStackTrace();
      }

   }


   // 定义读取客户端数据的方法
   private String readFromClient() {

      try {
         return br.readLine();
      } catch (IOException e) {// 如果捕捉到异常,表明该Socket对应的客户端已经关闭
         e.printStackTrace();
         // 删除该Socket。
         MyServer.userList.remove(userName);  //删除用户对象
      }

      return null;
   }


}

 

客户端APP中有:两个activity类。一个子线程类处理数据。

LoginActivity类代码:

package com.example.lab7;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import java.lang.ref.WeakReference;

public class LoginActivity extends AppCompatActivity {

    //定义UI组件
    private EditText username;
    private EditText userpwd;

    private Button loginBtn;

    //创建一个LoginThread线程
    public static ClientThread clientThread;

    //定义接受thread类传来的消息,表示用户信息已经发送给服务器,并且获得了服务器的响应消息
    class MyHandler extends Handler {

        private WeakReference<LoginActivity> loginActivityWeakReference;

        MyHandler(WeakReference<LoginActivity> loginActivityWeakReference) {
            this.loginActivityWeakReference = loginActivityWeakReference;
        }

        @Override
        public void handleMessage(Message msg) {

            // 如果消息来自子线程
            if (msg.what == 0x123) {//接受用户登录信息

                if(msg.obj.toString().equals("登录成功")){
                    Toast.makeText(LoginActivity.this,"用户登录成功",Toast.LENGTH_SHORT).show();
                    //表示跳转到其他activity
                    Intent intent = new Intent(LoginActivity.this,MainActivity.class);
                    intent.putExtra("login_name",username.getText().toString());
                    startActivity(intent);//启动activity

                    finish();  //当前activity结束
                }
            }
        }

    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        //获得UI组件
        username = findViewById(R.id.username);
        userpwd = findViewById(R.id.password);
        loginBtn = findViewById(R.id.loginBtn);

        //创建一个handler处理对象
        MyHandler handler = new MyHandler(new WeakReference<>(this));

        //初始化登录线程,实现用户登录逻辑判断
        clientThread = new ClientThread(handler);

        new Thread(clientThread).start();//启动thread

        //点击发送按钮逻辑
        loginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                // 当用户单击“发送”按钮后,将用户输入的数据封装成Message
                // 然后发送给子线程的Handler
                Message msg = new Message();
                msg.what = 0x345;  //表示发送用户登录信息的消息
                msg.obj = username.getText().toString()+","+userpwd.getText().toString();  //用户名和密码存储
                clientThread.revHandler.sendMessage(msg);  //调用loginThread线程中的revhandler对象接收消息
                Toast.makeText(LoginActivity.this,"登录...",Toast.LENGTH_SHORT).show();

            }
        });

    }

}

 

MainActivity类代码如下:

package com.example.lab7;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.lang.ref.WeakReference;


public class MainActivity extends Activity {

    private TextView show;
    // 定义与服务器通信的子线程
    private ClientThread clientThread;


    class MyHandler extends Handler {

        private WeakReference<MainActivity> mainActivity;

        MyHandler(WeakReference<MainActivity> mainActivity) {
            this.mainActivity = mainActivity;
        }

        @Override
        public void handleMessage(Message msg) {

            System.out.println("前端UI界面得到msg内容是:" + msg);

            // 如果消息来自子线程
            if (msg.what == 0x456) {//接受用户发送的消息
                // 将读取的内容追加显示在文本框中
                mainActivity.get().show.append("\n" + msg.obj.toString());
            }else if(msg.what == 0x404){  //表示用户未找到
                Toast.makeText(MainActivity.this,"收消息用户尚不存在!",Toast.LENGTH_SHORT).show();
            }
        }
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //获得当前应用界面的用户对象
        final String login_name = getIntent().getStringExtra("login_name");

        // 定义界面上的两个文本框
        final EditText input = findViewById(R.id.input);
        show = findViewById(R.id.show);
        final EditText toUser = findViewById(R.id.toUser);

        // 定义界面上的一个按钮
        Button send = findViewById(R.id.send);

        MyHandler handler = new MyHandler(new WeakReference<>(this));

        clientThread = new ClientThread(handler);

        // 客户端启动ClientThread线程创建网络连接,读取来自服务器的数据
        new Thread(clientThread).start();


        send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 当用户单击“发送”按钮后,将用户输入的数据封装成Message
                // 然后发送给子线程的Handler
                Message msg = new Message();
                msg.what = 0x567;  //
                String user="";

                if(toUser.getText().toString().equals("")){
                    user = " ";
                }else{
                    user = toUser.getText().toString();
                }

                //发送到服务器的登录信息的格式是:登录用户名,需要发送的用户名,发送的消息内容
                msg.obj = login_name + "," +user+","+input.getText().toString();
                clientThread.revHandler.sendMessage(msg);

            }
        });

    }
}


子线程ClientThread类:

package com.example.lab7;


import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;

public class ClientThread implements Runnable {

    private Socket s = null;

    // 定义向UI线程发送消息的Handler对象
    private Handler handler;

    // 该线程所处理的Socket所对应的输入流
    private BufferedReader br;
    private OutputStream os;

    // 定义接收不同UI线程的消息的Handler对象
    Handler revHandler;

    ClientThread(Handler handler) {
        this.handler = handler;
    }

    @Override
    public void run() {

        try {
                //创建一个套接字对象
            s = MSocket.getsocket();

            br = new BufferedReader(new InputStreamReader(s.getInputStream()));
            os = s.getOutputStream();

            //启动一条子线程来读取服务器响应的数据(包括用户登录消息和用户发送消息)
            new Thread(new Runnable() {
                @Override
                public void run() {
                    String content;
                    // 不断读取Socket输入流中的内容
                    try {
                        while ((content = br.readLine()) != null) {

                            System.out.println("得到content是:" + content);

                            //判断接收到的消息类型
                            if(content.equals("登录成功")){//表示用户登录消息
                                // 每当读到来自服务器的数据之后,发送消息通知
                                // 程序界面显示该数据
                                Message msg = new Message();
                                msg.what = 0x123;
                                msg.obj = content;
                                handler.sendMessage(msg);
                            }else if(content.equals("收消息用户尚不存在!")){ //表示收消息用户不存在
                                Message msg = new Message();
                                msg.what = 0x404;
                                msg.obj = content;
                                handler.sendMessage(msg);
                            }else{//表示成功收到消息
                                System.out.println("收到消息!");
                                Message msg = new Message();
                                msg.what = 0x456;
                                msg.obj = content;
                                handler.sendMessage(msg);
                                System.out.println("发送消息内容到UI前端成功!");
                            }

                        }
                    }
                    catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                }
            }).start();


            // 为当前线程初始化Looper
            Looper.prepare();

            // 创建revHandler对象
            revHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {

                    // 接收到UI线程中用户输入的数据
                    if (msg.what == 0x345) {

                        // 将用户在文本框内输入的内容写入网络
                        try {
                            os.write(("login," + msg.obj.toString() + "\r\n").getBytes("utf-8"));
                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                    }else if(msg.what == 0x567){
                        System.out.println("得到消息用户发送消息 ---> " + msg);
                        try{
                            os.write(("message," + msg.obj.toString() + "\r\n").getBytes("utf-8"));
                        }catch (IOException e){
                            e.printStackTrace();
                        }

                    }

                }
            };


            // 启动Looper
            Looper.loop();
        } catch (SocketTimeoutException e1) {
            System.out.println("网络连接超时!!");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


}

上述实现额外功能:但发送者输入收消息用户名之后,只给单独用户发送消息;当没有输入收消息用户名时,给所有人发送消息。

主要是使用一个HashMap结构存储了用户对象。(该结构定义为了static类型存放在MyServer类中)

 

最后感谢文章:https://blog.csdn.net/lhp15575865420/article/details/75136649 《多个Activity之间共用一个Socket实例》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wyypersist

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值