这是接着上一章,关于Socket以及输入输出流的知识点。
Socket在Java网络编程中十分常见,而在Android则是使用封装好的HttpClient和HttpURLConnection。
不过,随着版本的推移,HttpClient逐渐被费用,而HttpURLConnection逐渐的完善。
不过这些都是题外话,我现在要总结的东西是Socket,相比上面两者Socket我用到的机会不多,现在慢慢的总结一遍。
Socket中文名为套字节。下面究竟是怎么要一个协议暂时不需要知道,我们知道这是基于C/S通信模型实现的,需要一个服务端,客户端。客户端需要知道服务端的地址,这要再通过IO进行传输就能完成一次交互。
同样的这个思想可以套用到进程间通信,只是我们要设置并非IP地址+端口。而是本地由操作系统分配下来的端口就OK。
一般来说,通信的模型就像下面:
下面是一个Socket实现的,自己能够对应自己的客户端发送,服务端就能随机回答一句话,也就是说少一个客户端,只有服务器和一个客户端相互交流:
Service端:
public class TPCService extends Service{
//判断Service是否被销毁
private boolean mIsServiceDestroy = false;
private String[] mDefinedMessage = new String[]{
"你好呀,Hello!",
"请问你叫啥呀",
"今天北京天气不错啊",
"你知道吗?我可是可以和很多人一起聊天的呀"
};
private class TcpService implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
ServerSocket serverSocket = null;
try{
//约定的端口
serverSocket = new ServerSocket(8688);
}catch (IOException e) {
// TODO: handle exception
System.err.println("establish tcp server failed,port:8688");
e.printStackTrace();
return;
}
while (!mIsServiceDestroy) {
try {
//阻塞等待客户端的连接
final Socket client = serverSocket.accept();
System.out.print("accept");
new Thread(){
@Override
public void run(){
try {
responseClient(client);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
}
private void responseClient(Socket socket)throws IOException{
//客户端输入的流
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//从服务端输出到客户端的流
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true) ;
//一进聊天首先输出文字
out.println("欢迎来到聊天室");
while(!mIsServiceDestroy){
//读输入流
String str = in.readLine();
System.out.println("msg from client:"+str);
if(str == null){
break;
}
int i = new Random().nextInt(mDefinedMessage.length);
String msg = mDefinedMessage[i];
System.out.println("Send:"+msg);
//将信息写入输出流
out.println(msg);
}
System.out.println("client quit");
out.close();
in.close();
socket.close();
}
@Override
public void onCreate(){
//Service还是在主线程中工作,因此网络连接这种耗时工作将要在子线程中完成
new Thread(new TcpService()).start();
super.onCreate();
}
@Override
public void onDestroy(){
mIsServiceDestroy = true;
super.onDestroy();
}
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
}
客户端:
public class MainActivity extends Activity implements OnClickListener{
private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
private static final int MESSAGE_SOCKET_CONNECTD = 2;
private Button button;
private EditText edit;
private TextView text;
private PrintWriter writer;
private Socket client;
//UI中修改组件,最好交给Handler去完成
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg){
switch (msg.what) {
case MESSAGE_RECEIVE_NEW_MSG:
text.setText(text.getText()+(String)msg.obj);
break;
case MESSAGE_SOCKET_CONNECTD:
button.setEnabled(true);
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edit = (EditText)findViewById(R.id.msg);
text = (TextView)findViewById(R.id.msg_container);
button = (Button)findViewById(R.id.send);
button.setOnClickListener(this);
Intent intent = new Intent(MainActivity.this,TPCService.class);
startService(intent);
//连接网络的线程
new Thread(){
@Override
public void run(){
connectTPCService();
}
}.start();
}
@Override
protected void onDestroy(){
if(client != null){
try {
client.shutdownInput();
client.close();
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
}
super.onDestroy();
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(v == button){
final String msg = edit.getText().toString();
if(!TextUtils.isEmpty(msg) && writer != null){
writer.println(msg);
Log.e("tag","client send:"+msg);
edit.setText("");
String time = formatDateTime(System.currentTimeMillis());
final String showedMsg = "self" + time + ":" + msg + "\n";
text.setText(text.getText()+ showedMsg);
}
}
}
//获取实现的格式
private String formatDateTime(long time){
return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));
}
private void connectTPCService(){
Socket socket = null;
//socket为空的时候,每个1秒不断的尝试的连接
while (socket == null) {
try {
//要连接的端口
socket = new Socket("localhost",8688);
client = socket;
writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);
mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTD);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
SystemClock.sleep(1000);
System.out.println("retry connected");
e.printStackTrace();
}
}
//读取从服务端传入的流
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (!MainActivity.this.isFinishing()) {
String msg = br.readLine();
// System.out.println("receive:"+msg);
if(msg != null){
String time = formatDateTime(System.currentTimeMillis());
final String showedMsg = "server" + time + ":"+msg+"\n";
mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showedMsg).sendToTarget();
}
}
System.out.println("quit..");
writer.close();
br.close();
socket.close();
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
注意,打开了socket接口,不用了记住一定要将其关闭。就像IO接口一样,这是十分消耗资源的。