1. 网络应用中基本上都是TCP(Transmission Control Protocol传输控制协议)和UDP
(User Datagram Protocol用户数据报协议),TCP是面向连接的通信协议,UDP是无连接的通信协议.
Socket连接套接字,Java分别为TCP和UDP提供了相应的类,TCP是java.net.ServerSocket(用于服务器端)
和java.net.Socket(用于客户端);UDP是java.net.DatagramSocket
2. 两台主机之间的点对点通信,称为“单播 Unicast”。当多台主机同时接受一个数据报时,
若采用单播通信,则源主机需要给每个接收主机发送一个相同的数据报;若采用组播通信,
则源主机只需要发送一个数据报即可到达每个接收主机,从而节省了网络带宽,降低了
发送主机的负荷。
3. 组播 Multicast是一种特殊的数据报传输方式,它将具有相同需求的主机加入到某一个组,向
组发送的信息,其所有成员均可接收到。
- 组是用组播地址(D类IP地址:224.0.0.0 - 239.255.255.255)和标准UDP端口号来标识的,
即待发送的数据报的目的地址为一个组播地址。主机可以申请加入某个组播地址所标识
的组,也可以从该组中退出。
程序源代码:
http://download.csdn.net/detail/thinkinwm/6524085
命令定义
1.命令定义 80 用户注册2.命令定义 81 短消息
3.命令定义 82 文件传输
package com.android.flypigeon.util; import java.text.DecimalFormat; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import com.android.flypigeon.R; public class Constant { public static Map<String, Integer> exts = new HashMap<String, Integer>(); static { exts.put("doc", R.drawable.doc); exts.put("docx", R.drawable.doc); exts.put("xls", R.drawable.xls); exts.put("xlsx", R.drawable.xls); exts.put("ppt", R.drawable.ppt); exts.put("pptx", R.drawable.ppt); exts.put("jpg", R.drawable.image); exts.put("jpeg", R.drawable.image); exts.put("gif", R.drawable.image); exts.put("png", R.drawable.image); exts.put("ico", R.drawable.image); exts.put("apk", R.drawable.apk); exts.put("jar", R.drawable.jar); exts.put("rar", R.drawable.rar); exts.put("zip", R.drawable.rar); exts.put("mp3", R.drawable.music); exts.put("wma", R.drawable.music); exts.put("aac", R.drawable.music); exts.put("ac3", R.drawable.music); exts.put("ogg", R.drawable.music); exts.put("flac", R.drawable.music); exts.put("midi", R.drawable.music); exts.put("pcm", R.drawable.music); exts.put("wav", R.drawable.music); exts.put("amr", R.drawable.music); exts.put("m4a", R.drawable.music); exts.put("ape", R.drawable.music); exts.put("mid", R.drawable.music); exts.put("mka", R.drawable.music); exts.put("svx", R.drawable.music); exts.put("snd", R.drawable.music); exts.put("vqf", R.drawable.music); exts.put("aif", R.drawable.music); exts.put("voc", R.drawable.music); exts.put("cda", R.drawable.music); exts.put("mpc", R.drawable.music); exts.put("mpeg", R.drawable.video); exts.put("mpg", R.drawable.video); exts.put("dat", R.drawable.video); exts.put("ra", R.drawable.video); exts.put("rm", R.drawable.video); exts.put("rmvb", R.drawable.video); exts.put("mp4", R.drawable.video); exts.put("flv", R.drawable.video); exts.put("mov", R.drawable.video); exts.put("qt", R.drawable.video); exts.put("asf", R.drawable.video); exts.put("wmv", R.drawable.video); exts.put("avi", R.drawable.video); exts.put("3gp", R.drawable.video); exts.put("mkv", R.drawable.video); exts.put("f4v", R.drawable.video); exts.put("m4v", R.drawable.video); exts.put("m4p", R.drawable.video); exts.put("m2v", R.drawable.video); exts.put("dat", R.drawable.video); exts.put("xvid", R.drawable.video); exts.put("divx", R.drawable.video); exts.put("vob", R.drawable.video); exts.put("mpv", R.drawable.video); exts.put("mpeg4", R.drawable.video); exts.put("mpe", R.drawable.video); exts.put("mlv", R.drawable.video); exts.put("ogm", R.drawable.video); exts.put("m2ts", R.drawable.video); exts.put("mts", R.drawable.video); exts.put("ask", R.drawable.video); exts.put("trp", R.drawable.video); exts.put("tp", R.drawable.video); exts.put("ts", R.drawable.video); } // 自定义Action public static final String updateMyInformationAction = "com.android.flypigeon.updateMyInformation"; public static final String personHasChangedAction = "com.android.flypigeon.personHasChanged"; public static final String hasMsgUpdatedAction = "com.android.flypigeon.hasMsgUpdated"; public static final String receivedSendFileRequestAction = "com.android.flypigeon.receivedSendFileRequest"; public static final String refuseReceiveFileAction = "com.android.flypigeon.refuseReceiveFile"; public static final String remoteUserRefuseReceiveFileAction = "com.android.flypigeon.remoteUserRefuseReceiveFile"; public static final String dataReceiveErrorAction = "com.android.flypigeon.dataReceiveError"; public static final String dataSendErrorAction = "com.android.flypigeon.dataSendError"; public static final String whoIsAliveAction = "com.android.flypigeon.whoIsAlive";// 询问当前那个Activity是激活状态 public static final String imAliveNow = "com.android.flypigeon.imAliveNow"; public static final String remoteUserUnAliveAction = "com.android.flypigeon.remoteUserUnAlive"; public static final String fileSendStateUpdateAction = "com.android.flypigeon.fileSendStateUpdate"; public static final String fileReceiveStateUpdateAction = "com.android.flypigeon.fileReceiveStateUpdate"; public static final String receivedTalkRequestAction = "com.android.flypigeon.receivedTalkRequest"; public static final String acceptTalkRequestAction = "com.android.flypigeon.acceptTalkRequest"; public static final String remoteUserClosedTalkAction = "com.android.flypigeon.remoteUserClosedTalk"; // 系统Action // System Action declare public static final String bootCompleted = "android.intent.action.BOOT_COMPLETED"; public static final String WIFIACTION = "android.net.conn.CONNECTIVITY_CHANGE"; public static final String ETHACTION = "android.intent.action.ETH_STATE"; // 生成唯一ID码 public static int getMyId() { int id = (int) (Math.random() * 1000000); return id; } // other 其它定义,另外消息长度为60个汉字,utf-8中定义一个汉字占3个字节,所以消息长度为180bytes // 文件长度为30个汉字,所以总长度为90个字节 public static final int bufferSize = 256; public static final int msgLength = 180; public static final int fileNameLength = 90; public static final int readBufferSize = 4096;// 文件读写缓存 public static final byte[] pkgHead = "AND".getBytes(); public static final int CMD80 = 80; public static final int CMD81 = 81; public static final int CMD82 = 82; public static final int CMD83 = 83; public static final int CMD_TYPE1 = 1; public static final int CMD_TYPE2 = 2; public static final int CMD_TYPE3 = 3; public static final int OPR_CMD1 = 1; public static final int OPR_CMD2 = 2; public static final int OPR_CMD3 = 3; public static final int OPR_CMD4 = 4; public static final int OPR_CMD5 = 5; public static final int OPR_CMD6 = 6; public static final int OPR_CMD10 = 10; public static final String MULTICAST_IP = "239.9.9.1"; public static final int PORT = 5760; public static final int AUDIO_PORT = 5761; // int to ip转换 public static String intToIp(int i) { String ip = ((i >> 24) & 0xFF) + "." + ((i >> 16) & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + (i & 0xFF); return ip; } // 其它定义 public static final int FILE_RESULT_CODE = 1; public static final int SELECT_FILES = 1;// 是否要在文件选择器中显示文件 public static final int SELECT_FILE_PATH = 2;// 文件选择器只显示文件夹 // 文件选择状态保存 public static TreeMap<Integer, Boolean> fileSelectedState = new TreeMap<Integer, Boolean>(); // 转换文件大小 public static String formatFileSize(long fileS) { DecimalFormat df = new DecimalFormat("#.00"); String fileSizeString = ""; if (fileS < 1024) { fileSizeString = fileS + "B"; // fileSizeString = df.format((double) fileS) + "B"; } else if (fileS < 1048576) { fileSizeString = df.format((double) fileS / 1024) + "K"; } else if (fileS < 1073741824) { fileSizeString = df.format((double) fileS / 1048576) + "M"; } else { fileSizeString = df.format((double) fileS / 1073741824) + "G"; } return fileSizeString; } }
package com.android.flypigeon.service; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.MulticastSocket; import java.net.NetworkInterface; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import com.android.flypigeon.R; import com.android.flypigeon.util.ByteAndInt; import com.android.flypigeon.util.Constant; import com.android.flypigeon.util.FileName; import com.android.flypigeon.util.FileState; import com.android.flypigeon.util.Message; import com.android.flypigeon.util.Person; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioRecord; import android.media.AudioTrack; import android.media.MediaRecorder; import android.net.wifi.WifiManager; import android.os.Binder; import android.os.IBinder; import android.preference.PreferenceManager; public class MainService extends Service { private ServiceBinder sBinder = new ServiceBinder();// 服务绑定器 private static ArrayList<Map<Integer, Person>> children = new ArrayList<Map<Integer, Person>>();// 保存所有组中的用户,每个map对象保存一个组的全部用户 private static Map<Integer, Person> childrenMap = new HashMap<Integer, Person>();// 当前在线用户 private static ArrayList<Integer> personKeys = new ArrayList<Integer>();// 当前在线用户id private static Map<Integer, List<Message>> msgContainer = new HashMap<Integer, List<Message>>();// 所有用户信息容器 private SharedPreferences pre = null; private SharedPreferences.Editor editor = null; private WifiManager wifiManager = null; private ServiceBroadcastReceiver receiver = null; private InetAddress localInetAddress = null; private String localIp = null; private byte[] localIpBytes = null; private byte[] regBuffer = new byte[Constant.bufferSize];// 本机网络注册交互指令 private byte[] msgSendBuffer = new byte[Constant.bufferSize];// 信息发送交互 private byte[] fileSendBuffer = new byte[Constant.bufferSize];// 文件发送交互指令 private byte[] talkCmdBuffer = new byte[Constant.bufferSize];// 通话指令 private static Person me = null;// 用来保存自身的相关信息 private CommunicationBridge comBridge = null;// 通讯与协议解析模块 @Override public IBinder onBind(Intent arg0) { return sBinder; } @Override public boolean onUnbind(Intent intent) { return false; } @Override public void onRebind(Intent intent) { } @Override public void onCreate() { } @Override public void onStart(Intent intent, int startId) { initCmdBuffer();// 初始化指令缓存 wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); new CheckNetConnectivity().start();// 侦测网络状态,获取IP地址 comBridge = new CommunicationBridge();// 启动socket连接 comBridge.start(); pre = PreferenceManager.getDefaultSharedPreferences(this); editor = pre.edit(); regBroadcastReceiver();// 注册广播接收器 getMyInfomation();// 获得自身信息 new UpdateMe().start();// 向网络发送心跳包,并注册 new CheckUserOnline().start();// 检查用户列表是否有超时用户 sendPersonHasChangedBroadcast();// 通知有新用户加入或退出 System.out.println("Service started..."); } // 服务绑定 public class ServiceBinder extends Binder { public MainService getService() { return MainService.this; } } // 获得自已的相关信息 private void getMyInfomation() { SharedPreferences pre = PreferenceManager .getDefaultSharedPreferences(this); int iconId = pre.getInt("headIconId", R.drawable.black_bird); String nickeName = pre.getString("nickeName", "Zhang San"); int myId = pre.getInt("myId", Constant.getMyId()); editor.putInt("myId", myId); editor.commit(); if (null == me) me = new Person(); me.personHeadIconId = iconId; me.personNickeName = nickeName; me.personId = myId; me.ipAddress = localIp; // 更新注册命令用户数据 System.arraycopy(ByteAndInt.int2ByteArray(myId), 0, regBuffer, 6, 4); System.arraycopy(ByteAndInt.int2ByteArray(iconId), 0, regBuffer, 10, 4); for (int i = 14; i < 44; i++) regBuffer[i] = 0;// 把原来的昵称内容清空 byte[] nickeNameBytes = nickeName.getBytes(); System.arraycopy(nickeNameBytes, 0, regBuffer, 14, nickeNameBytes.length); // 更新通话命令用户数据 System.arraycopy(ByteAndInt.int2ByteArray(myId), 0, talkCmdBuffer, 6, 4); System.arraycopy(ByteAndInt.int2ByteArray(iconId), 0, talkCmdBuffer, 10, 4); for (int i = 14; i < 44; i++) talkCmdBuffer[i] = 0;// 把原来的昵称内容清空 System.arraycopy(nickeNameBytes, 0, talkCmdBuffer, 14, nickeNameBytes.length); } private String getCurrentTime() { Date date = new Date(); return date.toLocaleString(); } // 检测网络连接状态,获得本机IP地址 private class CheckNetConnectivity extends Thread { public void run() { try { if (!wifiManager.isWifiEnabled()) { wifiManager.setWifiEnabled(true); } for (Enumeration<NetworkInterface> en = NetworkInterface .getNetworkInterfaces(); en.hasMoreElements();) { NetworkInterface intf = en.nextElement(); for (Enumeration<InetAddress> enumIpAddr = intf .getInetAddresses(); enumIpAddr.hasMoreElements();) { InetAddress inetAddress = enumIpAddr.nextElement(); if (!inetAddress.isLoopbackAddress()) { if (inetAddress.isReachable(1000)) { localInetAddress = inetAddress; localIp = inetAddress.getHostAddress() .toString(); localIpBytes = inetAddress.getAddress(); System.arraycopy(localIpBytes, 0, regBuffer, 44, 4); } } } } } catch (Exception ex) { ex.printStackTrace(); } }; }; // 初始化指令缓存 private void initCmdBuffer() { // 初始化用户注册指令缓存 for (int i = 0; i < Constant.bufferSize; i++) regBuffer[i] = 0; System.arraycopy(Constant.pkgHead, 0, regBuffer, 0, 3); regBuffer[3] = Constant.CMD80; regBuffer[4] = Constant.CMD_TYPE1; regBuffer[5] = Constant.OPR_CMD1; // 初始化信息发送指令缓存 for (int i = 0; i < Constant.bufferSize; i++) msgSendBuffer[i] = 0; System.arraycopy(Constant.pkgHead, 0, msgSendBuffer, 0, 3); msgSendBuffer[3] = Constant.CMD81; msgSendBuffer[4] = Constant.CMD_TYPE1; msgSendBuffer[5] = Constant.OPR_CMD1; // 初始化发送文件指令缓存 for (int i = 0; i < Constant.bufferSize; i++) fileSendBuffer[i] = 0; System.arraycopy(Constant.pkgHead, 0, fileSendBuffer, 0, 3); fileSendBuffer[3] = Constant.CMD82; fileSendBuffer[4] = Constant.CMD_TYPE1; fileSendBuffer[5] = Constant.OPR_CMD1; // 初始化通话指令 // 初始化发送文件指令缓存 for (int i = 0; i < Constant.bufferSize; i++) talkCmdBuffer[i] = 0; System.arraycopy(Constant.pkgHead, 0, talkCmdBuffer, 0, 3); talkCmdBuffer[3] = Constant.CMD83; talkCmdBuffer[4] = Constant.CMD_TYPE1; talkCmdBuffer[5] = Constant.OPR_CMD1; } // 获得所有用户对象 public ArrayList<Map<Integer, Person>> getChildren() { return children; } // 获得所有用户id public ArrayList<Integer> getPersonKeys() { return personKeys; } // 根据用户id获得该用户的消息 public List<Message> getMessagesById(int personId) { return msgContainer.get(personId); } // 根据用户id获得该用户的消息数量 public int getMessagesCountById(int personId) { List<Message> msgs = msgContainer.get(personId); if (null != msgs) { return msgs.size(); } else { return 0; } } // 每隔10秒发送一个心跳包 boolean isStopUpdateMe = false; private class UpdateMe extends Thread { @Override public void run() { while (!isStopUpdateMe) { try { comBridge.joinOrganization(); sleep(10000); } catch (Exception e) { e.printStackTrace(); } } } } // 检测用户是否在线,如果超过15说明用户已离线,秒则从列表中清除该用户 private class CheckUserOnline extends Thread { @Override public void run() { super.run(); boolean hasChanged = false; while (!isStopUpdateMe) { if (childrenMap.size() > 0) { Set<Integer> keys = childrenMap.keySet(); for (Integer key : keys) { if (System.currentTimeMillis() - childrenMap.get(key).timeStamp > 15000) { childrenMap.remove(key); personKeys.remove(Integer.valueOf(key)); hasChanged = true; } } } if (hasChanged) sendPersonHasChangedBroadcast(); try { sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } } } // 发送用户更新广播 private void sendPersonHasChangedBroadcast() { Intent intent = new Intent(); intent.setAction(Constant.personHasChangedAction); sendBroadcast(intent); } // 注册广播接收器 private void regBroadcastReceiver() { receiver = new ServiceBroadcastReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction(Constant.WIFIACTION); filter.addAction(Constant.ETHACTION); filter.addAction(Constant.updateMyInformationAction); filter.addAction(Constant.refuseReceiveFileAction); filter.addAction(Constant.imAliveNow); registerReceiver(receiver, filter); } // 广播接收器处理类 private class ServiceBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Constant.WIFIACTION) || intent.getAction().equals(Constant.ETHACTION)) { new CheckNetConnectivity().start(); } else if (intent.getAction().equals( Constant.updateMyInformationAction)) { getMyInfomation(); comBridge.joinOrganization(); } else if (intent.getAction().equals( Constant.refuseReceiveFileAction)) { comBridge.refuseReceiveFile(); } else if (intent.getAction().equals(Constant.imAliveNow)) { } } } // 发送信息 public void sendMsg(int personId, String msg) { comBridge.sendMsg(personId, msg); } // 发送文件 public void sendFiles(int personId, ArrayList<FileName> files) { comBridge.sendFiles(personId, files); } // 接收文件 public void receiveFiles(String fileSavePath) { comBridge.receiveFiles(fileSavePath); } // 获得欲接收的文件名 public ArrayList<FileState> getReceivedFileNames() { return comBridge.getReceivedFileNames(); } // 获得欲发送的文件名 public ArrayList<FileState> getBeSendFileNames() { return comBridge.getBeSendFileNames(); } // 开始语音呼叫 public void startTalk(int personId) { comBridge.startTalk(personId); } // 结束语音呼叫 public void stopTalk(int personId) { comBridge.stopTalk(personId); } // 接受远程语音呼叫 public void acceptTalk(int personId) { comBridge.acceptTalk(personId); } @Override public void onDestroy() { comBridge.release(); unregisterReceiver(receiver); isStopUpdateMe = true; System.out.println("Service on destory..."); } // ========================协议分析与通讯模块======================================================= private class CommunicationBridge extends Thread { private MulticastSocket multicastSocket = null; private byte[] recvBuffer = new byte[Constant.bufferSize]; private int fileSenderUid = 0;// 用来保存文件发送者的id号 private boolean isBusyNow = false;// 现在是否正在收发文件,如果该状态为true则表示现在正在进行收发文件操作,这时需要向其它发送文件的用户发送忙指令 private String fileSavePath = null;// 用来保存接收到的文件 private boolean isStopTalk = false;// 通话结束标志 private ArrayList<FileName> tempFiles = null;// 用来临时保存需要发送的文件名 private int tempUid = 0;// 用来临时保存需要发送文件的用户id(接受文件方的用户id) private ArrayList<FileState> receivedFileNames = new ArrayList<FileState>(); private ArrayList<FileState> beSendFileNames = new ArrayList<FileState>(); private FileHandler fileHandler = null;// 文件处理线程,用来收发文件 private AudioHandler audioHandler = null;// 音频处理模块,用来收发音频数据 public CommunicationBridge() { fileHandler = new FileHandler(); fileHandler.start(); audioHandler = new AudioHandler(); audioHandler.start(); } // 打开组播端口,准备组播通讯 @Override public void run() { super.run(); try { multicastSocket = new MulticastSocket(Constant.PORT); multicastSocket.joinGroup(InetAddress .getByName(Constant.MULTICAST_IP)); System.out.println("Socket started..."); while (!multicastSocket.isClosed() && null != multicastSocket) { for (int i = 0; i < Constant.bufferSize; i++) { recvBuffer[i] = 0; } DatagramPacket rdp = new DatagramPacket(recvBuffer, recvBuffer.length); multicastSocket.receive(rdp); parsePackage(recvBuffer); } } catch (Exception e) { try { if (null != multicastSocket && !multicastSocket.isClosed()) { multicastSocket.leaveGroup(InetAddress .getByName(Constant.MULTICAST_IP)); multicastSocket.close(); } } catch (Exception e1) { e1.printStackTrace(); } e.printStackTrace(); } } // 解析接收到的数据包 private void parsePackage(byte[] pkg) { int CMD = pkg[3];// 命令字 int cmdType = pkg[4];// 命令类型 int oprCmd = pkg[5];// 操作命令 // 获得用户ID号 byte[] uId = new byte[4]; System.arraycopy(pkg, 6, uId, 0, 4); int userId = ByteAndInt.byteArray2Int(uId); switch (CMD) { case Constant.CMD80: switch (cmdType) { case Constant.CMD_TYPE1: // 如果该信息不是自己发出则给对方发送回应包,并把对方加入用户列表 if (userId != me.personId) { updatePerson(userId, pkg); // 发送应答包 byte[] ipBytes = new byte[4];// 获得请求方的ip地址 System.arraycopy(pkg, 44, ipBytes, 0, 4); try { InetAddress targetIp = InetAddress .getByAddress(ipBytes); regBuffer[4] = Constant.CMD_TYPE2;// 把自己的注册信息修改成应答信息标志,把自己的信息发送给请求方 DatagramPacket dp = new DatagramPacket(regBuffer, Constant.bufferSize, targetIp, Constant.PORT); multicastSocket.send(dp); } catch (Exception e) { e.printStackTrace(); } } break; case Constant.CMD_TYPE2: updatePerson(userId, pkg); break; case Constant.CMD_TYPE3: childrenMap.remove(userId); personKeys.remove(Integer.valueOf(userId)); sendPersonHasChangedBroadcast(); break; } break; case Constant.CMD81:// 收到信息 switch (cmdType) { case Constant.CMD_TYPE1: List<Message> messages = null; if (msgContainer.containsKey(userId)) { messages = msgContainer.get(userId); } else { messages = new ArrayList<Message>(); } byte[] msgBytes = new byte[Constant.msgLength]; System.arraycopy(pkg, 10, msgBytes, 0, Constant.msgLength); String msgStr = new String(msgBytes).trim(); Message msg = new Message(); msg.msg = msgStr; msg.receivedTime = getCurrentTime(); messages.add(msg); msgContainer.put(userId, messages); Intent intent = new Intent(); intent.setAction(Constant.hasMsgUpdatedAction); intent.putExtra("userId", userId); intent.putExtra("msgCount", messages.size()); sendBroadcast(intent); break; case Constant.CMD_TYPE2: break; } break; case Constant.CMD82: switch (cmdType) { case Constant.CMD_TYPE1:// 收到文件传输请求 switch (oprCmd) { case Constant.OPR_CMD1: // 发送广播,通知界面有文件需要传输 if (!isBusyNow) { // isBusyNow = true; fileSenderUid = userId;// 保存文件发送者的id号,以便后面若接收者拒绝接收文件时可以通过该id找到发送者,并给发送者发送拒绝接收指令 Person person = childrenMap.get(Integer .valueOf(userId)); Intent intent = new Intent(); intent.putExtra("person", person); intent.setAction(Constant.receivedSendFileRequestAction); sendBroadcast(intent); } else {// 如果当前正在收发文件则向对方发送忙指令 Person person = childrenMap.get(Integer .valueOf(userId)); fileSendBuffer[4] = Constant.CMD_TYPE2; fileSendBuffer[5] = Constant.OPR_CMD4; byte[] meIdBytes = ByteAndInt .int2ByteArray(me.personId); System.arraycopy(meIdBytes, 0, fileSendBuffer, 6, 4); try { DatagramPacket dp = new DatagramPacket( fileSendBuffer, Constant.bufferSize, InetAddress.getByName(person.ipAddress), Constant.PORT); multicastSocket.send(dp); } catch (Exception e) { e.printStackTrace(); } } break; case Constant.OPR_CMD5:// 接收对方传过来的文件名信息 byte[] fileNameBytes = new byte[Constant.fileNameLength]; byte[] fileSizeByte = new byte[8]; System.arraycopy(pkg, 10, fileNameBytes, 0, Constant.fileNameLength); System.arraycopy(pkg, 100, fileSizeByte, 0, 8); FileState fs = new FileState(); fs.fileName = new String(fileNameBytes).trim(); fs.fileSize = Long.valueOf(ByteAndInt .byteArrayToLong(fileSizeByte)); receivedFileNames.add(fs); break; } break; case Constant.CMD_TYPE2: switch (oprCmd) { case Constant.OPR_CMD2:// 对方同意接收文件 fileHandler.startSendFile(); System.out .println("Start send file to remote user ..."); break; case Constant.OPR_CMD3:// 对方拒绝接收文件 Intent intent = new Intent(); intent.setAction(Constant.remoteUserRefuseReceiveFileAction); sendBroadcast(intent); System.out .println("Remote user refuse to receive file ..."); break; case Constant.OPR_CMD4:// 对方现在忙 System.out.println("Remote user is busy now ..."); break; } break; } break; case Constant.CMD83:// 83命令,语音通讯相关 switch (cmdType) { case Constant.CMD_TYPE1: switch (oprCmd) { case Constant.OPR_CMD1:// 接收到远程语音通话请求 System.out.println("Received a talk request ... "); isStopTalk = false; Person person = childrenMap .get(Integer.valueOf(userId)); Intent intent = new Intent(); intent.putExtra("person", person); intent.setAction(Constant.receivedTalkRequestAction); sendBroadcast(intent); break; case Constant.OPR_CMD2: // 收到关闭指令,关闭语音通话 System.out .println("Received remote user stop talk cmd ... "); isStopTalk = true; Intent i = new Intent(); i.setAction(Constant.remoteUserClosedTalkAction); sendBroadcast(i); break; } break; case Constant.CMD_TYPE2: switch (oprCmd) { case Constant.OPR_CMD1: // 被叫应答,开始语音通话 if (!isStopTalk) { System.out .println("Begin to talk with remote user ... "); Person person = childrenMap.get(Integer .valueOf(userId)); audioHandler.audioSend(person); } break; } break; } break; } } // 更新或加用户信息到用户列表中 private void updatePerson(int userId, byte[] pkg) { Person person = new Person(); getPerson(pkg, person); childrenMap.put(userId, person); if (!personKeys.contains(Integer.valueOf(userId))) personKeys.add(Integer.valueOf(userId)); if (!children.contains(childrenMap)) children.add(childrenMap); sendPersonHasChangedBroadcast(); } // 关闭Socket连接 private void release() { try { regBuffer[4] = Constant.CMD_TYPE3;// 把命令类型修改成注消标志,并广播发送,从所有用户中退出 DatagramPacket dp = new DatagramPacket(regBuffer, Constant.bufferSize, InetAddress.getByName(Constant.MULTICAST_IP), Constant.PORT); multicastSocket.send(dp); System.out.println("Send logout cmd ..."); multicastSocket.leaveGroup(InetAddress .getByName(Constant.MULTICAST_IP)); multicastSocket.close(); System.out.println("Socket has closed ..."); } catch (Exception e) { e.printStackTrace(); } finally { fileHandler.release(); audioHandler.release(); } } // 分析数据包并获取一个用户信息 private void getPerson(byte[] pkg, Person person) { byte[] personIdBytes = new byte[4]; byte[] iconIdBytes = new byte[4]; byte[] nickeNameBytes = new byte[30]; byte[] personIpBytes = new byte[4]; System.arraycopy(pkg, 6, personIdBytes, 0, 4); System.arraycopy(pkg, 10, iconIdBytes, 0, 4); System.arraycopy(pkg, 14, nickeNameBytes, 0, 30); System.arraycopy(pkg, 44, personIpBytes, 0, 4); person.personId = ByteAndInt.byteArray2Int(personIdBytes); person.personHeadIconId = ByteAndInt.byteArray2Int(iconIdBytes); person.personNickeName = (new String(nickeNameBytes)).trim(); person.ipAddress = Constant.intToIp(ByteAndInt .byteArray2Int(personIpBytes)); person.timeStamp = System.currentTimeMillis(); } // 注册自己到网络中 public void joinOrganization() { try { if (null != multicastSocket && !multicastSocket.isClosed()) { regBuffer[4] = Constant.CMD_TYPE1;// 恢复成注册请求标志,向网络中注册自己 DatagramPacket dp = new DatagramPacket(regBuffer, Constant.bufferSize, InetAddress.getByName(Constant.MULTICAST_IP), Constant.PORT); multicastSocket.send(dp); } } catch (IOException e) { e.printStackTrace(); } } // 发送信息 public void sendMsg(int personId, String msg) { try { Person psn = childrenMap.get(personId); if (null != psn) { System.arraycopy(ByteAndInt.int2ByteArray(me.personId), 0, msgSendBuffer, 6, 4); int msgLength = Constant.msgLength + 10; for (int i = 10; i < msgLength; i++) { msgSendBuffer[i] = 0; } byte[] msgBytes = msg.getBytes(); System.arraycopy(msgBytes, 0, msgSendBuffer, 10, msgBytes.length); DatagramPacket dp = new DatagramPacket(msgSendBuffer, Constant.bufferSize, InetAddress.getByName(psn.ipAddress), Constant.PORT); multicastSocket.send(dp); } } catch (Exception e) { e.printStackTrace(); } } // 向对方发送请求接收文件指令 public void sendFiles(int personId, ArrayList<FileName> files) { if (personId > 0 && null != files && files.size() > 0) { try { tempUid = personId; tempFiles = files; Person person = childrenMap.get(Integer.valueOf(tempUid)); fileSendBuffer[4] = Constant.CMD_TYPE1; fileSendBuffer[5] = Constant.OPR_CMD5; byte[] meIdBytes = ByteAndInt.int2ByteArray(me.personId); System.arraycopy(meIdBytes, 0, fileSendBuffer, 6, 4); int fileNameLength = Constant.fileNameLength + 10;// 清除头文件包的文件名存储区域,以便写新的文件名 // 把要传送的所有文件名传送给对方 for (final FileName file : tempFiles) { // 收集生成要发送文件的文相关资料 FileState fs = new FileState(file.fileSize, 0, file.getFileName()); beSendFileNames.add(fs); byte[] fileNameBytes = file.getFileName().getBytes(); for (int i = 10; i < fileNameLength; i++) fileSendBuffer[i] = 0; System.arraycopy(fileNameBytes, 0, fileSendBuffer, 10, fileNameBytes.length);// 把文件名放入头数据包 System.arraycopy( ByteAndInt.longToByteArray(file.fileSize), 0, fileSendBuffer, 100, 8); DatagramPacket dp = new DatagramPacket(fileSendBuffer, Constant.bufferSize, InetAddress.getByName(person.ipAddress), Constant.PORT); multicastSocket.send(dp); } // 对方发送请求接收文件指令 fileSendBuffer[5] = Constant.OPR_CMD1; DatagramPacket dp = new DatagramPacket(fileSendBuffer, Constant.bufferSize, InetAddress.getByName(person.ipAddress), Constant.PORT); multicastSocket.send(dp); } catch (Exception e) { e.printStackTrace(); } } } // 向对方响应同意接收文件指令 public void receiveFiles(String fileSavePath) { this.fileSavePath = fileSavePath; Person person = childrenMap.get(Integer.valueOf(fileSenderUid)); fileSendBuffer[4] = Constant.CMD_TYPE2; fileSendBuffer[5] = Constant.OPR_CMD2; byte[] meIdBytes = ByteAndInt.int2ByteArray(me.personId); System.arraycopy(meIdBytes, 0, fileSendBuffer, 6, 4); try { DatagramPacket dp = new DatagramPacket(fileSendBuffer, Constant.bufferSize, InetAddress.getByName(person.ipAddress), Constant.PORT); multicastSocket.send(dp); } catch (Exception e) { e.printStackTrace(); } } // 向文件发送者发送拒绝接收文件指令 public void refuseReceiveFile() { // isBusyNow = false; Person person = childrenMap.get(Integer.valueOf(fileSenderUid)); fileSendBuffer[4] = Constant.CMD_TYPE2; fileSendBuffer[5] = Constant.OPR_CMD3; byte[] meIdBytes = ByteAndInt.int2ByteArray(me.personId); System.arraycopy(meIdBytes, 0, fileSendBuffer, 6, 4); try { DatagramPacket dp = new DatagramPacket(fileSendBuffer, Constant.bufferSize, InetAddress.getByName(person.ipAddress), Constant.PORT); multicastSocket.send(dp); } catch (Exception e) { e.printStackTrace(); } } // 获得欲接收文件的文件名 public ArrayList<FileState> getReceivedFileNames() { return receivedFileNames; } // 获得欲发送文件的文件名 public ArrayList<FileState> getBeSendFileNames() { return beSendFileNames; } // 开始语音呼叫,向远方发送语音呼叫请求 public void startTalk(int personId) { try { isStopTalk = false; talkCmdBuffer[4] = Constant.CMD_TYPE1; talkCmdBuffer[5] = Constant.OPR_CMD1; System.arraycopy(InetAddress.getByName(me.ipAddress) .getAddress(), 0, talkCmdBuffer, 44, 4); Person person = childrenMap.get(Integer.valueOf(personId)); DatagramPacket dp = new DatagramPacket(talkCmdBuffer, Constant.bufferSize, InetAddress.getByName(person.ipAddress), Constant.PORT); multicastSocket.send(dp); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } // 结束语音呼叫 public void stopTalk(int personId) { isStopTalk = true; talkCmdBuffer[4] = Constant.CMD_TYPE1; talkCmdBuffer[5] = Constant.OPR_CMD2; Person person = childrenMap.get(Integer.valueOf(personId)); try { System.arraycopy(InetAddress.getByName(me.ipAddress) .getAddress(), 0, talkCmdBuffer, 44, 4); DatagramPacket dp = new DatagramPacket(talkCmdBuffer, Constant.bufferSize, InetAddress.getByName(person.ipAddress), Constant.PORT); multicastSocket.send(dp); } catch (Exception e) { e.printStackTrace(); } } // 接受远程语音呼叫请求,并向远程发送语音数据 public void acceptTalk(int personId) { talkCmdBuffer[3] = Constant.CMD83; talkCmdBuffer[4] = Constant.CMD_TYPE2; talkCmdBuffer[5] = Constant.OPR_CMD1; try { // 发送接受语音指令 System.arraycopy(InetAddress.getByName(me.ipAddress) .getAddress(), 0, talkCmdBuffer, 44, 4); Person person = childrenMap.get(Integer.valueOf(personId)); DatagramPacket dp = new DatagramPacket(talkCmdBuffer, Constant.bufferSize, InetAddress.getByName(person.ipAddress), Constant.PORT); multicastSocket.send(dp); audioHandler.audioSend(person);// 同时向对方发送音频数据 } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } // =========================TCP文件传输模块================================================================== // 基于Tcp传输的文件收发模块 private class FileHandler extends Thread { private ServerSocket sSocket = null; public FileHandler() { } @Override public void run() { super.run(); try { sSocket = new ServerSocket(Constant.PORT); System.out.println("File Handler socket started ..."); while (!sSocket.isClosed() && null != sSocket) { Socket socket = sSocket.accept(); socket.setSoTimeout(5000); new SaveFileToDisk(socket).start(); } } catch (IOException e) { e.printStackTrace(); } } // 保存接收到的数据 private class SaveFileToDisk extends Thread { private Socket socket = null; public SaveFileToDisk(Socket socket) { this.socket = socket; } @Override public void run() { super.run(); OutputStream output = null; InputStream input = null; try { byte[] recvFileCmd = new byte[Constant.bufferSize];// 接收对方第一次发过来的数据,该数据包中包含了要发送的文件名 input = socket.getInputStream(); input.read(recvFileCmd);// 读取对方发过来的数据 int cmdType = recvFileCmd[4];// 按协议这位为命令类型 int oprCmd = recvFileCmd[5];// 操作命令 if (cmdType == Constant.CMD_TYPE1 && oprCmd == Constant.OPR_CMD6) { byte[] fileNameBytes = new byte[Constant.fileNameLength];// 从收到的数据包中提取文件名 System.arraycopy(recvFileCmd, 10, fileNameBytes, 0, Constant.fileNameLength); StringBuffer sb = new StringBuffer(); String fName = new String(fileNameBytes).trim(); sb.append(fileSavePath).append(File.separator) .append(fName);// 组合成完整的文件名 String fileName = sb.toString(); File file = new File(fileName);// 根据获得的文件名创建文件 // 定义数据接收缓冲区,准备接收对方传过来的文件内容 byte[] readBuffer = new byte[Constant.readBufferSize]; output = new FileOutputStream(file);// 打开文件输出流准备把接收到的内容写到文件中 int readSize = 0; int length = 0; long count = 0; FileState fs = getFileStateByName(fName, receivedFileNames); while (-1 != (readSize = input.read(readBuffer))) {// 循环读取内容 output.write(readBuffer, 0, readSize);// 把接收到的内容写到文件中 output.flush(); length += readSize; count++; if (count % 10 == 0) { fs.currentSize = length; fs.percent = ((int) ((Float.valueOf(length) / Float .valueOf(fs.fileSize)) * 100)); Intent intent = new Intent(); intent.setAction(Constant.fileReceiveStateUpdateAction); sendBroadcast(intent); } } fs.currentSize = length; fs.percent = ((int) ((Float.valueOf(length) / Float .valueOf(fs.fileSize)) * 100)); Intent intent = new Intent(); intent.setAction(Constant.fileReceiveStateUpdateAction); sendBroadcast(intent); } else { Intent intent = new Intent(); intent.putExtra("msg", getString(R.string.data_receive_error)); intent.setAction(Constant.dataReceiveErrorAction); sendBroadcast(intent); } } catch (Exception e) { Intent intent = new Intent(); intent.putExtra("msg", e.getMessage()); intent.setAction(Constant.dataReceiveErrorAction); sendBroadcast(intent); e.printStackTrace(); } finally { try { if (null != input) input.close(); if (null != output) output.close(); if (!socket.isClosed()) socket.close(); } catch (Exception e1) { e1.printStackTrace(); } } } } // 开始给对方发送文件 public void startSendFile() { // 获得接收方信息 Person person = childrenMap.get(Integer.valueOf(tempUid)); final String userIp = person.ipAddress; // 组合头数据包,该数据包中包括要发送的文件名 final byte[] sendFileCmd = new byte[Constant.bufferSize]; for (int i = 0; i < Constant.bufferSize; i++) sendFileCmd[i] = 0; System.arraycopy(Constant.pkgHead, 0, sendFileCmd, 0, 3); sendFileCmd[3] = Constant.CMD82; sendFileCmd[4] = Constant.CMD_TYPE1; sendFileCmd[5] = Constant.OPR_CMD6; System.arraycopy(ByteAndInt.int2ByteArray(me.personId), 0, sendFileCmd, 6, 4); for (final FileName file : tempFiles) {// 采用多线程发送文件 new Thread() { @Override public void run() { Socket socket = null; OutputStream output = null; InputStream input = null; try { socket = new Socket(userIp, Constant.PORT); byte[] fileNameBytes = file.getFileName() .getBytes(); int fileNameLength = Constant.fileNameLength + 10;// 清除头文件包的文件名存储区域,以便写新的文件名 for (int i = 10; i < fileNameLength; i++) sendFileCmd[i] = 0; System.arraycopy(fileNameBytes, 0, sendFileCmd, 10, fileNameBytes.length);// 把文件名放入头数据包 System.arraycopy(ByteAndInt .longToByteArray(file.fileSize), 0, sendFileCmd, 100, 8); output = socket.getOutputStream();// 构造一个输出流 output.write(sendFileCmd);// 把头数据包发给对方 output.flush(); sleep(1000);// sleep 1秒钟,等待对方处理完 // 定义数据发送缓冲区 byte[] readBuffer = new byte[Constant.readBufferSize];// 文件读写缓存 input = new FileInputStream(new File( file.fileName));// 打开一个文件输入流 int readSize = 0; int length = 0; long count = 0; FileState fs = getFileStateByName( file.getFileName(), beSendFileNames); while (-1 != (readSize = input.read(readBuffer))) {// 循环把文件内容发送给对方 output.write(readBuffer, 0, readSize);// 把内容写到输出流中发送给对方 output.flush(); length += readSize; count++; if (count % 10 == 0) { fs.currentSize = length; fs.percent = ((int) ((Float .valueOf(length) / Float .valueOf(fs.fileSize)) * 100)); Intent intent = new Intent(); intent.setAction(Constant.fileSendStateUpdateAction); sendBroadcast(intent); } } fs.currentSize = length; fs.percent = ((int) ((Float.valueOf(length) / Float .valueOf(fs.fileSize)) * 100)); Intent intent = new Intent(); intent.setAction(Constant.fileSendStateUpdateAction); sendBroadcast(intent); } catch (Exception e) { // 往界面层发送文件传输出错信息 Intent intent = new Intent(); intent.putExtra("msg", e.getMessage()); intent.setAction(Constant.dataSendErrorAction); sendBroadcast(intent); e.printStackTrace(); } finally { try { if (null != output) output.close(); if (null != input) input.close(); if (!socket.isClosed()) socket.close(); } catch (Exception e1) { e1.printStackTrace(); } } } }.start(); } } // 根据文件名从文件状态列表中获得该文件状态 private FileState getFileStateByName(String fileName, ArrayList<FileState> fileStates) { for (FileState fileState : fileStates) { if (fileState.fileName.equals(fileName)) { return fileState; } } return null; } public void release() { try { System.out.println("File handler socket closed ..."); if (null != sSocket) sSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } // =========================TCP文件传输模块结束============================================================== // =========================TCP语音传输模块================================================================== // 基于Tcp语音传输模块 private class AudioHandler extends Thread { private ServerSocket sSocket = null; // private G711Codec codec; public AudioHandler() { } @Override public void run() { super.run(); try { sSocket = new ServerSocket(Constant.AUDIO_PORT);// 监听音频端口 System.out.println("Audio Handler socket started ..."); while (!sSocket.isClosed() && null != sSocket) { Socket socket = sSocket.accept(); socket.setSoTimeout(5000); audioPlay(socket); } } catch (IOException e) { e.printStackTrace(); } } // 用来启动音频播放子线程 public void audioPlay(Socket socket) { new AudioPlay(socket).start(); } // 用来启动音频发送子线程 public void audioSend(Person person) { new AudioSend(person).start(); } // 音频播线程 public class AudioPlay extends Thread { Socket socket = null; public AudioPlay(Socket socket) { this.socket = socket; // android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); } @Override public void run() { super.run(); try { InputStream is = socket.getInputStream(); // 获得音频缓冲区大小 int bufferSize = android.media.AudioTrack .getMinBufferSize(8000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT); // 获得音轨对象 AudioTrack player = new AudioTrack( AudioManager.STREAM_MUSIC, 8000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM); // 设置喇叭音量 player.setStereoVolume(1.0f, 1.0f); // 开始播放声音 player.play(); byte[] audio = new byte[160];// 音频读取缓存 int length = 0; while (!isStopTalk) { length = is.read(audio);// 从网络读取音频数据 if (length > 0 && length % 2 == 0) { // for(int // i=0;i<length;i++)audio[i]=(byte)(audio[i]*2);//音频放大1倍 player.write(audio, 0, length);// 播放音频数据 } } player.stop(); is.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } // 音频发送线程 public class AudioSend extends Thread { Person person = null; public AudioSend(Person person) { this.person = person; // android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); } @Override public void run() { super.run(); Socket socket = null; OutputStream os = null; AudioRecord recorder = null; try { socket = new Socket(person.ipAddress, Constant.AUDIO_PORT); socket.setSoTimeout(5000); os = socket.getOutputStream(); // 获得录音缓冲区大小 int bufferSize = AudioRecord.getMinBufferSize(8000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT); // 获得录音机对象 recorder = new AudioRecord( MediaRecorder.AudioSource.MIC, 8000, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize * 10); recorder.startRecording();// 开始录音 byte[] readBuffer = new byte[640];// 录音缓冲区 int length = 0; while (!isStopTalk) { length = recorder.read(readBuffer, 0, 640);// 从mic读取音频数据 if (length > 0 && length % 2 == 0) { os.write(readBuffer, 0, length);// 写入到输出流,把音频数据通过网络发送给对方 } } recorder.stop(); os.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } public void release() { try { System.out.println("Audio handler socket closed ..."); if (null != sSocket) sSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } // =========================TCP语音传输模块结束================================================================== } // ========================协议分析与通讯模块结束======================================================= }