一、概述
关于Socket编程的基本方法在基础篇里已经讲过,今天把它给完善了。加入了多线程,这样UI线程就不会被阻塞;实现了客户端和服务器的双向通信,只要客户端发起了连接并成功连接后那么两者就可以随意进行通信了。
二、实现
在之前的工程基础上进行修改就可以了。
MyClient工程的main.xml文件不用修改,只需要修改MyClientActivity.java文件,主要是定义了一个继承自Thread类的用于接收数据的类,覆写了其中的run()方法,在这个函数里面接收数据,接收到数据后就通过Handler发送消息,收到消息后在UI线程里更新接收到的数据。完整的内容如下:
1 package com.nan.client; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.OutputStream; 6 import java.io.UnsupportedEncodingException; 7 import java.net.Socket; 8 import java.net.UnknownHostException; 9 10 import android.app.Activity; 11 import android.os.Bundle; 12 import android.os.Handler; 13 import android.os.Message; 14 import android.view.View; 15 import android.widget.Button; 16 import android.widget.EditText; 17 import android.widget.TextView; 18 import android.widget.Toast; 19 20 21 public class MyClientActivity extends Activity 22 { 23 private EditText mEditText = null; 24 private Button connectButton = null; 25 private Button sendButton = null; 26 private TextView mTextView = null; 27 28 private Socket clientSocket = null; 29 private OutputStream outStream = null; 30 31 private Handler mHandler = null; 32 33 private ReceiveThread mReceiveThread = null; 34 private boolean stop = true; 35 36 /** Called when the activity is first created. */ 37 @Override 38 public void onCreate(Bundle savedInstanceState) 39 { 40 super.onCreate(savedInstanceState); 41 setContentView(R.layout.main); 42 43 mEditText = (EditText)this.findViewById(R.id.edittext); 44 mTextView = (TextView)this.findViewById(R.id.retextview); 45 connectButton = (Button)this.findViewById(R.id.connectbutton); 46 sendButton = (Button)this.findViewById(R.id.sendbutton); 47 sendButton.setEnabled(false); 48 49 //连接按钮监听 50 connectButton.setOnClickListener(new View.OnClickListener() 51 { 52 53 @Override 54 public void onClick(View v) 55 { 56 // TODO Auto-generated method stub 57 try 58 { 59 //实例化对象并连接到服务器 60 clientSocket = new Socket("113.114.170.246",8888); 61 } 62 catch (UnknownHostException e) 63 { 64 // TODO Auto-generated catch block 65 e.printStackTrace(); 66 } 67 catch (IOException e) 68 { 69 // TODO Auto-generated catch block 70 e.printStackTrace(); 71 } 72 73 displayToast("连接成功!"); 74 //连接按钮使能 75 connectButton.setEnabled(false); 76 //发送按钮使能 77 sendButton.setEnabled(true); 78 79 mReceiveThread = new ReceiveThread(clientSocket); 80 stop = false; 81 //开启线程 82 mReceiveThread.start(); 83 } 84 }); 85 86 //发送数据按钮监听 87 sendButton.setOnClickListener(new View.OnClickListener() 88 { 89 90 @Override 91 public void onClick(View v) 92 { 93 // TODO Auto-generated method stub 94 byte[] msgBuffer = null; 95 //获得EditTex的内容 96 String text = mEditText.getText().toString(); 97 try { 98 //字符编码转换 99 msgBuffer = text.getBytes("GB2312"); 100 } catch (UnsupportedEncodingException e1) { 101 // TODO Auto-generated catch block 102 e1.printStackTrace(); 103 } 104 105 106 try { 107 //获得Socket的输出流 108 outStream = clientSocket.getOutputStream(); 109 } catch (IOException e) { 110 // TODO Auto-generated catch block 111 e.printStackTrace(); 112 } 113 114 115 try { 116 //发送数据 117 outStream.write(msgBuffer); 118 } catch (IOException e) { 119 // TODO Auto-generated catch block 120 e.printStackTrace(); 121 } 122 //清空内容 123 mEditText.setText(""); 124 displayToast("发送成功!"); 125 } 126 }); 127 128 //消息处理 129 mHandler = new Handler() 130 { 131 @Override 132 public void handleMessage(Message msg) 133 { 134 //显示接收到的内容 135 mTextView.setText((msg.obj).toString()); 136 } 137 }; 138 139 } 140 141 //显示Toast函数 142 private void displayToast(String s) 143 { 144 Toast.makeText(this, s, Toast.LENGTH_SHORT).show(); 145 } 146 147 148 private class ReceiveThread extends Thread 149 { 150 private InputStream inStream = null; 151 152 private byte[] buf; 153 private String str = null; 154 155 ReceiveThread(Socket s) 156 { 157 try { 158 //获得输入流 159 this.inStream = s.getInputStream(); 160 } catch (IOException e) { 161 // TODO Auto-generated catch block 162 e.printStackTrace(); 163 } 164 } 165 166 @Override 167 public void run() 168 { 169 while(!stop) 170 { 171 this.buf = new byte[512]; 172 173 try { 174 //读取输入数据(阻塞) 175 this.inStream.read(this.buf); 176 } catch (IOException e) { 177 // TODO Auto-generated catch block 178 e.printStackTrace(); 179 } 180 181 //字符编码转换 182 try { 183 this.str = new String(this.buf, "GB2312").trim(); 184 } catch (UnsupportedEncodingException e) { 185 // TODO Auto-generated catch block 186 e.printStackTrace(); 187 } 188 189 Message msg = new Message(); 190 msg.obj = this.str; 191 //发送消息 192 mHandler.sendMessage(msg); 193 194 } 195 } 196 197 198 } 199 200 @Override 201 public void onDestroy() 202 { 203 super.onDestroy(); 204 205 if(mReceiveThread != null) 206 { 207 stop = true; 208 mReceiveThread.interrupt(); 209 } 210 } 211 212 }
对于MyServer工程,改动比较大,首先是main.xml文件,在里面添加了两个TextView,一个用于显示客户端的IP,一个用于显示接收到的内容,一个用于发送数据的Button,还有一个EditText,完整的main.xml如下:
1 26 7 14 15 21 22 28 29 36 37
接着,修改MyServerActivity.java文件,定义了两个Thread子类,一个用于监听客户端的连接,一个用于接收数据,其他地方与MyClientActivity.java差不多。完整的内容如下:
1 package com.nan.server; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.OutputStream; 6 import java.io.UnsupportedEncodingException; 7 import java.net.ServerSocket; 8 import java.net.Socket; 9 10 import android.app.Activity; 11 import android.os.Bundle; 12 import android.os.Handler; 13 import android.os.Message; 14 import android.view.View; 15 import android.widget.Button; 16 import android.widget.EditText; 17 import android.widget.TextView; 18 import android.widget.Toast; 19 20 21 public class MyServerActivity extends Activity 22 { 23 private TextView ipTextView = null; 24 private EditText mEditText = null; 25 private Button sendButton = null; 26 private TextView mTextView = null; 27 28 private OutputStream outStream = null; 29 private Socket clientSocket = null; 30 private ServerSocket mServerSocket = null; 31 32 private Handler mHandler = null; 33 34 private AcceptThread mAcceptThread = null; 35 private ReceiveThread mReceiveThread = null; 36 private boolean stop = true; 37 38 /** Called when the activity is first created. */ 39 @Override 40 public void onCreate(Bundle savedInstanceState) 41 { 42 super.onCreate(savedInstanceState); 43 setContentView(R.layout.main); 44 45 ipTextView = (TextView)this.findViewById(R.id.iptextview); 46 mEditText = (EditText)this.findViewById(R.id.sedittext); 47 sendButton = (Button)this.findViewById(R.id.sendbutton); 48 sendButton.setEnabled(false); 49 mTextView = (TextView)this.findViewById(R.id.textview); 50 51 //发送数据按钮监听 52 sendButton.setOnClickListener(new View.OnClickListener() 53 { 54 55 @Override 56 public void onClick(View v) 57 { 58 // TODO Auto-generated method stub 59 byte[] msgBuffer = null; 60 //获得EditTex的内容 61 String text = mEditText.getText().toString(); 62 try { 63 //字符编码转换 64 msgBuffer = text.getBytes("GB2312"); 65 } catch (UnsupportedEncodingException e1) { 66 // TODO Auto-generated catch block 67 e1.printStackTrace(); 68 } 69 70 71 try { 72 //获得Socket的输出流 73 outStream = clientSocket.getOutputStream(); 74 } catch (IOException e) { 75 // TODO Auto-generated catch block 76 e.printStackTrace(); 77 } 78 79 80 try { 81 //发送数据 82 outStream.write(msgBuffer); 83 } catch (IOException e) { 84 // TODO Auto-generated catch block 85 e.printStackTrace(); 86 } 87 //清空内容 88 mEditText.setText(""); 89 displayToast("发送成功!"); 90 91 } 92 }); 93 //消息处理 94 mHandler = new Handler() 95 { 96 @Override 97 public void handleMessage(Message msg) 98 { 99 switch(msg.what) 100 { 101 case 0: 102 { 103 //显示客户端IP 104 ipTextView.setText((msg.obj).toString()); 105 //使能发送按钮 106 sendButton.setEnabled(true); 107 break; 108 } 109 case 1: 110 { 111 //显示接收到的数据 112 mTextView.setText((msg.obj).toString()); 113 break; 114 } 115 } 116 117 } 118 }; 119 120 121 mAcceptThread = new AcceptThread(); 122 //开启监听线程 123 mAcceptThread.start(); 124 125 } 126 127 //显示Toast函数 128 private void displayToast(String s) 129 { 130 Toast.makeText(this, s, Toast.LENGTH_SHORT).show(); 131 } 132 133 134 private class AcceptThread extends Thread 135 { 136 @Override 137 public void run() 138 { 139 try { 140 //实例化ServerSocket对象并设置端口号为8888 141 mServerSocket = new ServerSocket(8888); 142 } catch (IOException e) { 143 // TODO Auto-generated catch block 144 e.printStackTrace(); 145 } 146 147 try { 148 //等待客户端的连接(阻塞) 149 clientSocket = mServerSocket.accept(); 150 } catch (IOException e) { 151 // TODO Auto-generated catch block 152 e.printStackTrace(); 153 } 154 155 mReceiveThread = new ReceiveThread(clientSocket); 156 stop = false; 157 //开启接收线程 158 mReceiveThread.start(); 159 160 Message msg = new Message(); 161 msg.what = 0; 162 //获取客户端IP 163 msg.obj = clientSocket.getInetAddress().getHostAddress(); 164 //发送消息 165 mHandler.sendMessage(msg); 166 167 } 168 169 } 170 171 172 private class ReceiveThread extends Thread 173 { 174 private InputStream mInputStream = null; 175 private byte[] buf ; 176 private String str = null; 177 178 ReceiveThread(Socket s) 179 { 180 try { 181 //获得输入流 182 this.mInputStream = s.getInputStream(); 183 } catch (IOException e) { 184 // TODO Auto-generated catch block 185 e.printStackTrace(); 186 } 187 } 188 189 @Override 190 public void run() 191 { 192 while(!stop) 193 { 194 this.buf = new byte[512]; 195 196 //读取输入的数据(阻塞读) 197 try { 198 this.mInputStream.read(buf); 199 } catch (IOException e1) { 200 // TODO Auto-generated catch block 201 e1.printStackTrace(); 202 } 203 204 //字符编码转换 205 try { 206 this.str = new String(this.buf, "GB2312").trim(); 207 } catch (UnsupportedEncodingException e) { 208 // TODO Auto-generated catch block 209 e.printStackTrace(); 210 } 211 212 Message msg = new Message(); 213 msg.what = 1; 214 msg.obj = this.str; 215 //发送消息 216 mHandler.sendMessage(msg); 217 218 } 219 } 220 } 221 222 223 @Override 224 public void onDestroy() 225 { 226 super.onDestroy(); 227 228 if(mReceiveThread != null) 229 { 230 stop = true; 231 mReceiveThread.interrupt(); 232 } 233 } 234 235 236 }
两个工程都修改好了,同样,在模拟器上运行客户端:
在真机上运行服务器端:
接着,点击客户端的“连接”按钮,看到“连接成功”提示后输入一些内容再点击“发送”按钮,此时客户端显示:
服务器端显示:
接下来两边都可以随意发送数据了。