android基于socket的局域网内服务器与客户端加密通信
发布时间:2017-04-13 来源:查字典编辑
摘要:实现了基本的socket通信(即两台设备,一台用作服务器,一台用作客户端),服务器进行监听,客户端发送加密数据到服务器,服务器进行解密得到明...
实现了基本的socket通信(即两台设备,一台用作服务器,一台用作客户端),服务器进行监听,客户端发送加密数据到服务器,服务器进行解密得到明文。
注意:本项目中使用了ButterKnife及EventBus作为辅助工具,通信建立时默认网络正常(未做局域网网络环境检测),加密方式为AES加密
1.效果图:
(1)客户端
(2)服务器端
2.界面布局部分
(1)服务器端布局 function_socket_server.xml
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
(2)客户端布局 function_socket_client.xml
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
(3)用到的style
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
3.功能代码
(1)基类 BaseEventActivity.Java
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | import android.os.Bundle; import android.support.v7.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); getIntentData(); setContentView(getLayoutResId()); ButterKnife.bind(this); 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(); } |
(2)服务器主界面 Function_Socket.java
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.view.View; import android.widget.TextView; 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; private LocalService localService;//用于启动监听的服务 private ServiceConnection sc;//服务连接 @Override protected void init() { tv_localAddress.setText(ToolUtil.getHostIP()); sc = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { LocalService.LocalBinder localBinder = (LocalService.LocalBinder) service; localService = localBinder.getService(); localService.startWaitDataThread(); ToastUtil.showToast(Function_Socket_Server.this, "监听已启动"); } @Override public void onServiceDisconnected(ComponentName name) { } }; connection(); } @Subscribe(threadMode = ThreadMode.MAIN) public void getData(String data) { tv_receivedContent.setText(data); tv_decryptContent.setText(AESUtil.decrypt(ConstantUtil.password, data)); } /** * 绑定service */ private void connection() { Intent intent = new Intent(this, LocalService.class); bindService(intent, sc, BIND_AUTO_CREATE); } @Override protected int getLayoutResId() { return R.layout.function_socket_server; } /** * 获取连接到本机热点上的手机ip */ private ArrayList |
(3)客户端主界面 Function_Socket_Client.java
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | import android.app.ProgressDialog; import android.util.Log; import android.view.View; import android.widget.EditText; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import butterknife.BindView; import butterknife.OnClick; /** * 客户端界面 */ public class Function_Socket_Client extends BaseEventActivity { @BindView(R.id.edtTxt_Content) EditText edtTxt_Content; @BindView(R.id.edtTxt_serverAddress) EditText edtTxt_serverAddress; private ProgressDialog mProgressDialog;//加载的小菊花 /** * 初始化 */ @Override protected void init() { } @Override protected int getLayoutResId() { return R.layout.function_socket_client; } @OnClick(R.id.btn_encryptAndSend) public void onClick(View v) { switch (v.getId()) { case R.id.btn_encryptAndSend: String s = edtTxt_Content.getText().toString().trim(); String ip = edtTxt_serverAddress.getText().toString().trim(); if (ToolUtil.IsIpv4(ip)) { new SendDataThread(ip, AESUtil.encrypt(ConstantUtil.password, s), ConstantUtil.port).start();//消息发送方启动线程发送消息 showProgressDialog("尝试发送数据到ntt" + ip, true); } else { ToastUtil.showToast(this, "IP不合法!"); } break; } } /** * 连接结果 * * @param resultCode 0:连接超时;1:发送成功 2:失败 */ @Subscribe(threadMode = ThreadMode.MAIN) public void sendResult(Integer resultCode) { Log.i("succ", "=" + resultCode); dismissProgressDialog(); switch (resultCode) { case ConstantUtil.CODE_SUCCESS: ToastUtil.showToast(this, "发送成功"); break; case ConstantUtil.CODE_TIMEOUT: ToastUtil.showToast(this, "连接超时"); break; case ConstantUtil.CODE_UNKNOWN_HOST: ToastUtil.showToast(this, "错误-未知的host"); break; } } /** * 数据加载小菊花 * * @param msg 内容 * @param isCancel 是否允许关闭 true - 允许 false - 不允许 */ public void showProgressDialog(final String msg, final boolean isCancel) { runOnUiThread(new Runnable() { @Override public void run() { try { if (mProgressDialog == null) { mProgressDialog = new ProgressDialog(Function_Socket_Client.this); } if (mProgressDialog.isShowing()) { return; } mProgressDialog.setMessage(msg); mProgressDialog.setCancelable(isCancel); mProgressDialog.setCanceledOnTouchOutside(false); mProgressDialog.setOnCancelListener(null); mProgressDialog.show(); } catch (Exception e) { e.printStackTrace(); } } }); } /** * 隐藏数据加载的进度小菊花 **/ public void dismissProgressDialog() { try { if (mProgressDialog != null && mProgressDialog.isShowing()) { runOnUiThread( new Runnable() { @Override public void run() { mProgressDialog.dismiss(); } } ); } } catch (Exception e) { e.printStackTrace(); } } } |
(4)LocalService.java
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; /** * 此服务用于启动监听线程 */ public class LocalService extends Service { private IBinder iBinder = new LocalService.LocalBinder(); @Override public IBinder onBind(Intent intent) { return iBinder; } @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_STICKY; } public void startWaitDataThread() { new ListenThread(ConstantUtil.port).start(); } //定义内容类继承Binder public class LocalBinder extends Binder { //返回本地服务 public LocalService getService() { return LocalService.this; } } } |
(5)ListenThread.java
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | import org.greenrobot.eventbus.EventBus; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; /** * 监听线程 */ public class ListenThread extends Thread { private ServerSocket serverSocket; public ListenThread(int port) { try { serverSocket = new ServerSocket(port); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { while (true) { try { if (serverSocket != null) { Socket socket = serverSocket.accept(); InputStream inputStream = socket.getInputStream(); if (inputStream != null) { BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); String str; str = in.readLine(); EventBus.getDefault().post(str); socket.close(); } } } catch (IOException e) { e.printStackTrace(); } } } } |
(6)SendDataThread.java
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | import android.util.Log; import org.greenrobot.eventbus.EventBus; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; import java.net.UnknownHostException; /** * 数据发送线程 */ public class SendDataThread extends Thread { private Socket socket; private String ip;//接收方的IP private int port;//接收方的端口号 private String data;//准备发送的数据 public SendDataThread(String ip, String data, int port) { this.ip = ip; this.data = data; this.port = port; } @Override public void run() { try { socket = new Socket(); socket.connect(new InetSocketAddress(ip,port),ConstantUtil.TIME_MILLIS);//设置超时时间 } catch (UnknownHostException e) { EventBus.getDefault().post(ConstantUtil.CODE_UNKNOWN_HOST); Log.d("error", "SendDataThread.init() has UnknownHostException" + e.getMessage()); } catch (SocketTimeoutException e) { EventBus.getDefault().post(ConstantUtil.CODE_TIMEOUT); Log.d("error", "SendDataThread.init() has TimeoutException:" + e.getMessage()); }catch (IOException e){ Log.d("error", "SendDataThread.init() has IOException:" + e.getMessage()); } if (socket != null&&socket.isConnected()) { try { OutputStream ops = socket.getOutputStream(); OutputStreamWriter opsw = new OutputStreamWriter(ops); BufferedWriter writer = new BufferedWriter(opsw); writer.write(data + "rnrn");//由于socket使用缓冲区进行读写数据,因此使用rnrn用于表明数据已写完.不加这个会导致数据无法发送 EventBus.getDefault().post(ConstantUtil.CODE_SUCCESS); writer.flush(); } catch (IOException e) { e.printStackTrace(); } } } } |
(7)AESUtil.java
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | 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 /** * 生成加密后的密钥 * * @param password 密钥种子 * @return isSucceed */ private static SecretKeySpec createKey(String password) { byte[] data = null; if (password == null) { password = ""; } StringBuilder sb = new StringBuilder(32); sb.append(password); while (sb.length() < 32) { sb.append("0"); } if (sb.length() > 32) { sb.setLength(32); } try { data = sb.toString().getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return new SecretKeySpec(data, "AES"); } // /** 加密字节数据 **/ private static byte[] encrypt(byte[] content, String password) { try { SecretKeySpec key = createKey(password); System.out.println(key); Cipher cipher = Cipher.getInstance(CipherMode); cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec( new byte[cipher.getBlockSize()])); 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; try { data = content.getBytes("UTF-8"); } catch (Exception e) { e.printStackTrace(); } data = encrypt(data, password); String result = byte2hex(data); 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; } } |
(8)ConstantUti.java
?
1 2 3 4 5 6 7 8 9 10 11 | /** * 常量类 */ 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 int CODE_TIMEOUT = 0;//连接超时 public static final int CODE_SUCCESS = 1;//连接成功 public static final int CODE_UNKNOWN_HOST = 2;//错误-未知的host } |
(9)ToolUtil.java
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | 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 |
(10)ToastUtil.java
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import android.content.Context; import android.widget.Toast; public class ToastUtil { private static Toast mToast = null; /** * Toast方法 * * @param text 需要展示的文本 * @param context 所需上下文 */ public static void showToast(Context context, String text) { if (text != null) { if (mToast == null) { mToast = Toast.makeText(context, text, Toast.LENGTH_SHORT); } else { mToast.setText(text); mToast.setDuration(Toast.LENGTH_SHORT); } mToast.show(); } } } |
3.权限及声明
?
1 2 3 4 5 |