android开发之socket快传文件以及消息返回
应用场景:
两台android机器:一台自建wifi热点,另一台搜索到,连接该wifi热点。之后再通过socket传递消息,文件等,当服务器端接收到消息之后会返回对应的应答消息;
注意点:接收到消息之后不能直接拔dos关闭了,这样会造成socket关闭;
socket关闭只需要一端关闭即可;
这里发送没有使用缓存发送,每一次writeInt的时候度会直接发送出去,,因此可以不需要flush();
每一次的发送端的 write 和 接收端的read 必须对应,否则异常出错!
服务器端socket代码:
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; import com.carspeak.client.entity.NetTransEntity; import com.carspeak.terminal.utils.AppUtils; import android.content.Context; import android.os.Handler; import android.os.Message; import android.util.Log; /** * 服务器端完整socket接收代码(接收客户端的图片) * @author huqiang * */ public class SocketService implements Runnable{ private static final String TAG = "SocketService"; private ServerSocket serviceSocket; private boolean SCAN_FLAG = false; // 接收扫描标识 private boolean REV_FLAG = false; // 接收标识 private static String mfilePath = null; // 存放接收文件的路径 private static Context mContext; private static SocketService instance; // 唯一实例 private Thread mThread; private boolean IS_THREAD_STOP = false; // 是否线程开始标志 private static Handler mHandler; public SocketService() { try { serviceSocket = new ServerSocket(4700); Log.d(TAG, "建立监听服务器ServerSocket成功"); } catch (IOException e) { Log.d(TAG, "建立监听服务器ServerSocket失败"); e.printStackTrace(); } mThread = new Thread(this); } /** * <p> * 获取TcpService实例 * <p> * 单例模式,返回唯一实例 */ public static SocketService getInstance(Context context,Handler handler,String filepath) { mContext = context; mHandler = handler; mfilePath = filepath; if (instance == null) { instance = new SocketService(); } return instance; } public void setSavePath(String fileSavePath) { Log.d(TAG, "设置存储路径成功,路径为" + fileSavePath); this.mfilePath = fileSavePath; // REV_FLAG=true; } public SocketService(Context context) { this(); mContext = context; } private void scan_recv() { try { Socket socket = serviceSocket.accept(); // 接收UDP数据报 // socket.setSoTimeout(10*1000); // 设置掉线时间 Log.d(TAG, "客户端连接成功"); //通过子线程来循环读取socket的消息 ListenClientSocket ls = new ListenClientSocket(socket); ls.start(); } catch (IOException e) { e.printStackTrace(); Log.d(TAG, "客户端连接失败"); SCAN_FLAG = false; } } @Override public void run() { Log.d(TAG, "TCP_Service线程开启"); while (!IS_THREAD_STOP) { if (SCAN_FLAG) { scan_recv(); } } } public void release() { if (null != serviceSocket && !serviceSocket.isClosed()) try { serviceSocket.close(); serviceSocket = null; Log.d(TAG, "关闭socket成功"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } while (SCAN_FLAG == true) ;// 直到SCAN_FLAG为false的时候退出循环 SCAN_FLAG = false; IS_THREAD_STOP = true; } public void startReceive() { SCAN_FLAG = true; // 使能扫描接收标识 if (!mThread.isAlive()) mThread.start(); // 开启线程 } public void stopReceive() { while (SCAN_FLAG == true) ; SCAN_FLAG = false; // 失能扫描接收标识 } /** * 监听客户端发送的消息 * @author huqiang * */ class ListenClientSocket implements Runnable { private boolean IsListening = false; private Socket clientSocket; private Thread thread; public ListenClientSocket(Socket s) { this.clientSocket = s; this.thread = new Thread(this); } @Override public void run() { while(clientSocket!=null&&!clientSocket.isClosed()&&IsListening) { readMessage(this.clientSocket); } } public void start() { IsListening = true; thread.start(); } public void release() { IsListening = false; if(!clientSocket.isClosed()) { try { clientSocket.close(); clientSocket = null; } catch (IOException e) { e.printStackTrace(); } } } public void readMessage(Socket socket) { NetTransEntity entity = new NetTransEntity(); try { //分批次接收socket InputStream in = socket.getInputStream(); DataInputStream dis = new DataInputStream(in); entity.RequestCode = dis.readInt(); entity.params = dis.readUTF(); switch(entity.RequestCode) { case 100:// case 500://请求升级 Log.v(TAG, "请求图片"); entity.fileLength = dis.readLong(); Log.v(TAG, "接收到文件长度:"+entity.fileLength); if(entity.RequestCode==100) { entity.params = "screen.png"; } FileOutputStream fos =new FileOutputStream(new File(mfilePath,entity.params)); byte[] sendBytes =new byte[1024]; int transLen =0; Log.v(TAG, "----开始接收文件<" + entity.params +">,文件大小为<" + entity.fileLength +">----"); while(true){ int read =0; read = dis.read(sendBytes); Log.v(TAG, "read="+read); if(read == -1) break; transLen += read; Log.v(TAG, "接收文件进度" +100 * transLen/entity.fileLength +"%..."); fos.write(sendBytes,0, read); fos.flush(); } Log.v(TAG, "----接收文件<" + entity.params +">成功-------1"); entity.filePath = mfilePath + entity.params; //将下载下来的文件名字赋值给entity.filePath Log.v(TAG, "----接收文件<" + entity.params +">成功-------2"); break; case 101://请求终止投影 case 102://请求待机(超时自动待机) case 103://请求高速自动待机 break; case 501://请求固件版本信息 break; default://其他请求 break; } // dis.close();//会造成关闭socket // socket.shutdownInput(); //发送返回消息 Log.v(TAG, "----开始发送返回消息-------"); DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); Log.v(TAG, "发送返回消息 1"); dos.writeInt(200); dos.flush(); Log.v(TAG, "发送返回消息 2"); switch(entity.RequestCode) { case 501: //请求固件版本信息 //返回版本号 Log.v(TAG, "发送返回消息-返回版本号"); String versionCode = AppUtils.getVersionCode(mContext)+""; dos.writeUTF(versionCode); dos.flush(); break; case 100: case 101: case 102: case 103: default: dos.writeUTF("success"); dos.flush(); break; } // dos.close(); // socket.close(); } catch(Exception ex) { Log.e(TAG, "Exception:"+ex.getMessage()); } finally { Message msg = new Message(); msg.what = 1; //说明有socket消息 msg.obj = entity; mHandler.sendMessage(msg); release(); } } } }
对应的客户端socket代码:
import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import com.carspeak.client.config.SPConfig; import com.carspeak.client.entity.CusMessage; import com.carspeak.client.entity.NetTransEntity; import com.carspeak.client.util.SPUtils; import com.carspeak.client.util.WifiUtils; import android.content.Context; import android.os.Handler; import android.os.Message; import android.util.Log; /** * 连接到终端设备 * * @author huqiang * */ public class ClientService implements Runnable{ // AppConfig app; // String filePath; NetTransEntity mEntity; String ServerIP; WifiUtils mWifiUtils; private final int SERVER_PORT = 4700; //服务器端口 private final int TIME_OUT = 6*1000; //SOCKET超时时间 private Context mContext; private Handler mHandler; public ClientService(Context context,Handler handler,NetTransEntity entity) { // app = (AppConfig) context.getApplicationContext(); // this.filePath = filePath; this.mEntity = entity; mContext = context; this.mHandler = handler; mWifiUtils = WifiUtils.getInstance(context); ServerIP = (mWifiUtils.getServerIPAddress()==null)?"192.168.43.1":mWifiUtils.getServerIPAddress(); } /** * 向终端发送数据 * @param path * @throws IOException * @throws Exception */ private void sendMessage(NetTransEntity entity) throws IOException, Exception { // 1.连接服务器 Socket socket = new Socket(); Log.v("ConnectTerminalServer", "开始准备socket连接,ServerIP=" + ServerIP+ ";SERVER_PORT=" + SERVER_PORT + ";TIME_OUT=" + TIME_OUT+";当前连接状态:getSSID"+mWifiUtils.getSSID()); socket.connect(new InetSocketAddress(ServerIP, SERVER_PORT), TIME_OUT); DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); Log.v("ClientService", "开始写入请求码:"+entity.RequestCode); dos.writeInt(entity.RequestCode); //写入请求码 // dos.flush(); switch(entity.RequestCode) { case 100://请求图片 case 500://请求固件升级,发送apk文件 File file = new File(entity.filePath); Log.v("ClientService", "开始写入文件名 file.getName()="+file.getName()); dos.writeUTF(file.getName()); //第二次写入参数信息 dos.flush(); Log.v("ClientService", "开始写入文件长度 file.length()="+file.length()); dos.writeLong(file.length()); //第三次写入文件的长度 dos.flush(); //第四次写入文件 //传输文件 FileInputStream fis =new FileInputStream(file); byte[] sendBytes =new byte[1024]; int length =0; while((length = fis.read(sendBytes,0, sendBytes.length)) >0){ dos.write(sendBytes,0, length); dos.flush(); } fis.close(); break; case 101://请求终止 case 102://请求待机(超时自动待机) case 103://请求高速自动待机 Log.v("ClientService", "开始写入参数="+(entity.params==null?"params is null":entity.params)); dos.writeUTF(entity.params==null?" ":entity.params); dos.flush(); break; case 501://请求固件版本信息 dos.writeUTF(" "); dos.flush(); break; default://其他请求 break; } Log.v("ClientService", "即将关闭dos"); // dos.close(); //注意这里dos关闭会造成socket关闭,应该使用shutdownOutput socket.shutdownOutput(); Log.v("ClientService", "shutdownOutput"); /****************接收返回参数******************/ DataInputStream dis = new DataInputStream(socket.getInputStream()); CusMessage cms = new CusMessage(); Log.v("ClientService", "接收消息 1 dis="+dis); cms.code = dis.readInt(); Log.v("ClientService", "接收消息 2"); cms.message = dis.readUTF(); Log.v("ClientService", "接收到终端返回消息,code="+cms.code + ";message="+cms.message); switch(entity.RequestCode) { case 501: //请求固件版本信息 if(cms.code==200) { int terminalVersionCode = Integer.parseInt(cms.message); SPUtils.put(mContext, SPConfig.TerminalVersionCode, -1); Message msg = new Message(); msg.what = 501; msg.obj = terminalVersionCode; mHandler.sendMessage(msg); Log.v("ClientService", "接收到终端的消息返回,版本号:"+terminalVersionCode); } break; case 100: case 101: case 102: case 103: default: break; } dos.close(); dis.close(); socket.close(); } @Override public void run() { try { sendMessage(this.mEntity); } catch(IOException ex){ Log.v("ConnectTerminalServer", ex.getMessage()); } catch (Exception ex) { Log.v("ConnectTerminalServer", ex.getMessage()); } } }