安卓下的TCP通信socket编程
⼀、安卓下的Socket基本实现原理
服务端:⾸先声明⼀个ServerSocket对象并指定端⼝号,然后调⽤ServerSocket的accept( )⽅法接收客户端的数据。accept()⽅法在没有客户端请求连接之前处于阻塞状态,⼀旦接收到连接请求,则通过输⼊流读取接收的数据。代码实例如下
1import java.io.DataInputStream;
2import java.*;
3public class TCPServer {
4
5public static void main(String[] args) throws Exception{
6        ServerSocket ss = new ServerSocket(8000);
7//不⽌接受⼀个客户端
8while (true) {
9        Socket s = ss.accept();//接受⼀个连接
10        DataInputStream dis = new InputStream());//输⼊管道
11        System.out.adUTF());
12        dis.close();
13        s.close();
14
15        }
16    }
17
18 }
客户端:创建⼀个Socket对象,指定服务器端的ip地址和端⼝号,申请连接。通过输⼊流InPutStream读取服务端的数据,通过输出流OutPutStream向服务端写数据
1import java.io.DataOutputStream;
2import java.io.IOException;
3import java.io.OutputStream;
4import java.*;
5public class TCPClient {
6
7public static void main(String[] args) throws Exception {
8        Socket s = new Socket("192.168.1.100", 8000);//申请链接
9        OutputStream os = s.getOutputStream();
10        DataOutputStream dos = new DataOutputStream(os);
11        dos.writeUTF("hello server!");
12        dos.flush();
13        dos.close();
14        s.close();
15    }
16 }
⼆、安卓下实现socket通信
注意:⼀定要添加⽹络访问权限,⼀开始没有添加⼀直报异常,异常信息为:SocketException:socket failed:EACCES(Permission denied)
在l⽂件中添加<uses-permission android:name="android.permission.INTERNET" />
我在最刚开始写这个安卓APP的时候没有加这个⽹络权限,然后报这个异常,在⽹上知道是这个原因之后加了⽹络权限,还是报异常,这让我⼗分苦恼,还尝试了降低安卓运⾏版本的办法,试图降到4.0以下
的版本,发现不能实现。后来意外发现在我的⼿机上运⾏安卓程序时出现的是这样的情况
⽽运⾏其他安卓程序时是这样的
于是我发现问题的所在了,还是因为我的⼿机APP没有访问⽹络的权限,可是我明明加了呀,⽆奈之下我只好尝试将这个APP卸载掉,再重新安装试试,⼀试还真可以了,简直是⼀个⼤⼤的惊喜!解决这个
问题之后,运⾏的时候APP没有在崩掉,但是并没有连上开发板通信,打log程序运⾏到Socket socket = new Socket("192.168.1.100",9500)就不往下运⾏了,这个问题⼜困扰到我了,查看⽇志信息发现
原来在安卓⾥⾯,涉及到⽹络连接等耗时操作时,不能将其放在UI主线程中,需要添加⼦线程,在⼦线程进⾏⽹络连接,这就涉及到安卓线程间的通信了,⽤Handle来实现。
三、Handler
handle的定义:主要接受⼦线程发送的数据, 并⽤此数据配合主线程更新UI.
解释: 当应⽤程序启动时,Android⾸先会开启⼀个主线程 (也就是UI线程) , 主线程为管理界⾯中的UI控件,进⾏事件分发, ⽐如说, 你要是点击⼀个 Button, Android会分发事件到Button上,来响应你的操作。如果此时需要⼀个耗时的操作,例如: 联⽹读取数据,或者读取本地较⼤的⼀个⽂件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界⾯会出现假死现象, 如果5秒钟还没有完成的话,会收到Android系统的⼀个错误提⽰ "强制关闭".  这个时候我们需要把这些耗时的操作,放在⼀个⼦线程中,更新UI只能在主线程中更新,⼦线程中操作是危险的. 这个时候,Handler就出现了来解决这个复杂的问题,由于Handler运⾏在主线程中(UI线程中),它与⼦线程可以通
过Message对象来传递数据,这个时候,Handler就承担着接受⼦线程传过来的(⼦线程⽤sedMessage()⽅法传弟)Message对象,⾥⾯包含数据, 把这些消息放⼊主线程队列中,配合主线程进⾏更新UI。
下⾯为安卓客户端的MainActivity.java的代码
1package wApp;
2
3import java.io.BufferedReader;
4import java.io.IOException;
5import java.io.InputStream;
6import java.io.InputStreamReader;
7import java.io.OutputStream;
8import java.io.PrintStream;
9import java.io.PrintWriter;
10import java.math.MathContext;
11import java.Socket;
12import java.UnknownHostException;
13import java.util.Map;
14
15import com.tanxiaoyi.save.UserInfoUtil;
16import android.annotation.SuppressLint;
17import android.app.Activity;
t.Context;
t.Intent;
t.SharedPreferences;
t.SharedPreferences.Editor;
22import android.os.Bundle;
23import android.os.Handler;
24import android.os.Message;
25import android.os.StrictMode;
TextUtils;
27import android.util.Log;
28import android.view.Menu;
29import android.view.MenuItem;
30import android.view.View;
31import android.view.View.OnClickListener;
32import android.widget.Button;
33import android.widget.CheckBox;
34import android.widget.EditText;
35import android.widget.Toast;
36
37
38public class MainActivity extends Activity {
39protected static final String TAG = "TXY";
40//定义服务端的ip地址和端⼝号
41private final String SERVER_HOST_IP = "192.168.1.100";
42private final int SERVER_HOST_PORT = 9500;
43private Socket socket;
44private Thread thread = null; //声明线程
45private OutputStream output;
46private InputStream In;
47
48private Button btn_load; // 声明Button类型的对象
49private EditText et_secretKey;
50private Context mContext ;
51private CheckBox cb_rem ;
52private String secretKey = null;
53private String receive = null;
54public void toastText(String message)
55      {
56        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
57      }
58
59public void handleException(Exception e, String prefix)
60      {
61        e.printStackTrace();
62        toastText(prefix + e.toString());
63      }
64
65    @Override
66public void onCreate(Bundle savedInstanceState) {
67
69        setContentView(R.layout.activity_main);
70        mContext = this ;//this为MainActivity
71        btn_load = (Button) findViewById(R.id.btn_load);
72        cb_rem = (CheckBox) findViewById(R.id.cb_rem);
73        et_secretKey = (EditText) findViewById(_secretKey);
74
75//连接服务器
76        initClientSocket();
77
78//回显秘钥
79        String parent_data = getSecreKey(mContext);
80        et_secretKey.setText(parent_data);
81        cb_rem.setChecked(true);
82
83
84        btn_load.setOnClickListener(new OnClickListener() {
85
86            @Override
87public void onClick(View v) {
88                login();
89
90            }
91        });
92    }
93//登录
94private void login ()  {
95
96//得到秘钥
97          secretKey = Text().toString();
98//发送秘钥
99        sendSecretKey(secretKey);
100        Boolean isrem = cb_rem.isChecked();//默认记住秘钥
101if(TextUtils.isEmpty(secretKey)) {
102            Toast.makeText(mContext, "秘钥不能为空!", Toast.LENGTH_SHORT).show(); 103return;
104        }
105
106//判断是否记住秘钥,如果记住将秘钥保存到本地
107if(isrem) {
108            Boolean result = saveSecreKey(mContext,secretKey);
109if(result){
110            Toast.makeText(mContext, "秘钥保存成功!", Toast.LENGTH_SHORT).show();
111            }
112else {
113                Toast.makeText(mContext, "秘钥保存失败!", Toast.LENGTH_SHORT).show();
114            }
115        }else {
116//Toast.makeText(mContext, "⽆需保存!", Toast.LENGTH_SHORT).show();
117        }
118
119
120    }
121
122//保存秘钥到本地
123private Boolean saveSecreKey(Context context, String secretKey) {
124try {
125            SharedPreferences sharedPreferences = SharedPreferences("", context.MODE_PRIVATE); 126// 2.通过SharedPreference对象得到⼀个Editor对象
127            Editor editor = sharedPreferences.edit();
128            editor.putString("secretKey", secretKey);
129            editormit();
130return true;
131            }catch(Exception e) {
132                e.printStackTrace();
133        }
134return false ;
135    }
136
137private String getSecreKey(Context context) {
138        SharedPreferences sharedPreferences = SharedPreferences("", context.MODE_PRIVATE); 139// 2.通过SharedPreference对象获取存放的数据
140        String data = String("secretKey", "");
141return data ;
142    }
143
144//处理线程通信
145    Handler handler = new Handler(){
146public void handleMessage(android.os.Message msg) {
147switch (msg.what) {
148case 1:
149            IsAndOs stream = (IsAndOs)msg.obj;
150            output = Os();
151            In = Is();
152//等输⼊流的线程处理完之后再开这个线程
153            thread = new Thread(receivedDataThread);
154            thread.start();// 启动接收线程
155
156break;
157case 2:
158                receive= (String)msg.obj;
159if(!(receive ==null)) {
160                    Toast.makeText(mContext, "连接成功!", Toast.LENGTH_SHORT).show();
161                    Intent intent = new Intent(MainActivity.this,MainPageActivity.class);
162                    startActivity(intent);
163                }
164break;
165default:
166break;
167            }
168        };
169    };
socket通信在哪一层170
171private void initClientSocket()
172      {
173//开启⼦线程
174new Thread(new Runnable() {
175
176            @Override
177public void run() {
178try {
179                    socket = new Socket(SERVER_HOST_IP, SERVER_HOST_PORT);
180/* 获取输出,输⼊流 */
181                      PrintStream op = new OutputStream(), true, "utf-8");
182                      InputStream In =InputStream();
183
184                      IsAndOs io = new IsAndOs();
185                      io.setIs(In);
186                      io.setOs(op);
187//传到主线程的信息
188                      Message msg = handler.obtainMessage();
189//将输⼊输出流的类发送给主线程
190                      msg.obj = io;
191                      msg.what = 1;
192//传递消息到主线程
193                      handler.sendMessage(msg);
194                } catch (UnknownHostException e) {
195                      handleException(e, "unknown host exception: " + e.toString());
196                } catch (IOException e) {
197                    handleException(e, "io exception: " + e.toString());
198                }
199            }
200        }).start();
201      }
202
203// 接收线程
204private Runnable receivedDataThread = new Runnable()
205        {
206            @Override
207public void run()
208            {
209
210try{
211byte buffer[] = new byte[1024];
212int count = In.read(buffer);
213                        String receiveData = new String(buffer, 0, count);
214                        Log.i(TAG, "read buffer:"+receiveData+",count="+count);
215                        Message msg = handler.obtainMessage();;
216                        msg.what = 2;
217                        msg.obj = receiveData;
218                        handler.sendMessage(msg);
219
220                    } catch (IOException e)
221                    {
222                        e.printStackTrace();
223                    }
224
225            }
226        };
227
228//发送秘钥
229private void sendSecretKey(String msg)
230          {
231            ((PrintStream) output).print(msg);
232          }
233//创建⼀个类来存放输⼊输出流
234public static class IsAndOs{
235private InputStream is;
236private OutputStream os;
237
238public void setIs(InputStream is){
239this.is = is;
240            }
241
242public InputStream getIs(){
243return is;
244            }
245
246public void setOs(OutputStream os){
247this.os = os;
248            }
249
250public OutputStream getOs(){
251return os;
252            }
253
254        }
255 }
在这⾥,将通信⽹络连接部分开了⼀个⼦线程,并传消息(输⼊输出流对象)到主线程,同时开启⼀个接收数据的线程,⼀定要重新开⼀个线程,要不然会报错,客户端在接收到数据后,再更新UI进⾏页⾯跳转。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。