这段时间在学习Android的socket编程,我不是专做APP的,做的是bootloader、驱动、hal、framework这个线的,也就是系统搭建和功能优化设计。为了打通这整条线,为此学习了不少东西,今天把Android的socket学习记录一下,以防止以后会出现这样的低级错误。
我这里是在极客学院的源码基础上做的自己的一些添加和修改,学习开始不就是先会修改么,举一反三,自然就很快学会了。由于看过视频和资料后就迫不及待的按照自己的想法想做一个功能,但是遇到麻烦了,就是执行new socket(ip, port)程序崩溃的问题,怎么找都找不到,代码也看不出问题,头大了快一天,最终还是被解决了,O(∩_∩)O。不过也庆幸找遇到这样的问题,以后就不会因为这个问题而伤脑筋了。
下面记录一下源码和相关说明。
主要使用了两个java文件。
MainActivity.java:
<pre name="code" class="java">package com.jikexueyuan.mysocketclient;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.CompoundButton;import android.widget.EditText;import android.widget.TextView;import android.widget.ToggleButton;public class MainActivity extends Activity { EditText ip; EditText port; EditText editText; TextView text; ToggleButton connect; Button send; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ip = (EditText) findViewById(R.id.ip); port = (EditText) findViewById(R.id.port); editText = (EditText) findViewById(R.id.edit); text = (TextView) findViewById(R.id.text); send = (Button) findViewById(R.id.send); connect = (ToggleButton) findViewById(R.id.connect); connect.setOnCheckedChangeListener(new ToggleButtonCheckedChangeEvent()); send.setOnClickListener(new ButtonClickEvent()); ConnectClass.datainit(ip, port, editText, text, send, connect);//传递布局数据给类ConnectClass send.setEnabled(false); } // ----------------------------------------------------清除按钮、发送按钮 class ButtonClickEvent implements View.OnClickListener { public void onClick(View v) { if (v == send) { ConnectClass.send(); } } } class ToggleButtonCheckedChangeEvent implements ToggleButton.OnCheckedChangeListener { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (buttonView == connect) { if (isChecked) { ConnectClass.connect(MainActivity.this); } else { ConnectClass.unconnect(); } } } }}
这个主文件没什么好说的,就是做一些初始化。相对于极客学院的code,我添加了ToggleButton按钮,用于连接和断开使用,另外还添加了一个端口设置窗口。布局文件也贴出来。
activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <EditText android:id="@+id/ip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:hint="输入服务器的IP地址" android:text="192.168.0.55" > </EditText> <EditText android:id="@+id/port" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="0.35" android:hint="输入服务器的PORT" android:text="2030" > </EditText> <ToggleButton android:id="@+id/connect" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="连接" android:textOff="@string/connect" android:textOn="@string/unconnect" /> </LinearLayout> <ScrollView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="0.86" > <TextView android:id="@+id/text" android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="Ready..." /> </ScrollView> <EditText android:id="@+id/edit" android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="在这里输入内容" /> <Button android:id="@+id/send" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="发送" /></LinearLayout>
重点是ConnectClass.java这个类文件,源码如下:
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><pre name="code" class="cpp">package com.jikexueyuan.mysocketclient;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.net.Socket;import java.net.UnknownHostException;import android.content.Context;import android.os.AsyncTask;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;import android.widget.Toast;import android.widget.ToggleButton;public class ConnectClass { static Socket socket = null; static BufferedWriter writer = null; static BufferedReader reader = null; static EditText ip; static EditText port; static EditText editText; static TextView text; static Button send; static ToggleButton connect; public static void datainit(final EditText tIp, final EditText tPort, final EditText tEditText, final TextView tText, final Button tButton, final ToggleButton tToggleButton){ ip = tIp; port = tPort; editText = tEditText; text = tText; send = tButton; connect = tToggleButton; } public static void connect(final Context tActivity) { AsyncTask<Void, String, Void> read = new AsyncTask<Void, String, Void>() { @Override protected Void doInBackground(Void... arg0) { try { socket = new Socket(ip.getText().toString(), Integer.decode(port.getText().toString())); writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); // send.setEnabled(true); publishProgress("@success"); } catch (UnknownHostException e1) { Toast.makeText(tActivity, "无法建立链接", Toast.LENGTH_SHORT).show(); } catch (IOException e1) { Toast.makeText(tActivity, "无法建立链接", Toast.LENGTH_SHORT).show(); } try { String line; while ((line = reader.readLine())!= null) { publishProgress(line); } } catch (IOException e) { e.printStackTrace(); } return null; } @Override protected void onProgressUpdate(String... values) { if (values[0].equals("@success")) { Toast.makeText(tActivity, "链接成功!", Toast.LENGTH_SHORT).show(); send.setEnabled(true); } text.append("别人说:"+values[0]+"\n"); super.onProgressUpdate(values); } }; read.execute(); } public static void unconnect() { try { writer.close(); reader.close(); socket.close(); send.setEnabled(false); connect.setChecked(false); } catch (IOException e) { e.printStackTrace(); } } public static void send() { try { text.append("我说:"+editText.getText().toString()+"\n"); writer.write(editText.getText().toString()+"\n"); writer.flush(); editText.setText(""); } catch (IOException e) { e.printStackTrace(); } }}
这个文件类,完成网络的连接,断开,发送等功能。
这里重点说明new socket这个函数的使用,也就是标题的崩溃问题原因。进过谷歌发现,要使用new socket这个函数,不能在主线程ui中使用,否则就会报错崩溃,必须使用分线程来调用new socket函数进行网络操作,上面用的是AsyncTask异步线程,当然其他线程也是可以的。
另外还有一个原因会导致崩溃,就是网络权限,配置文件中一定要有这句<uses-permission android:name="android.permission.INTERNET"/>。
服务端我用的是Linux系统下的,源码如下:
/* File Name: server.c */ #include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #define DEFAULT_PORT 2030 #define MAXLINE 4096 int main(int argc, char** argv) { int socket_fd, connect_fd; struct sockaddr_in servaddr; char buff[4096]; int n; //初始化Socket if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){ printf("create socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } //初始化 memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。 servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT //将本地地址绑定到所创建的套接字上 if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){ printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } //开始监听是否有客户端连接 if( listen(socket_fd, 10) == -1){ printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno); exit(0); } printf("======waiting for client's request======\n"); while(1){ //阻塞直到有客户端连接,不然多浪费CPU资源。 if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){ printf("accept socket error: %s(errno: %d)",strerror(errno),errno); continue; } //接受客户端传过来的数据 n = recv(connect_fd, buff, MAXLINE, 0); //向客户端发送回应数据 if(!fork()){ /*紫禁城*/ if(send(connect_fd, "Hello,you are connected!\n", 26,0) == -1) perror("send error"); close(connect_fd); exit(0); } buff[n] = '\0'; printf("recv msg from client: %s\n", buff); close(connect_fd); } close(socket_fd); }
Makefile文件为:
tcp_server:tcp_server.c #arm-cortex_a9-linux-gnueabi-gcc -o tcp_server tcp_server.c gcc -o tcp_server tcp_server.c
如果想在arm的开发板上运行该服务器,那么使用注释掉的指令,在PC中运行使用gcc编译。
关于Linux的socket,可以参考这里http://blog.csdn.net/hguisu/article/details/7445768。
源码下载地址:http://download.csdn.net/detail/u010406724/8558445
欢迎转载,转载请注明原地址:http://blog.csdn.net/wang_shuai_ww/article/details/44835075