最近,公司的项目要做android手机端的相互聊天功能。每个手机在连接网络的时候,会被网络提供商提供一个动态的ip地址,所以,手机端之间的聊天和pc之间的聊天方法,在通讯上是不存在啥大的区别的。
现在给出个demo,在手机上建立个服务来接受另外的ip地址发送过来的数据,在手机上开启socket端口来发送数据。
界面布局,是个点击按钮,每点击一次就发送一次数据给服务。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:id="@+id/btn_lclick" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="click" /> </RelativeLayout>
相当于客户端的发送socket类:
public class LocalClient extends Activity implements OnClickListener { private Socket socket; private InputStream in; private BufferedReader reader; private OutputStream out; private InetAddress ipaddress; private StringBuffer sb; private String line; private Button click; private Intent intent; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.acy_chant); sb=new StringBuffer(); SetIp(); init(); // 启动服务 intent = new Intent(LocalClient.this, LocalService.class); startService(intent); } public void init() { click = (Button) findViewById(R.id.btn_lclick); click.setOnClickListener(this); } public void SetIp() { /** * ip地址 */ try { // 如果没有网络,会返回localhost/::1地址,是ipv6地址 ipaddress = InetAddress.getByName(GetIp()); } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } /** * 获取ip地址 * * @return */ public String GetIp() { try { for (Enumeration<NetworkInterface> en = NetworkInterface .getNetworkInterfaces(); en.hasMoreElements();) { NetworkInterface intf = en.nextElement(); for (Enumeration<InetAddress> ipAddr = intf.getInetAddresses(); ipAddr .hasMoreElements();) { InetAddress inetAddress = ipAddr.nextElement(); // ipv4地址 if (!inetAddress.isLoopbackAddress() && InetAddressUtils.isIPv4Address(inetAddress .getHostAddress())) { return inetAddress.getHostAddress(); } } } } catch (Exception ex) { } return ""; } @Override public void onClick(View v) { // TODO Auto-generated method stub if (v == click) { // 开启线程处理通讯 new Thread() { public void run() { try { // 连接 socket = new Socket(ipaddress, 9090); Log.i("**open**", "开启本地端口"); // 输出流 out = socket.getOutputStream(); BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(out)); writer.write("client:hello world\n"); writer.flush(); // 输入流 in = socket.getInputStream(); reader = new BufferedReader(new InputStreamReader(in)); line = reader.readLine(); sb.append(line); Log.i("**client***", sb.toString()); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }; }.start(); } } /** * 退出时,关闭服务 */ @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); stopService(intent); Log.i("**close service**", "关闭服务"); } }
public class LocalService extends IntentService { private Context context; private String name; private boolean isRun = false; private int Port = 9090; private ServerSocket ss; private Socket socket; private InputStream in; private OutputStream out; private BufferedReader reader; private StringBuffer sb; private String line = null; public LocalService() { super("localservice"); // TODO Auto-generated constructor stub } public LocalService(Context context, String name) { // 必须调用父类的有参构造方法,因为父类的无参构造方法已经被有参方法覆盖了。 super("localservice"); this.context = context; this.name = name; } /** * 初始化 */ @Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); sb = new StringBuffer(); try { ss = new ServerSocket(Port); Log.i("***start service***", "启动 服务"); isRun = true; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); Log.i("ss exception", e.getMessage()); } } /** * 在子线程中处理长连接 */ @Override protected void onHandleIntent(Intent intent) { // TODO Auto-generated method stub while (true) { try { Log.i("****into***", "进入服务子线程长连接"); socket = ss.accept(); Log.i("***get client**", "获取客户端发送的请求"); // 输入流 in = socket.getInputStream(); reader = new BufferedReader(new InputStreamReader(in)); line = reader.readLine(); sb.append(line); // reader.close(); Log.i("**service**", sb.toString()); // 输出流 out = socket.getOutputStream(); BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(out)); writer.write("server:yes!!\n"); writer.flush(); } catch (Exception e) { e.printStackTrace(); Log.i("service exception", e.getMessage()); } } } @Override public void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); } }
1.界面:
2.运行时的log:
3.发送数据后的log:
基本的就这样,有兴趣的小伙伴可以自己的增添功能,因为这是没链接服务器的情况,所以比较简单和粗糙。
注意:
1.在写着个的时候,遇到过socket is close 的异常,导致数据没接收到。网上说着可能是有服务器和客户端断开链接的情况导致的,以后遇到要了解这样的情况。而我这的情况是由于没有初始化的对象导致的,那样也会导致socket断开链接。
2.对于inputstream的处理。有两种方式,一种是用:inputstream.read()。读取字节数组的方式,来读取流。一种是使用BufferedReader来封装输入流,然后用reader.readLine(),来读取流。
但,使用封装的BufferedReader来读取数据,会导致阻塞问题,要读取到"\n"的时候结尾。否则会一直阻塞,使得流不能读取。
3.在获取ip地址的时候,开启的模拟器的ip地址分配是10.0的地址,二我们发送给模拟器的ip地址,其实是pc的ip地址,所以用模拟器测试的话,你需要改下地址。
4.intentservice在配置清单中,写的是service,而不是intentservice:
<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.chant.LocalClient" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name="com.chant.LocalService" > </service>
5.对流的处理也可以使用 :datainputstream dataoutputstream
public static void main(String[] args) { ServerSocket server = null; Socket socket = null; DataInputStream in = null; DataOutputStream out = null; // 循环等待客户端的访问 try { // 开启服务器端口,限定最大访问队列 server = new ServerSocket(8089, 1000); while (true) { // 阻塞直到有客户端连接 socket = server.accept(); // 获取输入流 in = new DataInputStream(socket.getInputStream()); String str = in.readUTF(); System.out.println(str); // 获取输出流 out = new DataOutputStream(socket.getOutputStream()); // 发送与机器无关的utf-8编码的字符串 out.writeUTF("service: welcome to service"); // 清空此数据输入流 out.flush(); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { // 关闭相关的流操作 try { if (socket != null) { socket.close(); } if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }