通用socket连接流程

先上图:

客户端,创建socket,连接服务器,将服务器ip传给connect(),tcp/ip协议中,connect,需要的参数是一个ip,这里蓝牙的参数有所不同,连接完成后,就可读取和发送数据,最后关闭

服务端:连接前,也需要一个socket()有了socket后,我们还需要一个端口,用bind()绑定,端口号就是服务端的端口号。比如80端口,21端口等。。。绑定端口后,我们需要一个监听操作,tcp/ip中,listen()监听就是阻塞了,处于等待状态,当客户端发东西过来的时候,他会继续往下走,执行accept()函数,(阻塞发生下这里),它会常见新的socket来处理客户端的连接,也就是原来的socket继续监听,新创建的socket进行处理 ,来一个就新建一个socket来和客户端进行通信交互(体现了服务端的并发),同样发送,读取数据,处理完后,关闭连接,一边状态连接有客户端主动发起 

服务端

1.listenUsingRfcommWithServiceRecord创建一个BluetoothServerSocket(是serverSoket)

2.监听网络accept

3。处理网络socket

4.关闭连接

客户端

1.creatRfcommWithServiceRecord创建一个BluetoothSocket(少了server)

2连接服务端connect

3。处理数据

4.关闭连接

等待连接,既进入监听状态。有小圆圈(进度条)在转(作为服务器)

客户端可以断开连接

我的代码结构图:

mainActivity:

package com.lmj.bluetoothdemo;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;

import com.lmj.bluetoothdemo.client.BluetoothController;
import com.lmj.bluetoothdemo.client.DeviceAdapter;
import com.lmj.bluetoothdemo.server.AcceptThread;
import com.lmj.bluetoothdemo.server.ConnectedThread;
import com.lmj.bluuetoothdemo.R;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private static final int REQUEST_CODE = 0;
    private List<BluetoothDevice> mDeviceList = new ArrayList<>();
    private List<BluetoothDevice> mBondedDeviceList = new ArrayList<>();
    private BluetoothController mController = new BluetoothController();

    private DeviceAdapter mAdapter;
    private ListView mListView;
    //这里是个延时创建
    private Toast mToast;
    private AcceptThread mAcceptThread;
    private ConnectedThread mConnectThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        try {
            initActionBar();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        setContentView(R.layout.activity_main);
        initUI();
        registerBluetoothReceiver();
        mController.turnOnBluetooth(this, REQUEST_CODE);
    }

    private void initActionBar() throws NoSuchFieldException {
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        getActionBar().setDisplayUseLogoEnabled(false);
        setProgressBarIndeterminate(true);
        ViewConfiguration config=ViewConfiguration.get(this);
        Field menuKeyField=ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
        if(menuKeyField!=null){

        }
    }

    private void registerBluetoothReceiver(){}
    private Handler mUIHandler =new MyHandler();
  /*  private BroadcastReceiver mReceiver=(context,intent)->{
        String action=intent.getAction();
        if(BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)){
            setProgressBarIndeterminateVisibility(true);
        }
    }*/
      /*  IntentFilter filter = new IntentFilter();
        //在开始的时候需要注册一个处理蓝牙的广播
        //  IntentFilter filter=new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
        //开始查找
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        //结束查找
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        //设备扫描模式改变,被看见有广播,不被看见也有广播过来
        filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
        //绑定状态
        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        //注册广播
        registerReceiver(mReceiver, filter);
        mController.turnOnBluetooth(this, REQUEST_CODE);
    }*/

    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
                //开始查找,查找上会有一个进度条,设别开始查找
                setSupportProgressBarIndeterminateVisibility(true);
                //初始数据列表
                //设备清空
                mDeviceList.clear();
                //发送通知,关闭进度条等等
                mAdapter.notifyDataSetChanged();
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
                setSupportProgressBarIndeterminateVisibility(false);
            }
            //字符串的比较,有常量的放前面,可以防止为空报空指针异常的情况
            //找到设备,就会有ACTION_FOUND这个广播,在广播的
            else if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                //得device是我们查找的数据,找到一个,就添加一个
                mDeviceList.add(device);
                mAdapter.notifyDataSetChanged();
                setSupportProgressBarIndeterminateVisibility(false);
            } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
                //scanMode为SCAN_MODE_CONNECTABLE_DISCOVERABLE,就让他可见
                int scanMode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, 0);
                if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
                    // 进度条转动,友好的人机交互,时间到了timeout就不可见。默认蓝牙扫描是12秒。
                    setProgressBarIndeterminateVisibility(true);
                } else {
                    //进度条结束,设备不可见,通过ui给出提示,这比较友好,有互动,这就是广播的应用。
                    setProgressBarIndeterminateVisibility(false);
                }
            } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
                BluetoothDevice remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                if (remoteDevice == null) {
                    showToast("没有设备");
                    return;
                }
                int status = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 0);
                if (status == BluetoothDevice.BOND_BONDED) {

                }
            }

        }
    };


    public void isSupportBlueTooth(View view) {
        boolean ret = mController.isSupportBlueTooth();
        showToast("支持蓝牙" + ret);
    }

    public void isBlueToothEable(View view) {
        boolean ret = mController.getBlueToothStatus();
        showToast("蓝牙可用" + ret);
    }

    public void requestTurnOnBlueTooth(View view) {
        mController.turnOnBluetooth(this, REQUEST_CODE);
        // showToast("蓝牙可用"+ret);
    }

    public void turnOffBlueTooth(View view) {
        mController.turnOffBlueTooth();
    }

    private void showToast(String text) {
        if (mToast == null) {
            mToast = Toast.makeText(this, text, Toast.LENGTH_SHORT);
        } else {
            mToast.setText(text);
        }
        mToast.show();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == RESULT_OK) {
            showToast("打开成功");
        } else {
            showToast("打开失败");
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.enable_visiblity) {
            //打开蓝牙,使其可见,广播监听什么时候可见,什么时候不可见
            mController.enableVisibly(this);
        } else if (id == R.id.find_device) {
            //查找设备
            //每个设备点击后,都可进行绑定
            mAdapter.refresh(mDeviceList);
            mController.findDevice();
            // 查找结果是通过广播传过来的
            mListView.setOnItemClickListener(bindDeviceClick);
        } else if (id == R.id.bonded_device) {
            //查看已邦设备
            //获取设备后,刷新
            mBondedDeviceList = mController.getBondedDeviceList();
            mAdapter.refresh(mBondedDeviceList);
            //如果是完整流程,可以解绑定,将numl换成别的
            mListView.setOnItemClickListener(null);

        }
        //启动了一个线程
        else if (id==R.id.listening){
            if(mAcceptThread!=null){
                mAcceptThread.cancel();
            }
            mAcceptThread=new AcceptThread(mController.getAdapter(),mUIHandler, mBluetoothAdapter, mHandler);
            mAcceptThread.start();
        }
        else if (id==R.id.stop_listening){
            if(mAcceptThread!=null){
                mAcceptThread.cancel();
            }
        }
        else if (id==R.id.disconnect){
            if(mAcceptThread!=null){
                mConnectThread.cancel();
            }

        }
        else if (id==R.id.say_hello){
            say("hello");
        }
        return super.onOptionsItemSelected(item);
    }
//客户端的起点在这里,点击绑定设备,
    private AdapterView.OnItemClickListener bindDeviceClick = new AdapterView.OnItemClickListener() {
        @RequiresApi(api = Build.VERSION_CODES.KITKAT)
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
            BluetoothDevice device = mDeviceList.get(i);
            //每个设备,点一下,就创建绑定
            if (mConnectThread!=null){
                mConnectThread.cancel();
            }
            //这是客户端的连接,要连接的目标,adapter,以及把网络数据发给ui的一个连接
            mConnectThread=new ConnectedThread(device,mController.getAdapter(),mUIHandler);
            mConnectThread.start();
           // device.createBond();
        }
    };



    //手机蓝牙被其他设备所发现
}

 constant:

package com.lmj.bluetoothdemo.server;

public class Constant {
    //必须是B34FB结尾的uuid才可以
    public static final String CONNECTTION_UUID="00001101-0000-8000-00805F9B34FB";
    //开始监听
    public static final int MSG_START_LISTENING=1;
    //结束监听
    public static final int MSG_FINISH_LISTENING=2;
    /**
     *有客户端连接
     */
    public static final int MSG_GOT_A_CLINET=3;
    /**
     * 连接到服务器
     */
    public static final int MSG_CONNECTED_TO_SERVER=4;
    /**
     * 获取到数据
     */
    public static final int MSG_GOT_DATA=5;
   /* public static final int MSG_START_LISTENING=1;
    public static final int MSG_START_LISTENING=1;*/
}
ConnectThread:
package com.lmj.bluetoothdemo.server;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;

import java.io.IOException;
import java.util.UUID;

