android Socket的使用

最近做了个项目,里面用了socket来通信,今天总结下。Socket服务端设备需提供热点供客户端所在设备连接。

先讲服务端:

因为需要服务端提供热点,所以我们先要去打开热点并配置,方法如下:

public static boolean setWifiApEnabled(boolean enable,String wifiName,String passWord){

    WifiManager wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);

    if(enabled){

    //wifi和热点不能同时打开,所以打开热点的时候需要关闭wifi

    wifiManager.setWifiEnabled(false);

    }

    try{

    WifiConfiguration apConfig = new WifiConfiguration();

    //热点名称

    apConfig .SSID = wifiName;

    //热点密码

    apConfig.preSharedKey = passWord;

    Method method = wifiManager.getClass().getMethod(“setWifiApEnabled”,WifiConfiguration.class,Boolean.TYPE);

    return (Boolean)method.invoke(wifiManager,apConfig,enabled);

    }catch(Exception e){

          return false;

    }

}

热点配置完后,服务端需要提供端口,等待客户端连接,因为网络操作不能直接写在主线程里,所以需要开一个线程去执行:

new Thread(){

    @Override

    public void run(){

    super.run();

    try{

     //服务端提供端口号

    ServerSocket serverSocket = new ServerSocket(7240);

    while(true){

    //等待连接

    Socket mSocket = serverSocket.accept();

    new Thread(new socketTask(mSocket)).start();

    }

    }catch(IOException e){

    e.printStackTrace();

    }

    }

}.start();

需要注意的是,端口号规定是16位,也就是65535个不同的端口,其中0-1023是分配给系统的端口号,1024-49151是登记端口号,主要分配给第三方应用使用,49152-65535是短暂端口号,留给客户进程暂时使用,一个进程用完供其他进程使用。

因为需要支持多客户端连接,所以每当有一个客户端连上时,新开线程去专门执行,这样不同的线程可以操作不同的客户端。

class socketTask implements Runnable{

    private Socket mSocket;

    private BufferedInputStream mBufferInputStream;

    private BufferedOutputStream mBufferOutputStream;

    public socketTask(Socket socket){

    this.mSocket = socket;

    if(mSocket ==null){

    return;

    }

    try{

    //设置不延时发送

    mSocket.setTcpNoDelay(true);

    //设置输入输出缓冲流大小

    mSocket.setSendBufferSize(8*1024);

    mSocket.setReceiveBufferSize(8*1024);

    mBufferInputStream = new BufferedInputStream(mSocket.getInputStream);

    mBufferOutputStream = new BufferedOutputStream(mSocket.getOutputStream);

}catch(Exception e){

    e.printStackTrace();

}

    }

    @Override

    public void run(){

    try{

    If(mSocket==null){

    return;

    }

    //开启一个while循环的线程,一直去获取输入流数据

    ReadThread mReadThread = new ReadThread(mSocket,mBufferInputStream ,mBufferOutputStream );

    mReadThread.start();

    boolean bool = true;

    while(bool ){

    try{

    mSocket.sendUrgentData(0xFF);

    Thread.sleep(3000);

    }catch(Exception e){

    bool = false;

    mReadThread.stopThread();

    mReadThread = null;

    mBufferInputStream.close;

    mBufferInputStream = null;

    mBufferOutputStream.close;

    mBufferOutputStream= null;

    mSocket.close;

    mSocket = null;

    }

    }

}catch(Exception e){

e.printStackTrace();

}

    }

}

 注意,setSendBufferSize和setReceiveBufferSize设置输入输出流缓冲区大小的时候,系统默认的缓冲区大小是8*1024,一般情况下不要去改这个大小。改小的话,socket通信速度会快点,但有可能会造成缓冲区溢出;改大的话,速度会相对变慢,且有可能造成服务端接收到数据的时延越来越大。

sendUrgentData发送紧急心跳包,一般通过定时发送可以判断当前的socket连接是否断开,一旦报异常,则表明socket连接已断开。特别注意的是,如果服务端运行在WIN7上,则不适用这种方法,因为WIN7上连续调用sendUrgentData十几次,就会出现异常,而其实socket并没有断开连接。(可能不止是WIN7上会发生这种情况)

当然你也可以定一条协议定时发送,以此来判断连接状态。

 

服务端开启一个while循环的线程,一直去获取输入流数据

class ReadThread extends Thread{

    private boolean isRunning = true;

    private Socket socket;

    private BufferedInputStream mInputStream;

    private BufferedOutputStream mOutputStream;

    public ReadThread(Socket socket,BufferedInputStream mInputStream,BufferedOutputStream mOutputStream){

    this.socket = socket;

    this.mInputStream = mInputStream;

    this.mOutputStream = mOutputStream;

    }

    public void startThread(){

    try{

    //当解析错误时,获取缓冲区里的数据,全部抛掉

    int inCount = mInputStream.available();

    if(inCount >0){

    byte[] buf = new byte[inCount ];

    readData(mInputStream,buf,inCount);

    }

}catch(Exception e){

e.printStackTrace();

}

    }

    public void stopThread(){

    isRunning = false;

    }

    @Override

    public void run(){

    super.run();

    byte[] inputByte = new byte[1024];

    While(isRunning ){

    readData(mInputStream,inputByte ,1024);

    }

    }  

}

 服务端读取输入流数据

private int readData(BufferedInputStream mInputStream,byte[] buffer,int len){

    int r = -1;

    try{

    if(mInputStream!=null){

    int cnt;

    cnt = len;

    int dataLen = 0;

    while(cnt >0){

    r = mInputStream.read(buffer,dataLen,cnt);

    if(r>0){

    cnt -= r;

    dataLen += r;

    }else{

    throw new IOException();

    }

    }

    if(dataLen != len){

    throw new IOException();

    }

    return dataLen;

    }else{

    throw new IOException();

    }

    }catch(Exception e){

    e.printStackTrace();

    return r;

    }

}

服务端写入输出流

private int writeData(BufferedOutputStream mOutputStream,byte[] buffer,int len){

    try{

if(mOutputStream!=null){

mOutputStream.write(buffer,0,len);

mOutputStream.flush();

return len;

}else{

Throw new IOException();

}

    }catch(Exception e){

e.printStackTrace();

return -1;

    }

}

Socket的写入操作在Android7.0以下,不需要写在线程里,而7.0以后,因为审验更严格,如果mOutputStream.write不是写在线程里,则会报主线程不能进行网络操作的异常。

 

 

接下来讲客户端:

客户端连接

InetAddress addr  =  InetAddress .getByName(“192.168.43.1”);

Socket mSocket = new Socket(addr,7240);

//设置不延时发送

mSocket.setTcpNoDelay(true);

//设置输入输出缓冲流大小

mSocket.setSendBufferSize(8*1024);

mSocket.setReceiveBufferSize(8*1024);

mBufferInputStream = new BufferedInputStream(mSocket.getInputStream);

mBufferOutputStream = new BufferedOutputStream(mSocket.getOutputStream);

客户端数据的获取和写入跟服务端是一样的写法,这里不就表述了。需要注意的是,android热点方的ip地址固定是192.168.43.1,这是在底层写死的,所以客户端去连接服务端,所要连接的ip地址就是192.168.43.1,而且要连接的端口号就是服务端暴露出来的端口号。

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/lhp1930/article/details/81983985