- 简介
Socket本质上就是Java封装了传输层上的TCP协议(注:UDP用的是DatagramSocket类)。要实现Socket的传输,需要构建客户端和服务器端。另外,传输的数据可以是字符串和字节。字符串传输主要用于简单的应用,比较复杂的应用(比如Java和C++进行通信),往往需要构建自己的应用层规则(类似于应用层协议),并用字节来传输。
- Socket案例
现实这个功能非常简单,创建一个Socket对象,如:Socket s = new Socket(HOST_ADDR,HOST_PORT); HOST_ADDR是服务器的IP地址,HOST_PORT是服务器的端口,一般我们会选择8000以上的端口,避免端口冲突.
当客户端与服务端产生了对应的Socket之后,程序无需再区分服务器与客户端,而是通过各自的Socket进行通信.Socket提供了2个方法来获取输入流和输出流.
- InputStream getInputStream():返回该Socket对象对应的输入流,让程序通过该方法从Socket中取出数据.
- OutputStream getOutputStream(): 返回该Socket对象对应的输出流,让程序通过该输出流向Socket中输出数据.
现在我们来看看android从服务端读取数据的方法:
InputStream is = s.getInputStream(); byte[] buff = new byte[1024 * 4]; int len = -1; while (!s.isClosed() && !s.isInputShutdown() && is_start && (len = is.read(buff)) != -1){ Log.i(TAG,">>>>>>reading.........."); String msg = new String(Arrays.copyOf(buff, len)).trim(); if (msg!=null){ Log.i(TAG,">>>>>>read:"+msg); Message m = mHandler.obtainMessage(); m.what = 1; Bundle b = new Bundle(); b.putString("msg",msg); m.setData(b); mHandler.sendMessage(m); } } is.close();
上一段代码就是使用了死循环来实现读取服务器发送过来的数据,当有数据到达的时候,我们使用了一个Handler来发送消息,用来通知UI,告诉它什么消息,把消息显示出来.
现在我们在来实现一段给服务器发送消息的代码:
try { if(!s.isClosed() && !s.isOutputShutdown()){ OutputStream out = s.getOutputStream(); msg += "\r\n"; out.write(msg.getBytes("utf-8")); out.flush(); } }catch (IOException e){ e.printStackTrace(); }
这段代码,我们先获取输出流,然后编辑消息msg是一个String文本对象,然后通过OutputStream对象的write的方法把数据发送出去.
以下是整个实例的代码:
package com.test.testnetwork; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.design.widget.FloatingActionButton; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.View; import android.view.Menu; import android.view.MenuItem; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.ref.WeakReference; import java.net.Socket; import java.net.UnknownHostException; import java.util.Arrays; public class MainActivity extends AppCompatActivity { private final static String TAG = "NetWorkLog"; private TextView text; private FloatingActionButton fab; private Context mContext; EditText editText; Button but; WeakReference<Socket> mSocket;//使用弱引用来保存对象 //处理消息 Handler mHandler = new Handler(){ public void handleMessage(Message msg) { Bundle b = msg.getData(); switch (msg.what){ case 1: String str = b.getString("msg"); text.setText(str+"\n"+text.getText()); Log.d(TAG,"msg:"+str); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); fab = (FloatingActionButton) findViewById(R.id.fab); mContext = this; text = (TextView)findViewById(R.id.text); but = (Button) findViewById(R.id.button); editText = (EditText) findViewById(R.id.editText); init3(); } /** * app初始化入口 */ private void init3() { //启动Socket的按钮,在应用中,我们一般会写在服务类中 fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(mSocket == null){ initSocket(); }else { releaseLastSocket(mSocket); } } }); //给服务器发送消息的按钮 but.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String msg = editText.getText().toString(); sendMsg(msg); } }); } /** * 初始化Socket */ private void initSocket(){ new Thread(new Runnable() { @Override public void run() { try { Socket s = new Socket(HOST_ADDR,HOST_PORT); mSocket = new WeakReference<Socket>(s);//加到弱引用 mReadThread = new ReadThread(s); mReadThread.start(); mHandler.postDelayed(HeartBeatRunnable, TAKT_TIME);//初始化成功后,就准备发送心跳包 } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } /** * 心跳线程,每隔一段时间与服务器通信,发送的数据是:1,也可以制定你们想要的数据 */ private Runnable HeartBeatRunnable = new Runnable() { @Override public void run() { if(System.currentTimeMillis() - lastTime >= TAKT_TIME){ boolean status = sendMsg("1"); if(!status){ mHandler.removeCallbacks(HeartBeatRunnable); releaseLastSocket(mSocket); mReadThread.release();//释放线程 initSocket();//重建线程 } mHandler.postDelayed(HeartBeatRunnable, TAKT_TIME);//继续发送心跳包 Log.d(TAG,"HeartBeatRunnable"); } } }; private ReadThread mReadThread;//消息读取县城 private long lastTime = 0L; //最后一次发送时间 private static final int TAKT_TIME = 3000; //间隔时间 private static final String HOST_ADDR = "192.168.1.205"; //服务器IP地址 private static final int HOST_PORT = 30003; //端口号 /** * 向服务端发送数据 * @param msg * @return */ public boolean sendMsg(String msg) { if(null == mSocket || null == mSocket.get()){ return false; } Socket s = mSocket.get(); try { if(!s.isClosed() && !s.isOutputShutdown()){ OutputStream out = s.getOutputStream(); msg += "\r\n"; out.write(msg.getBytes("utf-8")); out.flush(); lastTime = System.currentTimeMillis();//记录最后发送时间 } }catch (IOException e){ e.printStackTrace(); return false; } return true; } /** * 创建一个线程用来监听服务器发送过来的消息 */ class ReadThread extends Thread{ private WeakReference<Socket> mWeakSocket; private boolean is_start = true; public ReadThread(Socket s){ mWeakSocket = new WeakReference<Socket>(s); } private void release(){ is_start = false; releaseLastSocket(mWeakSocket); } @Override public void run() { super.run(); Socket s = mWeakSocket.get(); if(s != null){ try { InputStream is = s.getInputStream(); byte[] buff = new byte[1024 * 4]; int len = -1; while (!s.isClosed() && !s.isInputShutdown() && is_start && (len = is.read(buff)) != -1){ Log.i(TAG,">>>>>>reading.........."); String msg = new String(Arrays.copyOf(buff, len)).trim(); if (msg!=null){ Log.i(TAG,">>>>>>read:"+msg); Message m = mHandler.obtainMessage(); m.what = 1; Bundle b = new Bundle(); b.putString("msg",msg); m.setData(b); mHandler.sendMessage(m); } } is.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 释放Socket * @param s WeakReference中的socket对象 */ private void releaseLastSocket(WeakReference<Socket> s) { if(null != s){ Socket skt = s.get(); if(!skt.isClosed()){ try { skt.close(); } catch (IOException e) { e.printStackTrace(); } } skt = null; mSocket = null; } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
代码解析:
首先我们在initSocket()方法中初始化Socket对象,然后通过Handler对象实现了一个线程延迟发送心跳机制的代码,在HeartBeatRunnable方法中我们对时间进行判断,并且在代码执行完毕后继续发送一个延迟消息,就这样我们实现了按间隔时间向服务器发送存活状态的功能.
最后说一下服务端,服务端可以用很多语言去实现,这里我用的是易语言,方便快速一点.
也可以使用java去实现一个简单的服务端代码:
public static void Socketer() {
try {
ServerSocket serverSocket = new ServerSocket(30003);
while (true) {
Socket socket = serverSocket.accept();OutputStream out = socket.getOutputStream();
System.out.println(“addr:”+socket.getLocalSocketAddress());out.write(“欢迎您使用”.getBytes(“UTF-8″));
out.close();
socket.close();}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}}
以下是效果图:
易语言服务端的代码:
.版本 2
.支持库 iconv.程序集 窗口程序集1
.程序集变量 kh, 文本型.子程序 _服务器1_数据到达
.局部变量 a, 文本型a = 删首尾空 (到文本 (服务器1.取回数据 ()))
.判断开始 (到整数 (a) = 1)
‘ 心跳机制发送的消息
.默认
服务器1.发送数据 (kh, 编码转换 (到字节集 (“信息已收到,请稍后”), #编码_GBK, #编码_UTF_8, ), )
.判断结束
.子程序 _服务器1_客户进入kh = 服务器1.取回客户 ()
服务器1.发送数据 (kh, 编码转换 (到字节集 (“欢迎您使用”), #编码_GBK, #编码_UTF_8, ), )
.子程序 _服务器1_客户离开服务器1.发送数据 (kh, 编码转换 (到字节集 (“拜拜”), #编码_GBK, #编码_UTF_8, ), )
.子程序 _按钮1_被单击
.局部变量 a, 字节集按钮1.禁止 = 真
a = 编码转换 (到字节集 (编辑框1.内容), #编码_GBK, #编码_UTF_8, )
按钮1.禁止 = 假
下载地址:http://pan.baidu.com/s/1o7NwnRO