在PC端IDEA IDE使用ServerSocket,在android端使用Socket,搭建简单的聊天室
Posted wyy_persist
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在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实例》
以上是关于在PC端IDEA IDE使用ServerSocket,在android端使用Socket,搭建简单的聊天室的主要内容,如果未能解决你的问题,请参考以下文章
Intellij IDEA debug断点调试技巧与总结详解篇