文章目录

这是服务器端,整个通信软件客户端,服务器端源码包已经上传到我的资源。
manifests
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.socketserver">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".Activity.Function_Socket_Server">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
java
Activity
BaseEventAcitivity
package com.example.socketserver.Activity;
import android.os.Bundle;
import android.view.Window;
import androidx.appcompat.app.AppCompatActivity;
import org.greenrobot.eventbus.EventBus;
import butterknife.ButterKnife;
public abstract class BaseEventActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
getIntentData();
setContentView(getLayoutResId());
ButterKnife.bind(this);// 相当于 activity.subtitle = (TextView) activity.findViewById(R.id.subtitle);
EventBus.getDefault().register(this);//注册订阅者
init();
}
protected void getIntentData() {
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
protected abstract void init();
protected abstract int getLayoutResId();
}
Function_Socket_Server
package com.example.socketserver.Activity;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.example.socketserver.Thread.ListenThread;
import com.example.socketserver.Util.AESUtil;
import com.example.socketserver.Util.ConstantUtil;
import com.example.socketserver.Service.LocalService;
import com.example.socketserver.R;
import com.example.socketserver.Util.ToolUtil;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import butterknife.BindView;
import butterknife.OnClick;
/**
* 服务器界面
*/
public class Function_Socket_Server extends BaseEventActivity {
@BindView(R.id.tv_localAddress)
TextView tv_localAddress;
@BindView(R.id.tv_receivedContent)
TextView tv_receivedContent;
@BindView(R.id.tv_decryptContent)
TextView tv_decryptContent;
@BindView(R.id.edtTxt_server_Content)
TextView edtTxt_server_Content;
@BindView(R.id.edtTxt_server_clientAddress)
TextView edtTxt_server_clientAddress;
@Override
protected int getLayoutResId() {
return R.layout.function_socket_server;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new ListenThread(ConstantUtil.port,Function_Socket_Server.this).start();
}
@Override
protected void init() {
Log.d("Function_Socket_Server","init");
tv_localAddress.setText(ToolUtil.getHostIP());//获取本机局域网ip地址
}
@OnClick({R.id.btn_startListener, R.id.btn_stopListener, R.id.btn_getUser,R.id.btn_server_encryptAndSend})
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_startListener://启动监听
break;
case R.id.btn_stopListener://停止监听
break;
case R.id.btn_getUser://刷新连接到此设备的IP并清空之前接收到的数据
ArrayList<String> connectedIP = getConnectedIP();
StringBuilder resultList = new StringBuilder();
for (String ip : connectedIP) {
resultList.append(ip);
resultList.append("\n");
}
Toast.makeText(Function_Socket_Server.this, "连接到手机上的Ip是"+ resultList.toString(), Toast.LENGTH_LONG).show();
tv_decryptContent.setText("");
tv_receivedContent.setText("");
break;
case R.id.btn_server_encryptAndSend:
Toast.makeText(Function_Socket_Server.this, "未实现功能", Toast.LENGTH_SHORT).show();
break;
}
}
//订阅事件处理,处理 ListenThread 的 EventBus.getDefault().post(str);
@Subscribe(threadMode = ThreadMode.MAIN)//在ui线程执行
public void getData(String data) {
//dismissProgressDialog();
switch (data) {
default:
tv_receivedContent.append(data+"\n");
tv_decryptContent.append(AESUtil.decrypt(ConstantUtil.password, data)+"\n");
break;
}
}
/**
* 获取连接到本机热点上的手机ip
*/
private ArrayList<String> getConnectedIP() {
ArrayList<String> connectedIP = new ArrayList<>();
try {
//通过读取配置文件实现
BufferedReader br = new BufferedReader(new FileReader(
"/proc/net/arp"));
String line;
while ((line = br.readLine()) != null) {
String[] splitted = line.split(" +");
if (splitted.length >= 4) {
String ip = splitted[0];
connectedIP.add(ip);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return connectedIP;
}
}
SQL
SQLiteHelper
package com.example.socketserver.SQL;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class SQLiteHelper extends SQLiteOpenHelper {
//构造socket的数据库
public SQLiteHelper(Context context) {
super(context, "socket.db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
//创建一个名字为 information,3列的表格
//列:1: _id,2: USERNAME,3: PASSWORD
db.execSQL("CREATE TABLE information(_id INTEGER PRIMARY KEY AUTOINCREMENT," +
"USERNAME VARCHAR(20),PASSWORD VARCHAR(30),PRIVATEKEY VARCHAR(1250),PUBLICKEY VARCHAR(350))");//,LATESTIP VARCHAR(15)
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
Thread
ListenThread
package com.example.socketserver.Thread;
import android.content.Context;
import android.util.Log;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 监听线程
*/
public class ListenThread extends Thread {
private ServerSocket serverSocket;
private Context context;
//构造函数,传递,端口,上下文
public ListenThread(int port, Context context) {
try {
serverSocket = new ServerSocket(port);//服务器端:创建绑定到指定 端口 的服务器套接字
} catch (IOException e) {
e.printStackTrace();
}
this.context = context;
}
@Override
public void run() {
Log.d("ListenThread","run");
while (true) {//不断得循环等待连接,读取数据
try {
if (serverSocket != null) {
Socket socket = serverSocket.accept();//调用accept()方法 等待客户连接
final String address = socket.getRemoteSocketAddress().toString();//获取发送客户端的地址
Log.d("ListenThread","new clients socket address:"+address);
//每一个socket单独开启一个线程
ServerHandleSocketThread serverThread = new ServerHandleSocketThread(socket,context);
serverThread.start();
}
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}
ServerHandleSocketThread
package com.example.socketserver.Thread;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.util.Log;
import androidx.annotation.RequiresApi;
import com.example.socketserver.SQL.SQLiteHelper;
import com.example.socketserver.Util.AESUtil;
import com.example.socketserver.Util.ClientSocket;
import com.example.socketserver.Util.ConstantUtil;
import com.example.socketserver.Util.RSAUtil;
import com.example.socketserver.Util.SRConstantsUtil;
import org.greenrobot.eventbus.EventBus;
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.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.Set;
class ServerHandleSocketThread extends Thread{
private Socket socket;
private Context context;
private boolean exit = false;
//在构造中得到要单独会话的socket
public ServerHandleSocketThread(Socket socket, Context context) {
this.socket = socket;
this.context = context;
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void run() {
super.run();
if(!exit)
while(true){
if(exit == true)break;
try {
InputStream inputStream = socket.getInputStream();//实现客户端和服务器端在线程上对话, 读取输入流
if (inputStream != null) {
//读取接收到的数据
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
String dataPacket = in.readLine();//读一行数据
//发送过来的数据不为空,判断数据内容
if (dataPacket != null) {
String clientData = AESUtil.decrypt(ConstantUtil.password, dataPacket);
int cmdBeginIndex1=clientData.indexOf(":")+1;
int cmdEndIndex1 = clientData.indexOf("}");
String cmd=clientData.substring(cmdBeginIndex1,cmdEndIndex1);
//登录,
if (cmd.equals("login")) {
Log.d("HandleSocketThread", "handle login");
//获取登录的用户名
int userNameBeginIndex = clientData.indexOf(":",cmdBeginIndex1) + 1;
int userNameEndIndex = clientData.indexOf("}",userNameBeginIndex);
String username = clientData.substring(userNameBeginIndex, userNameEndIndex);
//获取登录的密码
int passwordBeginIndex = clientData.indexOf(":", userNameBeginIndex) + 1;
int passwordEndIndex = clientData.indexOf("}", userNameEndIndex + 1);
String password = clientData.substring(passwordBeginIndex, passwordEndIndex);
SQLiteDatabase db;
ContentValues values;
SQLiteHelper helper = new SQLiteHelper(context);
db = helper.getReadableDatabase();
//执行查询的SQL语句,查询账号,密码是否存在数据库中
//密码加密加密存储,所以得把得到的密码加密查询
Cursor cursor = db.rawQuery("select * from information where USERNAME=? and PASSWORD=?",
new String[]{username, AESUtil.encrypt(ConstantUtil.password, password)});
//查无此数据,返回
if (cursor.getCount() == 0) {
Log.d("HandleSocketThread", "login,无此用户数据");
//返回登录失败信息
writeResponse(socket, SRConstantsUtil.LOGIN_FAILED);
socket.close();
exit = true;
}
//有此数据,准许登录
else if(cursor.getCount()!=0){
cursor.moveToFirst();
Log.d("HandleSocketThread ", "Login success "+"账号:" + cursor.getString(1) + "密码:" + cursor.getString(2));
Set<String> keyset = ClientSocket.keySet();
Iterator<String> it = keyset.iterator();
while (it.hasNext()) {
String username1 = it.next();
if (username1.equals(username)) {
//返回该账号已经登录信息
writeResponse(socket,SRConstantsUtil.LOGIN_ALREADY);
cursor.close();
db.close();
return;
}
}
ClientSocket.put(username,socket);
Log.d("HandleSocketThread","add socket,clientSocketSize:"+String.valueOf(ClientSocket.size()));
//返回登录成功信息
writeResponse(socket,SRConstantsUtil.LOGIN_SUCCESS);
}
cursor.close();
db.close();
}
//连接通信,保持socket存在获取客户聊天使用的socket
else if(cmd.equals("connect")){
Log.d("HandleSocketThread", "connect ");
//获取登录的用户名
int userNameBeginIndex = clientData.indexOf(":",cmdBeginIndex1) + 1;
int userNameEndIndex = clientData.indexOf("}",userNameBeginIndex);
String username = clientData.substring(userNameBeginIndex, userNameEndIndex);
//返回连接成功信息
writeResponse(socket,SRConstantsUtil.CONNECTSUCCESS);
}
//获得好友公钥
else if(cmd.equals("addFriend")){
//获取添加的好友的用户名
int userNameBeginIndex = clientData.indexOf(":",cmdBeginIndex1) + 1;
int userNameEndIndex = clientData.indexOf("}",userNameBeginIndex);
String frinedUsername = clientData.substring(userNameBeginIndex, userNameEndIndex);
//查询数据库有无此数用户名
SQLiteDatabase db;
ContentValues values;
SQLiteHelper helper = new SQLiteHelper(context);
db = helper.getReadableDatabase();
//执行查询的SQL语句
Cursor cursor = db.rawQuery("select * from information where USERNAME=? ",
new String[]{frinedUsername});
//查无此数据,存储返回
if (cursor.getCount() == 0) {
Log.d("HandleSocketThread", "no this frinedUsername ");
//返回获取公钥错误
writeResponse(socket,SRConstantsUtil.NOFRIENDACCOUNT);
}
else {
cursor.moveToFirst();//这句话一定要有,不然无法选中第一条!!!!!
String publicKey = cursor.getString(4);
if(publicKey != null){
Log.d("ServerThread", "发送公钥");
String publicKeyPacket = "{response:sendPublicKey}{key:"+publicKey+"}";
writeResponse(socket,publicKeyPacket);
}
}
cursor.close();
db.close();
}
//自己私钥
else if(cmd.equals("getPrivateKey")){
//{cmd:getPrivateKey}{username:"+username+"}"
//获取登录的用户名
int userNameBeginIndex = clientData.indexOf(":",cmdBeginIndex1) + 1;
int userNameEndIndex = clientData.indexOf("}",userNameBeginIndex);
String username = clientData.substring(userNameBeginIndex, userNameEndIndex);
//查询数据库有无此数用户名
SQLiteDatabase db;
ContentValues values;
SQLiteHelper helper = new SQLiteHelper(context);
db = helper.getReadableDatabase();
//执行查询的SQL语句
Cursor cursor = db.rawQuery("select * from information where USERNAME=? ",
new String[]{username});
//查无此数据,存储返回
if (cursor.getCount() == 0) {
Log.d("HandleSocketThread", "get public key error");
//返回获取公钥错误
writeResponse(socket,SRConstantsUtil.GETPUBLICKEYERROR);
}
else {
cursor.moveToFirst();//这句话一定要有,不然无法选中第一条!!!!!
String privateKey = cursor.getString(3);
Log.d("ServerThread", "发送私钥");
//返回注册失败信息
String publicKeyPacket = "{response:sendPrivateKey}{key:"+privateKey+"}";
writeResponse(socket,publicKeyPacket);
}
cursor.close();
db.close();
}
//发送消息,
else if (cmd.equals("sendMsg")) {
Log.d("HandleSocketThread", "handle sendmsg");
boolean recIsOnline = false;
//提取接收者账号
//{cmd:sendMsg}{receiverAccount:123}{msg:xxxx}{sender:xxx}
int recBeginIndex=clientData.indexOf(":",cmdBeginIndex1)+1;
int recIndex=clientData.indexOf("}", recBeginIndex);
String recAccount=clientData.substring(recBeginIndex,recIndex);
//根据接收者账号 遍历寻找是否在线
//判断登录存储的map中有 数据接收者账号
//有就从map中找到 相应接收者账号的socket,提取cilentData中的msg,并且发送消息出去给相应客户
//返回 发送者发送成功的消息,关闭发送者 发送数据的socket
Set<String> keyset = ClientSocket.keySet();
Iterator<String> it = keyset.iterator();
while (it.hasNext()){
String username = it.next();
if(username.equals(recAccount))
{
Log.d("HandleSocketThread","handle sendmsg,search and get recAccount:"+username);
Log.d("HandleSocketThread","handle sendmsg,receiver is online ");
recIsOnline = true;
int msgBeginIndex=clientData.indexOf(":",recBeginIndex)+1;
int msgIndex=clientData.indexOf("}", recIndex+1);
String msg=clientData.substring(msgBeginIndex,msgIndex);
int nextBeginIndex=clientData.indexOf("{",msgBeginIndex);
int sendBeginIndex=clientData.indexOf(":",nextBeginIndex)+1;
int sendIndex=clientData.indexOf("}", sendBeginIndex);
String sendAccount=clientData.substring(sendBeginIndex,sendIndex);
Log.d("HandleSocketThread","handle sendmsg,sendAccount is "+sendAccount+",now send "+msg+" to "+username);
String msgPacket = "{response:transmit}{sender:"+sendAccount+"}{msg:"+msg+"}";
//根据keyset 找到 对应接收者的socket
Socket socket1 = ClientSocket.get(username);
writeResponse(socket1,msgPacket);
Log.d("HandleSocketThread","handle sendmsg,msg has sent");
//返回发送成功信息
writeResponse(socket,SRConstantsUtil.MSGSEND_SUCCESS);
//找到发送对象并且发送完毕,退出本次发送循环
break;
}
}
//不在线,在线标志位 由遍历过程 改变
if(recIsOnline == false){
Log.d("HandleSocketThread","handle sendmsg,receiver isn't online ");
//返回接收者不在线信息
writeResponse(socket,SRConstantsUtil.MSGSEND_FAILED_NOT_ONLINE);
}
exit = true;
}
//注册,关闭socket
else if (cmd.equals("register")) {
Log.d("HandleSocketThread", "handle register");
//获取注册的用户名
int userNameBeginIndex = clientData.indexOf(":",cmdBeginIndex1) + 1;
int userNameEndIndex = clientData.indexOf("}",userNameBeginIndex);
String username = clientData.substring(userNameBeginIndex, userNameEndIndex);
//获取注册的密码
int passwordBeginIndex = clientData.indexOf(":", userNameBeginIndex) + 1;
int passwordEndIndex = clientData.indexOf("}", userNameEndIndex + 1);
String password = clientData.substring(passwordBeginIndex, passwordEndIndex);
//查询数据库有无此数用户名
SQLiteDatabase db;
ContentValues values;
SQLiteHelper helper = new SQLiteHelper(context);
db = helper.getReadableDatabase();
//执行查询的SQL语句
Cursor cursor = db.rawQuery("select * from information where USERNAME=? ",
new String[]{username});
//查无此数据,存储返回
if (cursor.getCount() == 0) {
Log.d("HandleSocketThread", "register,注册用户名无冲突");
db = helper.getWritableDatabase();
values = new ContentValues();//创建Contentvalues对象
values.put("USERNAME", username);//将数据添加到ContentValues对象
values.put("PASSWORD", AESUtil.encrypt(ConstantUtil.password, password));
RSAUtil.genKeyPair();
String publicKey = RSAUtil.getPublicKey();
String privateKey = RSAUtil.getPrivateKey();
values.put("PRIVATEKEY", privateKey);
values.put("PUBLICKEY", publicKey);
db.insert("information", null, values);
Log.d("HandleSocketThread", "register,存储成功");
//返回注册成功信息
writeResponse(socket,SRConstantsUtil.REGISTER_SUCCESS);
}
else {
Log.d("ServerThread", "注册用户名冲突");
//返回注册失败信息
writeResponse(socket,SRConstantsUtil.REGISTER_FAILED);
}
cursor.close();
db.close();
socket.close();
exit = true;
}
//用户退出APP消息,关闭socket,删除相应的map中的socket
else if(cmd.equals("exitApp")) {
Log.d("HandleSocketThread", "handle exist");
//{cmd:exitAPP}{username:123}
int userNameBeginIndex=clientData.indexOf(":",cmdBeginIndex1)+1;
int userNameEndIndex = clientData.indexOf("}",userNameBeginIndex);
String username=clientData.substring(userNameBeginIndex,userNameEndIndex);
ClientSocket.remove(username);
Log.d("HandleSocketThread","handle exist,clientSocketSize:"+String.valueOf(ClientSocket.size()));
//返回收到退出信息
writeResponse(socket,SRConstantsUtil.EXISTAPPNORMAL);
socket.close();
exit = true;
}
else Log.d("HandleSocketThread","数据格式错误");
EventBus.getDefault().post(dataPacket);//发布事件,把获取到的信息显示出来
}
}
}
catch (IOException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
private void writeResponse(Socket socket,String response) throws IOException {
OutputStream outputStream = socket.getOutputStream();
OutputStreamWriter opsw = new OutputStreamWriter(outputStream);//
BufferedWriter writer = new BufferedWriter(opsw);
writer.write(AESUtil.encrypt(ConstantUtil.password, response)+ "\r\n\r\n");//由于socket使用缓冲区进行读写数据,因此使用\r\n\r\n用于表明数据已写完.不加这个会导致数据无法发送
writer.flush();
}
}
Util
AESUtil
package com.example.socketserver.Util;
import android.util.Log;
import java.io.UnsupportedEncodingException;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* AES加密工具类
*/
public class AESUtil {
// private static final String CipherMode = "AES/ECB/PKCS5Padding";使用ECB加密,不需要设置IV,但是不安全
private static final String CipherMode = "AES/CFB/NoPadding";//使用CFB加密,需要设置IV;CFB(Cipher FeedBack Mode,加密反馈),不填充
/**
* 生成加密后的密钥
*
* @param password 密钥种子
* @return isSucceed
*/
private static SecretKeySpec createKey(String password) {
byte[] data = null;
if (password == null) {
password = "";
}
StringBuilder sb = new StringBuilder(32);//32位容量的字符串
sb.append(password);//字符串追加密码
while (sb.length() < 32) {
sb.append("0");//少于32位,追加‘0’
}
if (sb.length() > 32) {
//setLength(newLength)
//如果 newLength 参数小于当前长度,则长度将更改为指定的长度。
//如果 newLength 参数大于或等于当前长度,则将追加有效的 null 字符 ('u0000'),使长度满足 newLength 参数。
sb.setLength(32);
}
try {
data = sb.toString().getBytes("UTF-8");//得到 使用UTF-8编码表 的一个系统默认的编码格式的 字节数组
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return new SecretKeySpec(data, "AES");//根据32字节的数据,生成一个AES算法生成的密钥
}
// /** 加密字节数据 **/
private static byte[] encrypt(byte[] content, String password) {
try {
SecretKeySpec key = createKey(password);//根据密钥种子生成密钥
System.out.println(key);
Cipher cipher = Cipher.getInstance(CipherMode);
//初始化Cipher,mode指定是加密还是解密,key为公钥或密钥;ENCRYPT_MODE加密模式,
// 实例化IvParameterSpec对象,使用指定的初始化向量
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(
new byte[cipher.getBlockSize()]));
//使用CFB加密CFB(Cipher FeedBack Mode,加密反馈)、不填充的方式
//init为 加密形式
//返回btye[]数组
return cipher.doFinal(content);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// /** 加密(结果为16进制字符串) **/
public static String encrypt(String password, String content) {
Log.d("加密前", "seed=" + password + "\ncontent=" + content);
byte[] data = null;//byte字节数组
try {
data = content.getBytes("UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
data = encrypt(data, password);//加密字节数据
String result = byte2hex(data);//字节转hex
Log.d("加密后", "result=" + result);
return result;
}
// /** 解密字节数组 **/
private static byte[] decrypt(byte[] content, String password) {
try {
SecretKeySpec key = createKey(password);
Cipher cipher = Cipher.getInstance(CipherMode);
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(
new byte[cipher.getBlockSize()]));
return cipher.doFinal(content);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// /** 解密16进制的字符串为字符串 **/
public static String decrypt(String password, String content) {
Log.d("解密前", "seed=" + password + "\ncontent=" + content);
byte[] data = null;
try {
data = hex2byte(content);
} catch (Exception e) {
e.printStackTrace();
}
data = decrypt(data, password);
if (data == null)
return null;
String result = null;
try {
result = new String(data, "UTF-8");
Log.d("解密后", "result=" + result);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
// /** 字节数组转成16进制字符串 **/
private static String byte2hex(byte[] b) { // 一个字节的数,
StringBuilder sb = new StringBuilder(b.length * 2);
String tmp ;
for (byte aB : b) {
// 整数转成十六进制表示
tmp = (Integer.toHexString(aB & 0XFF));
if (tmp.length() == 1) {
sb.append("0");
}
sb.append(tmp);
}
return sb.toString().toUpperCase(); // 转成大写
}
// /** 将hex字符串转换成字节数组 **/
private static byte[] hex2byte(String inputString) {
if (inputString == null || inputString.length() < 2) {
return new byte[0];
}
inputString = inputString.toLowerCase();
int l = inputString.length() / 2;
byte[] result = new byte[l];
for (int i = 0; i < l; ++i) {
String tmp = inputString.substring(2 * i, 2 * i + 2);
result[i] = (byte) (Integer.parseInt(tmp, 16) & 0xFF);
}
return result;
}
}
ClientSocket
package com.example.socketserver.Util;
import java.net.Socket;
import java.util.HashMap;
import java.util.Set;
public class ClientSocket {
private static HashMap<String, Socket> clients = new HashMap<String,Socket>();// 创建一个集合存放所有的客户端 的用户名,和客户socket
public static void put(String username, Socket userSocket){
clients.put(username,userSocket);
}
public static int size() {
return clients.size();
}
public static Socket get(String username) {
return clients.get(username);
}
public static void remove(String username) {
clients.remove(username);
}
public static Set<String> keySet() {
return clients.keySet();
}
}
ConstantUtil
package com.example.socketserver.Util;
/**
* 常量类
*/
public class ConstantUtil {
public static final int TIME_MILLIS = 5 * 1000;//连接超时时间
public static final int port = 25256;//端口号
public static final String password = "123456885";//加密所使用的密钥
public static final String CODE_TIMEOUT = "pzl0";//连接超时
public static final String CODE_SUCCESS = "pzl1";//连接成功
public static final String CODE_UNKNOWN_HOST = "pzl2";//错误-未知的host
public static final String SERVERIP = "SERVERIP";//服务器地址
}
RSAUtil
package com.example.socketserver.Util;
import android.os.Build;
import androidx.annotation.RequiresApi;
import java.util.Base64;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
public class RSAUtil {
/**
* 密钥长度 于原文长度对应 以及越长速度越慢
*/
private final static int KEY_SIZE = 1536;
private static final String CipherMode = "RSA";
/**
* 用于封装随机产生的公钥与私钥
*/
private static Map<String, String> keyMap = new HashMap<String, String>();
//公钥
public static final String PUBLIC_KEY = "RSAPublicKey";
//私钥
public static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* 随机生成密钥对
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public static void genKeyPair() throws NoSuchAlgorithmException {
// KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(CipherMode);
// 初始化密钥对生成器
keyPairGen.initialize(KEY_SIZE, new SecureRandom());
// 生成一个密钥对,保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
// 得到私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// 得到公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());
// 得到私钥字符串
String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());
// 将公钥和私钥保存到Map
//0表示公钥
keyMap.put(PUBLIC_KEY, publicKeyString);
//1表示私钥
keyMap.put(PRIVATE_KEY, privateKeyString);
}
public static String getPublicKey(){
return keyMap.get(PUBLIC_KEY);
}
public static String getPrivateKey(){
return keyMap.get(PRIVATE_KEY);
}
/**
* RSA公钥加密
*
* @param str 加密字符串
* @param publicKey 公钥
* @return 密文
* @throws Exception 加密过程中的异常信息
*/
@RequiresApi(api = Build.VERSION_CODES.O)
public static String encrypt(String str, String publicKey) throws Exception {
//base64编码的公钥
byte[] decoded = Base64.getDecoder().decode(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(CipherMode).generatePublic(new X509EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance(CipherMode);
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
String outStr = Base64.getEncoder().encodeToString(cipher.doFinal(str.getBytes("UTF-8")));
return outStr;
}
/**
* RSA私钥解密
*
* @param str 加密字符串
* @param privateKey 私钥
* @return 明文
* @throws Exception 解密过程中的异常信息
*/
public static String decrypt(String str, String privateKey) throws Exception {
//64位解码加密后的字符串
byte[] inputByte = Base64.getDecoder().decode(str);
//base64编码的私钥
byte[] decoded = Base64.getDecoder().decode(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(CipherMode).generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance(CipherMode);
cipher.init(Cipher.DECRYPT_MODE, priKey);
String outStr = new String(cipher.doFinal(inputByte));
return outStr;
}
}
SRConstantsUtil
package com.example.socketserver.Util;
public class SRConstantsUtil {
public static final String REGISTER_SUCCESS = "{response:register_success}";//注册成功
public static final String REGISTER_FAILED = "{response:register_failed}";//注册失败
public static final String LOGIN_SUCCESS = "{response:login_success}";//登录成功
public static final String LOGIN_FAILED = "{response:login_failed}";//登录识别
public static final String LOGIN_ALREADY = "{response:login_already}";//登录识别
public static final String MSGSEND_SUCCESS = "{response:send_msg_success}";//连接成功
public static final String MSGSEND_FAILED = "{response:send_msg_failed}";//连接成功
public static final String MSGSEND_FAILED_NOT_ONLINE = "{response:receiver_not_online}";//接收者不在线
public static final String MSGSEND_FAILED_NOT_EXIST = "{response:receiver_not_exist}";//接收者错误
public static final String EXISTAPPNORMAL = "{response:exist_normal}";//退出成功
public static final String EXISTAPPABNORMAL = "{response:exist_abnormal}";//退出错误
public static final String CONNECTSUCCESS = "{response:connect_success}";//连接成功
public static final String NOFRIENDACCOUNT = "{response:no_user_account}";//没有此好友用户
public static final String GETPUBLICKEYERROR = "{response:get_privateKey_error}";//获取公钥错误
//public static final String PRIVATEKEY = "{response:privateKey}{key:";//获取公钥错误
}
ToolUtil
package com.example.socketserver.Util;
import android.util.Log;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
/**
* 工具类
*/
public class ToolUtil {
/**
* 获取ip地址
* 如果是移动网络,会显示自己的公网IP,如果是局域网,会显示局域网IP
* 因此本例中服务器端需要断开移动网络以得到本机局域网IP
*/
public static String getHostIP() {
String hostIp = null;
try {
Enumeration nis = NetworkInterface.getNetworkInterfaces();
InetAddress ia;
while (nis.hasMoreElements()) {
NetworkInterface ni = (NetworkInterface) nis.nextElement();
Enumeration<InetAddress> ias = ni.getInetAddresses();
while (ias.hasMoreElements()) {
ia = ias.nextElement();
if (ia instanceof Inet6Address) {
continue;// skip ipv6
}
String ip = ia.getHostAddress();
if (!"127.0.0.1".equals(ip)) {
hostIp = ia.getHostAddress();
break;
}
}
}
} catch (SocketException e) {
Log.i("error", "SocketException");
e.printStackTrace();
}
return hostIp;
}
/**
* 判断地址是否为IPV4地址
*/
public static boolean IsIpv4(String ipv4) {
if (ipv4 == null || ipv4.length() == 0) {
return false;//字符串为空或者空串
}
String[] parts = ipv4.split("\\.");//因为java doc里已经说明, split的参数是reg, 即正则表达式, 如果用"|"分割, 则需使用"\\|"
if (parts.length != 4) {
return false;//分割开的数组根本就不是4个数字
}
for (String part : parts) {
try {
int n = Integer.parseInt(part);
if (n < 0 || n > 255) {
return false;//数字不在正确范围内
}
} catch (NumberFormatException e) {
return false;//转换数字不正确
}
}
return true;
}
}
res
layout
funtion_socket_server.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@mipmap/bg2"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="55dp">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:textSize="30dp"
android:textColor="#fec"
android:text="网络加密-服务器端" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp"
android:layout_margin="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="本机地址:"
android:textSize="18sp"
android:textColor="#fec"/>
<TextView
android:id="@+id/tv_localAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textColor="#fec"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_margin="5dp">
<Button
android:id="@+id/btn_startListener"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="启动监听"
android:background="#5fff"
android:textSize="18sp"
android:textColor="#fec"/>
<Button
android:id="@+id/btn_stopListener"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="停止监听"
android:textSize="18sp"
android:textColor="#fec"
android:background="#5791"/>
<Button
android:id="@+id/btn_getUser"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="刷新用户"
android:textColor="#fec"
android:textSize="18sp"
android:background="#5fff"
android:padding="5dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp"
android:layout_margin="5dp"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="好友账号:"
android:textSize="18sp"
android:textColor="#fec"/>
<EditText
android:id="@+id/edtTxt_server_clientAddress"
android:layout_width="match_parent"
android:text="192.168.1.105"
android:layout_height="wrap_content"
android:singleLine="true"
android:textColor="#fec"/>
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_margin="5dp"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送文本内容:"
android:textColor="#fec"
android:textSize="15sp"/>
<EditText
android:id="@+id/edtTxt_server_Content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff"
android:padding="10dp"
android:text="123木头人"
android:textColor="#fec"
android:backgroundTint="#33000000" />
</LinearLayout>
</ScrollView>
<Button
android:id="@+id/btn_server_encryptAndSend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:text="加密并发送"
android:textColor="#fec"
android:background="#5791"
android:layout_margin="5dp"
android:visibility="gone"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="接收到的密文:"
android:textColor="#fec"
android:textSize="18sp"/>
<TextView
android:id="@+id/tv_receivedContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:textColor="#fec"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="解密后的明文:"
android:textColor="#fec"
android:textSize="18sp"/>
<TextView
android:id="@+id/tv_decryptContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:textColor="#fec"/>
</LinearLayout>
</ScrollView>
</LinearLayout>
values
styles.xml
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
gradle与客户端一样