套接字分为流式套接字和拥护数据报套接字。分别对应于tcp和udp。tcp三次握手才能完成,超时重传,稳定性好,双向通信。udp不稳定单向通信,也可以双向通信。
1:Executors工厂
我们可以通过Executors工厂获取到很多关于创建ExecutorService的方法,比如newFixedThreadPool、newScheduledThreadPool等,虽然newScheduledThreadPool方法返回的是ExecutorService的子类。
ExecutorService为我们提供了很多处理异步任务的方法。比如submit(callable),submit(runnable).callable和runnable很像,都是用来执行任务的,但是callable的call方法可以返回结果和抛异常,但是runnable的run方法则不返回结果,也不会扔异常。
虽然再ExecutorService中execute和submit都可以执行任务,但是submit还可以接受执行任务的返回结果Future.通过Future的get方法获得。
ExecutorService的invokeAny接受的是一个callable集合,当我们调用这个方法时,集合中的callable会随机执行一个,当一个执行完之后,其他的就都会被取消。
ExecutorService的shutdown会不再接受新任务,之前的任务完成,才会关闭。而shotdownnow这個方法会尝试马上关闭所有正在执行的任务,并且跳过所有已经提交但是还没有运行的任务。
2:使用socket建立通信
socket通信需要有客户端和服务端。这里通过service的远程进程来模拟服务端。
- 服务端
当服务被创建的时候创建一个线程池,并开启一个任务
executorService = Executors.newFixedThreadPool(2);
executorService.submit(new MyCallable());
任务详情是创建一个serverSocket,并监听8687这个端口。
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
ServerSocket serverSocket=null;
serverSocket=new ServerSocket(8687);
while (!mIsServiceDestoryed){
final Socket accept = serverSocket.accept();
Log.e("TcpService","accept");
executorService.execute(new Runnable() {
@Override
public void run() {
responseClient(accept);
}
});
}
return null;
}
}
当监听的这个端口有消息的时候,从我们预选准备的消息中返回一条消息。
private void responseClient(Socket accept) {
try {
BufferedReader in=new BufferedReader(new InputStreamReader(accept.getInputStream()));
PrintWriter out=new PrintWriter(new BufferedWriter(
new OutputStreamWriter(accept.getOutputStream())),true);
Log.e("TcpService","welcome");
while (!mIsServiceDestoryed){
String s = in.readLine();
Log.e("TcpService",s);
if(s==null){
break;
}
int i = new Random().nextInt(4);
String mDefineMessage = mDefineMessages[i];
out.println(mDefineMessage);
}
out.close();
in.close();
accept.close();
} catch (Exception e) {
e.printStackTrace();
}
}
- 客户端
在Activity的初始化中开启服务,并且用线程建立socket连接
Intent intent=new Intent(this,TcpService.class);
startService(intent);
executorService = Executors.newFixedThreadPool(2);
executorService.execute(new Runnable() {
@Override
public void run() {
Log.e("mainService","connecting");
connect();
}
});
连接的具体方法如下,但是这里的主机名是在真机上测试用的,如果使用模拟器,请查找对应的模拟器主机地址,大多数情况下模拟器的地址不会是"127.0.0.1"。
private void connect() {
Socket socket1=null;
while (socket1==null){
try {
socket1=new Socket("127.0.0.1",8687);
socket=socket1;
printWriter=new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
Log.e("mainService","hasConnected");
}catch (Exception e){
e.printStackTrace();
SystemClock.sleep(1000);
Log.e("mainService","connect retry");
}
}
try {
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (!isFinishing()){
String s = bufferedReader.readLine();
Log.e("mainService",s);
}
printWriter.close();
bufferedReader.close();
socket1.close();
}catch (Exception e){
}
}
上面一共分为两步,一步是建立连接,一步是等服务端的消息。此时的情况是服务端在等客户端发送消息,客户端在等服务端回消息。那么我们让客户端发送一条消息到服务端。
findViewById(R.id.tv1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
executorService.submit(new Runnable() {
@Override
public void run() {
if(printWriter!=null){
Log.e("mainService","click");
printWriter.println("hello");
}
}
});
}
});
我们在这里每点击一下按钮,就回发送一条“hello"到服务端,服务端也会从预先准备好的语句中,回复我们一条消息。
使用到了权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />