Socket可以说是android中跨进程通信的一种方式,有时候也可以用于简单的网络传输,对于套接字连接,需要了解ServerSocket和Socket这两个流式套接字,这两个类的具体实现是有PlainSocketImpl实现的,它是SocketImpl的子类,同时SocketImpl也是一个是实现了SocketOption的抽象类。所以要了解Socket怎么用,就需要对这些都了解。下面介绍。
一、ScoketOptions
SocketOptions是为Scoket定义的一个用于设置或者获取Socket各种属性(比如 连接超时,缓冲区大小......)的接口。这里面包含了很多重要的Socket属性信息以及两个get,set方法,了解它对于以后使用Socket进行连接通讯,可以更加得心应手。现在按照笔者认为的便于大家理解的顺序来讲解。
①、设置SocketOptions里的各数据项的值:
public void setOption(int optID, Object val) throws SocketException;
用于将val值设置给指定的optID对象。
②、获取SocketOptions的各项数据值:
public Object getOption(int optID) throws SocketException;
获取SocketOptions中指定optID的值。
接下来你会看到一堆静态常量,你可能会奇怪的是,final定义的常量为什么可以重新设置值。这里先打个预防针,因为SocketOptions定义的常量都是默认的常量值,也就是说你完全可以不理这些,你自己直接使用系统的值,但如果你要设置某个具体数据的值,可以通过setOptions来设置,这个方法的具体实现是在AcstractPlainSocketImpl里面,是根据optID来设置optID对应的局部变量的值。因此,我们调用此方法并不是直接修改optID的静态常量值,这点从ScoketOptions是一个接口就可以看出。下面介绍各个optID代表的意思。
③、public static final int SO_LINGER = 128
表示如果在关闭Socket的时候仍然有缓冲数据未发送,那么Socket在关闭之前应该等待的时间,128表示128秒。此值可以设为0,表示无论缓冲区是否还有数据未发送,当前的Socke都会马上关闭,并且close方法会立即执行。此值应该介于0-65535之间,大于65535将当作65535来对待。如果在指定的等待时间内,数据发送完毕,那么socket会正常关闭,否则将会强制关闭。
④、public static final int SO_TIMEOUT = 4102
表示连接超时时间(即在读取操作的时间不可以超过此时间值),如果是0表示没有连接超时时间,此值不可以是负值,单位是毫秒。
⑤、public static final int TCP_NODELAY = 1
指示数据是马上发送还是先缓存之后在发送,如果是马上发送会导致效率底下,缓存发送会有很高的效率。
⑥、public static final int SO_KEEPALIVE = 8
表示Socket是否发送检测连接是否存活的消息。如果在链接过程中,一直未得到对方的应答,socket会一直尝试连接,直到连接成功。
SocketOptions定义了连接的基本属性,接下来解析SocketImpl这个抽象类,此类定义了socket基本的方法,了解了这些方法的含义,有助于更好的了解Socket,因为Socket内部的操作是通过SocketImpl的子类来实现的。
二、SocketImpl
SocketImpl是一个抽象类,是所有流式套接字的基础,流式套接字主要有两个ServerSocket和Socket,前者一般用于服务端后者用于客户端,作为服务端通常会嵌入两种流式套接字,分别为ServerSocket表示用于监听客户端的对象,一旦连接成功会返回一个Socket,代表客户端的连接对象。而SocketImpl封装了一些上述流式套接字会用到的基本方法,下面来了解:
①、端口,ip
protected InetAddress address
protected int port;
protected int localport
②、监听:
protected abstract void accept(SocketImpl newSocket) throws IOException;
③、绑定:
protected abstract void bind(InetAddress address, int port) throws IOException;
④、关闭连接:
protected abstract void close() throws IOException;
⑤、建立连接:
protected abstract void connect(String host, int port) throws IOException;
此方法用于将socket对象连接到远程主机地址和端口号。
⑥、获取远程地址:
protected InetAddress getInetAddress()
获取当前socket对象的远程连接对象的地址。
⑦、获取输入流:
protected abstract InputStream getInputStream()
获取当前Socket对象的输入流,用来读取数据。
⑧、获取本地绑定端口:
protected int getLocalPort()
⑨、获取输出流:
protected abstract OutputStream getOutputStream()
获取socket对象的输出流,用于发送数据。
⑩、获取远程连接对象的端口号
protected int getPort()
more:关闭输入流输出流:
protected void shutdownInput() throws IOException
protected void shutdownOutput() throws IOException
关闭输入流输出流。
SocketImpl是一个抽象类,它只是定义了用于scoket通信机制的基本方法,在Android中,默认使用的是PlainSocketImpl来实现基本的通讯细节。如果有需要可以自己实现SocketImpl的通信细节,可以参考PlainSocketImpl。下面讲解Socket。
三、Socket
public Socket()
public Socket(String dstName, int dstPort)
两个构造函数都是使用了默认的SocketImpl的子类PlainSocketImpl的基本实现机制。后者还指定了远程连接对象的地址和端口。
public synchronized void close()
public InetAddress getInetAddress()
public InputStream getInputStream()
public OutputStream getOutputStream()
获取输入流和输出流,输入流用来读取数据,输出流用来发送数据。
public void shutdownInput()
public void shutdownOutput()
前者用于关闭输入流,一旦关闭了,将不再接收任何远程对象发送的信息。如果在关闭了输入流之后又进行读取数据的操作会导致异常。
⑥、绑定本地地址
public void bind(SocketAddress localAddr)
用于将socket绑定到本地地址和本地端口,如果localAddr为null的话,将会从本地地址寻找任一可使用端口作为绑定端口。
⑦、远程连接
public void connect(SocketAddress remoteAddr, int timeout)
连接到指定地址的远程对象,timeout表示连接超时时间,可以传值0,0表示无穷大的连接超时时间。
综述,对于ScoketOptions里面提到的很多属性,可以同个scoket.setXXX的方法来设置。可以看到scoket封装了很多基本的方法,使用socket通信的时候,我们只需要关注scoket(如果需要自定义SokcetImpl,请参考plainSocketImpl)这一块就行了。下面再来了解ServerSocket.
四、ServerSocket
ServerSocket表示一个用于等待客户端连接的服务端对象,一旦有客户端连接成功,就可以进行一些适当的回应。同时,ServerSocket的socket通信机制也是又ScoketImpl来实现的,默认的是PlainSockImpl。ServerSocker用法上和Socket有很大相似之处,但也有不用的地方,比如ServerSocket拥有自己的阻塞队列用于接收客户端请求。下面讲解:
①构造函数:
public ServerSocket(int port) throws IOException
public ServerSocket(int port, int backlog) throws IOException
前者用于创建一个用指定端口指定的ServerSocket对象,后者顺带制订了阻塞队列的长度,这个值默认是50.一旦接受的客户端连接对象数量超过了这个值,那么所有超过这个值得连接对象都会被拒绝。
②、监听客户端连接:
public Socket accept() throws IOException
用于监听客户端连接,此方法会导致线程阻塞,直到有新的连接进来,并且会返回一个socket对象,用于和客户端通信。
③、关闭监听:
public void close() throws IOException
调用此方法后,任何尝试建立连接都会失败。
ServerSocket很大程度上和Socket相似,这里就介绍这么多,接下来用一个例子讲解。
ServiceClient:
package com.example.hy.second.Socket;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.example.hy.second.R;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringReader;
import java.net.Socket;
/**
* Created by X1Carbon on 2016/6/16.
*/
public class ServiceClient extends Activity {
private EditText etContent;
private TextView tvMsg;
private Button btSend;
private final String path = "127.0.0.1";
private Socket socket = null;
private BufferedReader reader = null;
private PrintWriter writer = null;
private final int SOCKET_CONNECTED = 1, RECEIVED_MSG = 2;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SOCKET_CONNECTED:
btSend.setEnabled(true);
break;
case RECEIVED_MSG:
tvMsg.setText(tvMsg.getText().toString() + "来自服务端的消息:" + msg.obj +"\n");
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.socket_layout);
etContent = (EditText) findViewById(R.id.etContent);
tvMsg = (TextView) findViewById(R.id.tvMsg);
btSend = (Button) findViewById(R.id.btSend);
Intent intent = new Intent(ServiceClient.this, ServiceSocket.class);
startService(intent);
clientThread.start();
}
private Thread clientThread = new Thread(new Runnable() {
@Override
public void run() {
while (socket == null) {
try {
Message message=handler.obtainMessage();
socket = new Socket(path, 8887);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
handler.sendEmptyMessage(SOCKET_CONNECTED);
while (!ServiceClient.this.isFinishing()) {
readReply();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
CloseUtils.close(reader);
CloseUtils.close(writer);
}
}
}
});
private void readReply() {
if (reader != null) {
try {
String msg = reader.readLine();
if(msg!=null)
Message.obtain(handler, RECEIVED_MSG, msg).sendToTarget();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void send(View view) {
if (writer != null) {
String msg = etContent.getText().toString();
writer.println(msg);
tvMsg.setText(tvMsg.getText().toString() + "我发送了:" + msg + "\n");
} else {
Toast.makeText(ServiceClient.this, "尚未链接", Toast.LENGTH_LONG).show();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
try {
socket.shutdownOutput();
socket.shutdownInput();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
serviceSocket:
package com.example.hy.second.Socket;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
/**
* Created by X1Carbon on 2016/6/16.
*/
public class ServiceSocket extends Service {
private boolean isServiceClose = false;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private Thread serviceThread = new Thread(new Runnable() {
@Override
public void run() {
try {
ServerSocket server = new ServerSocket(8887);
while (!isServiceClose) {
final Socket socket = server.accept();
new Thread(new Runnable() {
@Override
public void run() {
processSocket(socket);
}
}).start();
}
} catch (Exception err) {
}
}
});
private synchronized void processSocket(Socket socket) {
BufferedReader reader = null;
PrintWriter writer = null;
Random random = new Random();
try {
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
while (!isServiceClose) {
String clientMsg = reader.readLine();//当收到的信息为null说明客户端关闭连接了
Log.i("service", "收到来自客户端的消息:" + clientMsg);
if (clientMsg == null)
break;
else {
writer.println(clientMsg.length());
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
CloseUtils.close(reader);
CloseUtils.close(writer);
try {
socket.shutdownInput();
socket.shutdownOutput();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
isServiceClose = true;
}
@Override
public void onCreate() {
super.onCreate();
serviceThread.start();
}
}
closeUtils:
package com.example.hy.second.Socket;
import java.io.Closeable;
import java.io.IOException;
/**
* Created by X1Carbon on 2016/6/16.
*/
public class CloseUtils {
public static void close(Closeable obj) {
if (obj != null)
try {
obj.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/etContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入内容" />
<Button
android:id="@+id/btSend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="send"
android:enabled="false"
android:text="发送" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tvMsg"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</ScrollView>
</LinearLayout>
运行效果:
接下来读者好好体会一下(写的有点烦,所以没怎么解释,不过应该看得懂)。
---------文章写自:HyHarden---------
--------博客地址:http://blog.youkuaiyun.com/qq_25722767-----------
---------文章写自:HyHarden---------
--------博客地址:http://blog.youkuaiyun.com/qq_25722767-----------