Socket也被称为“套接字”编程,它分为流式套接字和用户数据套接字两种,分别对应于网络传输控制中层中TCP和UDP协议。TCP协议是面向连接的协议,提供稳定的双向通信功能,TCP连接的建立需要经过”三次握手”才能实现,为了实现稳定的数据传输功能,其本身提供了超时重传机制,具有很高的稳定性。UDP是无连接的,提供不稳定的单向通信功能,当然UDP也可以实现双向通信功能,在性能上,UDP就有更好的效率,其缺点是不能保证数据一定传输正确,尤其是网络拥塞的情况下。
演示一个Socket聊天的程序:
服务端
package com.app.service;
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.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class TcpService extends Service {
private boolean mIsServiceDestoryed = false;
private String[] mDefinedMessage = new String[] { "你好", "hello", "辛苦了" };
private ServerSocket mServerSocket;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
new Thread(new TcpThread()).start();
}
class TcpThread implements Runnable {
@Override
public void run() {
try {
//服务端在8688这个端口监听
mServerSocket = new ServerSocket(8688);
} catch (IOException e) {
e.printStackTrace();
return;
}
while (!mIsServiceDestoryed) {
try {
//一个阻塞的方法,当有客户端Socket时会运行
final Socket socket = mServerSocket.accept();
new Thread() {
@Override
public void run() {
responseClient(socket);
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//服务端相应客户端
private void responseClient(Socket socket) {
try {
//读取客户端发来的消息
BufferedReader in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
//用于向客户端发送消息
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())), true);
out.println("欢迎来到聊天室!");
while (!mIsServiceDestoryed) {
String str = in.readLine();//通信连接后,客户端不发消息,这个方法会一直阻塞
Log.e("tag", "客户端消息:====" + str);
if (str == null) {
break;
}
int i = new Random().nextInt(mDefinedMessage.length);
String msg = mDefinedMessage[i];
out.println(msg);//向客户端发送消息
}
//客户端连接断开后,要关闭数据流
in.close();
out.close();
socket.close();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onDestroy() {
mIsServiceDestoryed = true;//停止线程
super.onDestroy();
}
}
客户端
先看布局代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#fff"
android:orientation="vertical" >
<EditText
android:id="@+id/et_recore"
android:layout_width="fill_parent"
android:layout_height="200dip"
android:gravity="top|left"
android:text="聊天记录:\n" >
</EditText>
<EditText
android:id="@+id/et_send_content"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="输入要发送的内容" >
</EditText>
<Button
android:id="@+id/btn_send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送"
android:textColor="#000" />
</LinearLayout>
java代码:
package com.app.client;
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.io.UnsupportedEncodingException;
import java.net.Socket;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
public class MainActivity extends Activity implements OnClickListener {
private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
private static final int MESSAGE_SOCKET_CONNECTED = 2;
private Button mButton_Send;
private EditText mEditText01, mEditText02;
private PrintWriter mPintWriter;
private Socket mClientSocket;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_RECEIVE_NEW_MSG:
mEditText01.setText(mEditText01.getText() + (String) msg.obj);
break;
case MESSAGE_SOCKET_CONNECTED:
mButton_Send.setEnabled(true);
mEditText02.setEnabled(true);
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton_Send = (Button) findViewById(R.id.btn_send);
mEditText01 = (EditText) findViewById(R.id.et_recore);
mEditText02 = (EditText) findViewById(R.id.et_send_content);
mButton_Send.setEnabled(false);
mButton_Send.setOnClickListener(this);
//开启服务
Intent i = new Intent("com.app.service");
startService(i);
new Thread() {
@Override
public void run() {
connectTCPServer();
}
}.start();
}
protected void connectTCPServer() {
Socket socket = null;
//while里面使用了超时重连策略
while (socket == null) {
try {
socket = new Socket("localhost", 8688);
mClientSocket = socket;
mPintWriter = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())), true);
mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
} catch (Exception e) {
SystemClock.sleep(1000);
}
}
try {
//用于读取服务端的消息
BufferedReader br = new BufferedReader(new InputStreamReader(
mClientSocket.getInputStream(), "utf-8"));
while (!MainActivity.this.isFinishing()) {
String msg = br.readLine();//读取
Log.e("tag", "服务端消息:====" + msg);
if (msg != null) {
String showeMsg = "server : " + msg + "\n";
mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showeMsg)
.sendToTarget();
}
}
//关闭资源
mPintWriter.close();
br.close();
socket.close();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onClick(View v) {
String msg = mEditText02.getText().toString();
if (!TextUtils.isEmpty(msg) && mPintWriter != null) {
mPintWriter.println(msg);
mEditText02.setText("");
String showMsg = "client : " + msg + "\n";
mEditText01.setText(mEditText01.getText() + showMsg);
}
}
@Override
protected void onDestroy() {
if (mClientSocket != null) {
try {
mClientSocket.shutdownInput();
mClientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
super.onDestroy();
}
}
客户端和服务端联网需要权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />