android socket通信demo (本篇服务于android消息推送)
本文系作者原创,转载请附原文地址,谢谢。
原文地址: http://blog.csdn.net/a774057695/article/details/47039433文章末尾提供本文中的源码下载链接,需资源积分1分,人艰不拆,下载后评论资源可获系统返回积分=无损!
前言:
关于什么是socket通信,本篇文档中不进行解释,不甚清楚的可以去百科查找,日后得空我也会整理相关的内容。
本文是对上一篇关于消息推送的文章的补充,此处给出快速链接:http://blog.csdn.net/a774057695/article/details/47024887
本篇书写的目的:
1.让android客户端和服务器端实现socket通信,且demo需实现双向
2.为实现服务器向客户端推送消息奠定基础
特别说明:
1.服务器端利用pc机运行apach服务器模拟
2.Pc集成xampp环境
3.服务器端eclipse编译,java实现
4.客户端使用真机调试(使用模拟器需要对源码进行修改,因为没有尝试,所以没有给出示例代码,使用模拟器调试出错的请留意该点)
5.手机和pc加入同一域,至少连入同一台路由器,使用wifi共享的方法本文不提供技术支持、不保证其可行性。
功能概述:
l 服务器端:
主函数实现:阻塞的代码段等待socket连接、启动线程为socket连接提供应答;
互动线程(暂且这样称呼):实现收到客户端消息后进行反馈
单向线程(暂且这样称呼):实现接受控制台输入并向客户端发送
连接断开删去该条socket
l 客户端:
实现向服务器端建立socket连接
启动线程监听服务器发送的消息、并反馈UI显示
将本地消息发往服务器
代码结构:
服务器端:(主要)
|-- MyServer
|--src
|--[package]
|--MyServer.java
|--ServerThread.java
|--Thread2.java
客户端:(主要)
|--MultiThreadClient
|--src
|--[package]
|--ClientThread.java
|--MainActivity.java
|--res
|--layout
|--activity_main.xml
|--AndroidManifest.xml
代码示例及关键点:
l 服务器端:
MyServer.java
import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.util.ArrayList;public class MyServer { //定义保存所有Socket的ArrayList public static ArrayList<Socket> socketList = new ArrayList<Socket>(); /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // param 端口号 ServerSocket ss = new ServerSocket(30000); while(true){ //此代码会阻塞,将一直等待别人连接 Socket s = ss.accept(); socketList.add(s); /*每当客户端连接后启动一条ServerThread线程为该客户端服务*/ new Thread(new ServerThread(s)).start(); new Thread(new Thread2(s)).start(); } } }
此处我们需要留意的是端口号,第一不要去使用系统关键服务的端口号,第二我们要注意客户端中的端口号需要匹配,在后面我们还会提及。
ServerThread.java
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStream;import java.net.Socket; public class ServerThread implements Runnable { //定义当前线程所处理的Socket Socket s = null; //该线程所处理的Socket所对应的输入流 BufferedReader br = null; public ServerThread(Socket s) throws IOException { this.s = s; //初始化该Socket对应的输入流 br = new BufferedReader(new InputStreamReader(s.getInputStream(), "utf-8")); } @Override public void run() { try { String content = null; //采用循环不断从Socket中读取客户端发送过来的数据 while((content = readFromClient())!=null){ //遍历socketList中的每个Socket //将读到的内容向每个Socket发送一次 for(Socket s : MyServer.socketList){ OutputStream os = s.getOutputStream(); //简单处理模拟应答 os.write((content + "——返回自服务器\n").getBytes("utf-8")); } } }catch (IOException e) { e.printStackTrace(); } } /** * 读取客户端数据 * @return */ public String readFromClient() { try{ return br.readLine(); }catch(IOException e){ //如果捕捉到异常,表明该Socket对应的客户端已经被关闭 //删除该Socket MyServer.socketList.remove(s); } return null; } }
这里也没有太多值得注意的,这条线程是实现对客户端应答,当客户端向服务器发出消息,服务器收到后进行反馈,这里我们只是对原来的内容进行了简单的处理,可以按照服务器-客户端之间的约定,实现更有意义的事情。
当连接断开时,删除该socket
Thread2.java
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStream;import java.net.Socket; public class Thread2 implements Runnable { //定义当前线程所处理的Socket Socket s = null; //该线程所处理的Socket所对应的输入流 BufferedReader br = null; public Thread2(Socket s) throws IOException { this.s = s; //初始化该Socket对应的输入流 br = new BufferedReader(new InputStreamReader(s.getInputStream(), "utf-8")); } /** * 读取控制台输入 * */ private void readConsole() { //数组缓冲 byte[] b = new byte[1024]; //有效数据个数 int n = 0; try{ while(true){ //提示信息 System.out.println("请输入:"); //读取数据 n = System.in.read(b); //转换为字符串 String str = new String(b,0,n - 2); //发送到客户端 for(Socket s : MyServer.socketList){ OutputStream os = s.getOutputStream(); os.write(("服务器发送"+str + "\n").getBytes("utf-8")); } //判断是否是quit if(str.equalsIgnoreCase("quit")){ break; //结束循环 } //回显内容 System.out.println("输入内容为:" + str); } }catch(Exception e){} } @Override public void run() { readConsole(); }}
这条线程实现读取控制台输入内容并向客户端发送,这里我们没有关心连接断开,这与demo的目的不冲突,但是投入生产时还是应当注意。
l 客户端:
AndroidManifest.xml
创建一个应用,首先做得事情不是在默认打开的布局文件中进行布局,而是规划一下需要哪些权限,并在配置文件中添加!
本处我们需要申请Internet权限:
<uses-permission android:name="android.permission.INTERNET"/> |
手打的朋友请注意:是uses,不是user!
activity_main.xml
预览:(真的太大,求调大小功能) |
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/RelativeLayout1" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="bottom" android:orientation="vertical" > <!--个并不美观的布局 --> <EditText android:id="@+id/show" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@+id/linearLayout1" android:layout_alignParentLeft="true" android:cursorVisible="false" android:gravity="top" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/linearLayout1" android:orientation="horizontal" android:weightSum="5" android:layout_alignParentBottom="true" > <EditText android:id="@+id/input" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="5" > <requestFocus /> </EditText> <Button android:id="@+id/send" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="send" /> </LinearLayout> </RelativeLayout>
MainActivity.java
package com.example.multithreadclient; import java.io.IOException;import java.io.OutputStream;import java.io.UnsupportedEncodingException;import java.net.Socket; import android.app.Activity;import android.os.AsyncTask;import android.os.Bundle;import android.os.Handler;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.EditText;import android.widget.TextView; public class MainActivity extends Activity { EditText input, show; TextView mode; Button send,smode,cmode; OutputStream os; Handler handler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); input = (EditText) findViewById(R.id.input); send = (Button) findViewById(R.id.send); show = (EditText) findViewById(R.id.show); handler = new Handler() { public void handleMessage(android.os.Message msg) { if (msg.what == 0x123) { show.append("\n" + msg.obj.toString()); } }; }; new AsyncTask<String, String, String>(){ @Override protected String doInBackground(String... params) { // TODO Auto-generated method stub Socket s; try { /**@ param param1 param2 * param1: 服务器ip地址,本例中,即pc机的ip地址 cmd输入ipconfig -all可查询 * param2:端口号 注意和服务器源码中的匹配本 *例中30000 * */ s = new Socket(param1,param2); //客户端启动ClientThread线程,不断读取来自服务器的数据 new Thread(new ClientThread(s, handler)).start(); os = s.getOutputStream(); } catch (Exception e) { e.printStackTrace(); } return null; } }.execute(""); send.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub try { //将用户在文本框中输入的内容写入网络 os.write((input.getText().toString() + "\r\n") .getBytes("utf-8")); input.setText(""); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); } @Override protected void onDestroy() { super.onDestroy(); try { os.write("".getBytes("utf-8")); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }}
值得特别注意的就是两个参数!在注释中已经说得很明白了,第一个是pc机的ip,第二个是服务器约定的端口号。使用模拟器进行调试需要修改ip,我印象中是10.0.2.2,而127.0.0.1会被认为是android模拟器本身。
ClientThread.java
package com.example.multithreadclient; import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.Socket; import android.os.Handler;import android.os.Message; public class ClientThread implements Runnable { private Socket s; //该线程负责处理的Socket private Handler handler; //该线程所处理的Socket对应的输入流 BufferedReader br = null; public ClientThread(Socket s, Handler handler) throws IOException { this.s = s; this.handler = handler; br = new BufferedReader(new InputStreamReader(s.getInputStream())); } @Override public void run() { // TODO Auto-generated method stub try { String content = null; //不断读取Socket输入流中的内容 while ((content = br.readLine()) != null) { //每当读到来自服务器的数据后,发送消息通知程序界面显示数据 Message msg = new Message(); msg.what = 0x123; msg.obj = content; handler.sendMessage(msg); } } catch (Exception e) { e.printStackTrace(); } }}
此处没有特别关注的地方
运行测试:
调试android应用,将其安装到手机,只是想先装上去而已
运行xampp panel 启动apach服务
Eclipse中运行服务器端程序
重新启动手机端的客户端程序——只是需要服务器先启动,客户端再连接而已。
你可以在eclipse的console中输入想要发送的消息,回车会被去掉,输入quit回车可以停止之,然并卵
你也可以在手机端发送消息等待回传消息
截图就不附了
本文系作者原创,转载请附原文地址,谢谢。
原文地址: http://blog.csdn.net/a774057695/article/details/47039433本文中的源码下载链接:http://download.csdn.net/detail/a774057695/8929341需资源积分1分,人艰不拆,下载后评论资源可获系统返回积分=无损!
更多相关文章
- 万字长文带你了解最常用的开源 Squid 代理服务器
- Nginx系列教程(一)| 手把手教你在Linux环境下搭建Nginx服务
- Nginx系列教程(二)| 一文带你读懂Nginx的正向与反向代理
- Nginx系列教程(三)| 一文带你读懂Nginx的负载均衡
- RHEL 6 下 DHCP+TFTP+FTP+PXE+Kickstart 实现无人值守安装
- Android中有关Handler的使用(一)
- Android怎样停止AsyncTask和Thread
- [Android] Handler详解
- Android(安卓)Service 详解二:创建一个service