- 简介
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