public class ConnectThread extends Thread {
    private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);
    private final BluetoothSocket mSocket;
    private final BluetoothDevice mDevice;
    private final BluetoothAdapter mBluetoothAdapter;
    //和ui通信用的,将事件发送给ui,以为ui是不同的线程,
    private final Handler mHandler;
    //服务端新建线程,进行创建socket
    private ConnectedThread mConnectedThread;
    //private static final UUID MY


    public ConnectThread(BluetoothSocket mSocket, BluetoothDevice mDevice, BluetoothAdapter mBluetoothAdapter, Handler handler) {
        BluetoothSocket tmp = null;
        this.mDevice = mDevice;


        this.mSocket = mSocket;

        this.mBluetoothAdapter = mBluetoothAdapter;
        this.mHandler = handler;

        try {
            //整么的创建服务器端的socket,调用listenUsingRfcommWithServiceRecord方法创建,(常量,uuid必须是指定的)
            //就是之前的uuid
            tmp = mDevice.createRfcommSocketToServiceRecord(MY_UUID);
        } catch (IOException E) {
        }
        mSocket = tmp;
    }

    public void run() {
        //一边传输数据,一边发现设备,这是不行的,需先关闭发现设备,避免数据冲突,提升传输效率
        mBluetoothAdapter.cancelDiscovery();
        try {
            mSocket.connect();
        } catch (Exception connectException) {
            mHandler.sendMessage(mHandler.obtainMessage(Constant.MSG_ERROR, connectException));
            try {
                mSocket.close();
            } catch (IOException closeException) {
            }
            return;
        }
        managerConnectedSocket(mSocket);
    }
    private void managerConnectedSocket(BluetoothSocket mSocket){
        //连上后进行数据处理,不管服务端,还是客户端,它发送数据的方式都是一样的
        mHandler.sendEmptyMessage(Constant.MSG_CONNECTED_TO_SERVER);
        mConnectedThread=new ConnectedThread(mSocket,mHandler);
        mConnectedThread.start();
    }


}
ConnectedThread:
package com.lmj.bluetoothdemo.server;

import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class ConnectedThread extends Thread{
    private final BluetoothSocket mSocket;
    private final InputStream mInStream;
    private final OutputStream mOutStream;
    private final Handler mHandler;

    public ConnectedThread(BluetoothSocket socket, Handler handler) {
        mSocket=socket;
        InputStream temIn=null;
        OutputStream temOut=null;
        mHandler=handler;
        try{
            temIn=socket.getInputStream();
            temOut=socket.getOutputStream();
        }catch (IOException e){}
        mInStream=temIn;
        mOutStream=temOut;
    }
    public void run(){
        byte[] buffer=new byte[1024];
        int bytes;
        while (true){
            //从网络里面读取数据
            try {
                bytes=mInStream.read(buffer);
           //实际开发中,是将buffer通过handler交给listen(监听)来处理
            if (bytes>0){
                //将数据转为字符串,就是我们约定,网络通信的一个协议是Sring,故会将buffer强转为String,发给ui,ui会显示到屏幕上,
                Message message=mHandler.obtainMessage(Constant.MSG_GOT_DATA,new String(buffer,0,bytes,"utf-8"));
                mHandler.sendMessage(message);
            }
            Log.d("GOTMSG","mseeage size"+bytes);
            } catch (IOException e) {
                mHandler.sendMessage(mHandler.obtainMessage(Constant.MSG_ERROR,e));
                break;
            }
        }
    }
    public void write(byte[]bytes){
        try {
            //后期会将网络发送和协议解析拆开
            mOutStream.write(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void cancel(){
        try {
            mSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
AcceptThread:
package com.lmj.bluetoothdemo.server;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;

import java.io.IOException;
import java.util.UUID;

public class AcceptThread  extends Thread{
    //连接的名字,
    private static final String NAME="";
    //uuid,蓝牙通信的基础常量
    private static final UUID MY_UUID=UUID.fromString(Constant.CONNECTTION_UUID);
    private  final BluetoothServerSocket mServerSocket;
    private  final BluetoothAdapter mBluetoothAdapter;
    //和ui通信用的,将事件发送给ui,以为ui是不同的线程,
    private  final Handler mHandler;
    //服务端新建线程,进行创建socket
    private  ConnectedThread mConnectedThread;
    private  final String NAME="";
 /*   public AcceptThread(Object adapter, Handler mUIHandler, BluetoothAdapter mBluetoothAdapter, Handler mHandler) {
        this.mBluetoothAdapter = mBluetoothAdapter;
        this.mHandler = mHandler;
        mServerSocket = null;
    }*/

    public AcceptThread( BluetoothAdapter mBluetoothAdapter, Handler mHandler) {
        this.mBluetoothAdapter = mBluetoothAdapter;
        this.mHandler = mHandler;
        BluetoothServerSocket tmp=null;
        try{
            //整么的创建服务器端的socket,调用listenUsingRfcommWithServiceRecord方法创建,(常量,uuid必须是指定的)
            //
            tmp=mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME,MY_UUID);
        }catch (IOException E){}
        mServerSocket=tmp;
    }
    public void run(){
        BluetoothSocket socket=null;
        //监听直到异常或socket重置,
        //服务端不退出,要长期开启,所以用while (true){},socket对象出问题的时候,他会退出
        while (true){
            try{
                //收到请求,进入监听
                mHandler.sendEmptyMessage(Constant.MSG_START_LISTENING);
                //创建socket
                socket=mServerSocket.accept();
            }catch (IOException e){
                mHandler.sendMessage(mHandler.obtainMessage(Constant.MEG_ERROR,e));
                break;

            }
            if (socket!=null){
                manageConnectedSocket(socket);
                try{
                    mServerSocket.close();
                    //到这里面进行管理
                    mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);
                }catch (IOException e){
                    e.printStackTrace();
                }
                break;
            }
        }
    }

    private void manageConnectedSocket(BluetoothSocket socket) {
        //只支持同时处理一个连接,真实情况下,这里是个连接池,服务端要能处理多个连接,mConnectedThread不为空,就把之前
        //客户端踢掉
        if (mConnectedThread!=null){
            mConnectedThread.cancel();
        }
        //连接前发一个消息给ui,ui返回一个提示
        mHandler.sendEmptyMessage(Constant.MSG_GOT_A_CLINET);
        //创建了读取数据的一个线程,传socket,以及和ui通信的mHandler
        mConnectedThread=new ConnectedThread(socket,mHandler);
    }
    public void cancel(){
        try{
            mServerSocket.close();

            mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);
        }catch (IOException e){}
    }
    public void sendData(byte[] data){
        if (mConnectedThread!=null){
            mConnectedThread.write(data);
        }
    }
}

客户端的

DeviceAdapter
package com.lmj.bluetoothdemo.client;

import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.util.List;

public class DeviceAdapter extends BaseAdapter {
    private List<BluetoothDevice> mData;
    private Context mContext;

    public DeviceAdapter(List<BluetoothDevice>data,Context context){
        mData=data;
        //默认数据为空
        mContext=context.getApplicationContext();
    }


    @Override
    public int getCount() {
        return 0;
    }

    @Override
    public Object getItem(int i) {
        return null;
    }

    @Override
    public long getItemId(int i) {
        return 0;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        View itemView=view;
        //复用veiw,优化性能
        if(itemView==null){
            //传下来的view为空,就创建一次,用android自带的简单simple_list_item_2来绘制view
            itemView=LayoutInflater.from(mContext).inflate(android.R.layout.simple_list_item_2,viewGroup,false);
        }
        TextView line1=(TextView)itemView.findViewById(android.R.id.text1);
        TextView line2=(TextView)itemView.findViewById(android.R.id.text2);

        //获取对应的蓝牙设备
        BluetoothDevice device=(BluetoothDevice)getItem(i);
        //第一行显示名称
        line1.setText(device.getName());
        line2.setText(device.getAddress());
        //第二行显示地址
        return itemView;
    }
    public void refresh(List<BluetoothDevice>data){
        mData=data;
        //刷新一次数据,就发一次通知,注册的是观察者模式
        notifyDataSetChanged();
    }
}
BluetoothController:
package com.lmj.bluetoothdemo.client;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;

import java.util.ArrayList;
import java.util.List;
//设置蓝牙被别的设备检查到

public class BluetoothController {
   /* 本地蓝牙适配器*/
    private BluetoothAdapter mAdapter;
    public BluetoothController(){
      mAdapter=BluetoothAdapter.getDefaultAdapter();
    }


    public boolean isSupportBlueTooth() {

        if (mAdapter != null) {
            //支持蓝牙,一般设备都支持蓝牙
            return true;
        } else {
            return false;
        }
    }
    public boolean getBlueToothStatus(){
        //判断蓝牙状态,true为打开,false为关闭
        //如果不支持,则会空指针,所以我们在mAdapter里加个断点函数
        assert(mAdapter!=null);
        return mAdapter.isEnabled();
    }

    /**
     *打开蓝牙
     * @param activity
     * @param requestCode
     */
    //系统主动调,一般不要设置成手动调用
        public void turnOnBluetooth(Activity activity,int requestCode){
        //通过intent获取蓝牙,action_request_enable请求可用
            Intent intent=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            //知道它的返回结果
            activity.startActivityForResult(intent,requestCode);
            //设置成自动调用
            mAdapter.enable();
        }
        public void enableVisibly(Context context){
            Intent discoverableIntent=new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
            //可发生的时间做一个intent。
            discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,300);
            //不需要forResult,因为设备不止一个,通过广播来作用
            context.startActivity(discoverableIntent);
        }

        public void findDevice(){
            assert(mAdapter!=null);
            mAdapter.startDiscovery();
        }

    /**
     * 获取绑定设备
     * @return
     */
    public List<BluetoothDevice>getBondedDeviceList(){
            return new ArrayList<>(mAdapter.getBondedDevices());
        }

    public void turnOffBlueTooth() {
        mAdapter.disable();
    }
}

目前就这些,后期会完善好。

猜你喜欢

转载自blog.csdn.net/qq_41063141/article/details/84900704