I would like to create a network application where some devices have to send a packet to the same another device. This device is an Android one. My idea is to broadcast the message to the network so that the device will get it. I have checked on the Internet and I have found that one solution might be the MulticastSocket. I've followed the tutorial from the javadoc and this is quite easy. I did it on my Android phone and on one computer. The problem I have now is the fact that I want this socket to be bound on port 80. Effectively, I get an error, more precisely an EACCES when I try to create the socket. Here is the code of my server :
public class MyServer extends Thread {
private int port;
private boolean isRunning = true;
private MulticastSocket socket;
private InetAddress group;
public MyServer(int port) {
this.port = port;
isRunning = true;
}
public void run() {
socket = null;
try {
socket = new MulticastSocket(80);
group = InetAddress.getByName("coucou");
socket.joinGroup(group);
} catch (IOException e) {
e.printStackTrace();
return;
}
while (isRunning) {
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
try {
socket.receive(packet);
Log.i("Server", "Packet received");
MyCipher rec = new MyCipher(Arrays.copyOfRange(packet.getData(), 0, packet.getLength()));
Receiver.getInstance().put(rec);
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
public void mustStop() {
this.notify();
isRunning = false;
}
}
Does someone have an idea how to fix it ? Furthermore, does someone know if the name of the group must be the ip of the server or might it be a "random" string ?
Thank you !
Related
I am writing an app on Android Studio.
I communicate from an Android device to an arduino board via Bluetooth.
For now everything works but i am starting a new Activity and i need to stop the actual BT connection. so i want to call a stop method.
The problem is that it crash when i call it.
here is the code
public class BtInterface {
private BluetoothDevice device = null;
private BluetoothSocket socket = null;
private InputStream receiveStream = null;
private OutputStream sendStream = null;
String GlobalBuff="";
String Right_Buff="";
private ReceiverThread receiverThread;
Handler handler;
public BtInterface(Handler hstatus, Handler h,String Device_Name) {
Set<BluetoothDevice> setpairedDevices = BluetoothAdapter.getDefaultAdapter().getBondedDevices();
BluetoothDevice[] pairedDevices = (BluetoothDevice[]) setpairedDevices.toArray(new BluetoothDevice[setpairedDevices.size()]);
for(int i=0;i<pairedDevices.length;i++) {
if(pairedDevices[i].getName().contains(Device_Name)) {
device = pairedDevices[i];
try {
socket = device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
receiveStream = socket.getInputStream();
sendStream = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
handler = hstatus;
receiverThread = new ReceiverThread(h);
}
public void sendData(String data) {
sendData(data, false);
}
public void sendData(String data, boolean deleteScheduledData) {
try {
sendStream.write(data.getBytes());
sendStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public void connect() {
new Thread() {
#Override public void run() {
try {
socket.connect();
Message msg = handler.obtainMessage();
msg.arg1 = 1;
handler.sendMessage(msg);
receiverThread.start();
} catch (IOException e) {
Log.v("N", "Connection Failed : " + e.getMessage());
e.printStackTrace();
}
}
}.start();
}
public void close() {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
socket=null; //???
}
public BluetoothDevice getDevice() {
return device;
}
private class ReceiverThread extends Thread {
Handler handler;
ReceiverThread(Handler h) {
handler = h;
}
#Override public void run() {
while(true) {
try {
if(receiveStream.available() > 0) {
byte buffer[] = new byte[1000];
int k = receiveStream.read(buffer, 0, 1000);
if(k > 0) {
byte rawdata[] = new byte[k];
for(int i=0;i<k;i++)
rawdata[i] = buffer[i];
String data = new String(rawdata);
GlobalBuff= GlobalBuff+data;
Right_Buff= GlobalBuff.substring(GlobalBuff.length()-1,GlobalBuff.length());
if(Right_Buff.equals("\n")){
Message msg = handler.obtainMessage();
Bundle b = new Bundle();
b.putString("receivedData", GlobalBuff);
msg.setData(b);
handler.sendMessage(msg);
GlobalBuff="";
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
i try some extra code :
receiverThread.interrupt();
receiverThread=null;
if (receiveStream != null) {
try {receiveStream.close();} catch (Exception e) {}
receiveStream = null;
}
if (sendStream != null) {
try {sendStream.close();} catch (Exception e) {}
sendStream = null;
}
before closing but the result is the same , it crash.
The strange behavior is that it didn't crash immediately as it could happen with a type error or else ( i am talking of the behavior in debug mode...)
If anybody got an idea.
Googling this bring me to people with this issue but no solution that works for my case.
Thanks
UPDATE
what i found as a trace when it crash is that :
06-02 07:45:27.369 9025-9133/fr.icservice.sechage A/libc? Fatal signal 11 (SIGSEGV) at 0x00000008 (code=1), thread 9133 (Thread-1436)
I also made a test on a sony Z3 phone under 5.0.2 (compare to my T210 samsung galaxy tab3 under 4.4.2)and it not crash..!
maybe it's a ROM bug?! or a android version problem...
This is a known problem (or bug?) on Android. If you close the Bluetooth socket and then access it later on, some devices will crash with a segmentation fault (like yours). A common workaround is to check socket.isConnected() before or to synchronize the access to close(), write(), read(), ... by setting a flag like closeWasCalled = true and prevent any further calls to methods of this socket in your code after a close() call.
The problem comes with Socket Input/Output. I faced this problem when disconnecting with peer bluetooth device.
Reason :
From code, we are trying to read() , write() from socket object/connection which is closed.
Solution :
Add checking socket.isConnected() before above operations
You can read more about this problem on Stack Overflow question : Here
I am creating an application that will monitor movements in a particular Android device (client) and report such instances to another Android device (server). Also, under specific conditions, the client will take a picture and transmit the image to the server.
I am using WiFi direct to setup the connection between the two devices. After that I am using socket connections as explained in the WiFi Direct Demo. I am using port 8988 to send the motion sensor events and I am using port 8987 to send the images capture.
On the server side, I am using two different instances of the same Async Task with serversocket connecting to different ports to listen for the incoming messages. Everything works fine as long as only the motion sensor events are being sent across. The first image capture is also being sent/received correctly. However, after that the server doesn't receive any additional messages. I tried having two different Async Task classes to avoid having two instances of the same class but that didn't work as well. I also tried having one as an Async Task and another as an Intent Service but even that doesn't work.
This is IntentService I am using to send the messages across to the server.
public class MessageSender extends IntentService {
public static final String EXTRAS_TIMEOUT = "timeout";
public static final String EXTRAS_ADDRESS = "go_host";
public static final String EXTRAS_PORT = "go_port";
public static final String EXTRAS_DATA = "data";
private Handler handler;
public MessageSender(String name) {
super(name);
}
public MessageSender() {
super("MessageTransferService");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
handler = new Handler();
return super.onStartCommand(intent, flags, startId);
}
#Override
protected void onHandleIntent(Intent intent) {
String host = intent.getExtras().getString(EXTRAS_ADDRESS);
Socket socket = new Socket();
int port = intent.getExtras().getInt(EXTRAS_PORT);
byte[] data = intent.getExtras().getByteArray(EXTRAS_DATA);
int timeout = intent.getExtras().getInt(EXTRAS_TIMEOUT);
try {
socket.bind(null);
socket.connect((new InetSocketAddress(host, port)), timeout);
OutputStream stream = socket.getOutputStream();
stream.write(data);
} catch (final IOException e) {
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "Exception has occurred: " + e.getMessage(),
Toast.LENGTH_SHORT).show();
}
});
} finally {
if (socket != null) {
if (socket.isConnected()) {
try {
socket.close();
/*handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "Socket Connection closed now..",
Toast.LENGTH_SHORT).show();
}
});*/
} catch (IOException e) {
// Give up
e.printStackTrace();
}
}
}
}
}
}
This is Async Task on the server that starts listeners on two ports (8987 and 8988) to receiver the information of motion sensor events and images.
public class MessageReceiver extends AsyncTask<Void, Void, String> {
private Context context;
private int port;
private Bitmap mBitmap;
public MessageReceiver(Context context, int port) {
this.context = context;
this.port = port;
}
#Override
protected String doInBackground(Void... params) {
try {
ServerSocket serverSocket = new ServerSocket(port);
Socket client = serverSocket.accept();
InputStream inputstream = client.getInputStream();
String returnString = "";
if (port == MainActivity.PORT_SENSOR_COMM) {
// do something
} else if (port == MainActivity.PORT_IMAGE_COMM) {
//do something
}
serverSocket.close();
return returnString;
} catch (Exception e) {
return "Exception Occurred:" + e.getMessage();
}
}
#Override
protected void onPostExecute(String result) {
boolean startNewTask = true;
if (port == MainActivity.PORT_SENSOR_COMM) {
//do something
} else if (port == MainActivity.PORT_IMAGE_COMM) {
//do something
}
//doing this to start listening for new messages again
new MessageReceiver(context, port).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
#Override
protected void onPreExecute() {
}
}
I am now wondering whether Android WiFiDirect allows parallel communication between two devices on different ports. Searched the docs but could'nt find much help. What I am doing wrong? What is the correct method to accomplish what I am trying to do? Any help would be greatly appreciated. Thanks for looking.
I am trying to develop an android application that can exchange data on peer to peer connection with other devices without server. So please suggest how can I do this. Thank you in advance.
This is a complete code for chat by SocketProgramming without server.
In my Application, first you are a client and you search for a server. When you do not find any server, you become a server and wait for a client.
public class MainActivity extends ActionBarActivity {
private Handler handler = new Handler();
private TextView text;
private EditText input;
private Button send;
private Socket socket;
private DataOutputStream outputStream;
private BufferedReader inputStream;
private String DeviceName = "Device";
private boolean searchNetwork() {
log("Connecting");
String range = "192.168.56.";
for (int i = 1; i <= 255; i++) {
String ip = range + i;
try {
socket = new Socket();
socket.connect(new InetSocketAddress(ip, 9000), 50);
outputStream = new DataOutputStream(socket.getOutputStream());
inputStream = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
DeviceName += "1";
Log.i("Server", DeviceName);
log("Connected");
return true;
} catch (Exception e) {
}
}
return false;
}
private void runNewChatServer() {
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(9000);
log("Waiting for client...");
socket = serverSocket.accept();
DeviceName += "2";
log("a new client Connected");
} catch (IOException e) {
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.text);
input = (EditText) findViewById(R.id.input);
send = (Button) findViewById(R.id.send);
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
if (!searchNetwork()) {
runNewChatServer();
}
outputStream = new DataOutputStream(
socket.getOutputStream());
inputStream = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
while (true) {
String Message = inputStream.readLine();
if (Message != null) {
log(Message);
}
}
} catch (IOException e) {
log("Error: IO Exception");
e.printStackTrace();
}
}
});
send.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
if (outputStream == null) {
return;
}
try {
String Message = input.getText().toString() + "\n";
outputStream.write(Message.getBytes());
log2(input.getText().toString());
} catch (IOException e) {
e.printStackTrace();
}
input.setText("");
}
});
thread.start();
}
private void log(final String message) {
handler.post(new Runnable() {
String DeviceName2="";
#Override
public void run() {
if (DeviceName.equals("Device1")) {
DeviceName2 = "Device2";
}else if(DeviceName.equals("Device2")) {
DeviceName2 = "Device1";
}else{
DeviceName2 = "UnknowDevice";
}
text.setText(text.getText() + "\n" + DeviceName2 + " :"
+ message);
}
});
}
private void log2(final String message) {
handler.post(new Runnable() {
#Override
public void run() {
text.setText(text.getText() + "\n" + "you" + " :"
+ message);
}
});
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.exit(0);
return true;
}
return super.onKeyDown(keyCode, event);
}
}
Your design has a big problem : ...
If there is no central server some android devices should act as client and others as server but this will not work in some situations:
When the mobile telephony provider assigns private and non-public IP
When the device is connected to a Wi-Fi network but no NAT rule is defined on the router.
In both cases the problem is that the listening port of the device that must act as server is unreachable.
Java provides ServerSocket and Socket to communicate b/w devices. One of the device you can make as server and other device you can make as client and communicate b/w 'em without introducing server hosted on some machine.
The Other and better option is Using Wi-Fi Peer-to-Peer. WifiP2pManager help you to achieve your purpose.Here is an example.
If you're looking for such P2P over a local network, there are two parts to it:
Discovering peers
Communicating with peers
Among Android APIs, you can either use Network Service Discovery APIs for this or Wifi P2P Service Discovery APIs.
There's a wrapper library which which uses these internally and has comparatively better documentation - Salut, which can also be used.
I also created a library for P2P - Near, which uses sockets directly. The problem I was facing with Android APIs was that discovery wasn't happening with certainty every time and the underlying issue was unknown.
If you're looking for P2P across the internet, socket IO is a prevalent solution. Even Near should be able to facilitate the transfers if you provide the IP addresses and they're not behind NAT firewalls.
I'm playing a little bit with multicast sockets. I write a server which sends a message to a android client. Until yet the client should only log the received message.
I noticed that no multicast packet are received on my device.
Here is the code for the server (runs on the pc):
public class MulticastServer{
private int port;
private boolean running = false;
private MulticastSocket serverSocket;
private InetAddress group;
private String multicastAddress = "230.192.0.11";
public MulticastServer(int port) {
super();
this.port = port;
init();
}
public MusicStreamerServer() {
this(5500);
}
private void init() {
try {
group = InetAddress.getByName(multicastAddress);
serverSocket = new MulticastSocket(port);
serverSocket.joinGroup(group);
} catch (IOException e) {
e.printStackTrace();
}
}
public void start() throws IOException {
System.out.println("server started");
if (running)
return;
running = true;
new Thread(new Runnable() {
#Override
public void run() {
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length,
group, port);
String msg = "msg";
while (running) {
packet.setData(msg.getBytes(), 0, msg.length());
try {
serverSocket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
public void stop() throws IOException {
running = false;
} }
Here is the code for the android client:
public class MainActivity extends Activity {
private MulticastSocket socket;
private InetAddress group;
private String multicastAddress = "230.192.0.11";
private int port = 5500;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
.permitAll().build();
StrictMode.setThreadPolicy(policy);
try {
group = InetAddress.getByName(multicastAddress);
socket = new MulticastSocket(port);
socket.joinGroup(group);
socket.setBroadcast(true);
} catch (IOException e) {
e.printStackTrace();
Log.wtf("init", e.getMessage());
}
new Thread(new Runnable() {
#Override
public void run() {
WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiManager.MulticastLock multicastLock = wm
.createMulticastLock("mylock");
multicastLock.acquire();
byte[] buf = new byte[1024];
while (true) {
try {
socket.receive(packet);
Log.d("receiver","received = " + (new String(packet.getData())));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}}
I've tested the code with 2 different devices. The Nexus4 and the Nexus7 (2013) both running the latest Android.
Could anybody help me?
Thanks
I've seen that the issue is really inconsistent.
Android version: 4.2.x
On Samsung S4 active:
Multicast is working as expected.
On Samsung Note 10.1 and Nexus 4.2.3
Multicast is not working as expected.
239.x.x.x is not supported (and sadly it's the one used to multicast television...)
224.0.0.251 is working as expected.
I think they have a bug with the mask.
A multicast address is normally
|1 1 1 0| MULTICAST Address | 224.0.0.0 - 239.255.255.255
11100000.00000000.00000000.00000001 = 224.0.0.1
11101111.00000000.00000000.00000001 = 239.0.0.1
So the mask should be 224.0.0.0/4 and not 224.0.0.0/8
Does your manifest request the proper permissions?
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
Also, you may want to play with the Advanced settings in the WiFi menu on your phone, both Wi-Fi optimizations and Keep Wi-Fi on during sleep may impact your ability to do multicasts.
I write an Android socket demo which just post user's input to server.
I establish a socket from android client through wifi connection, and everything goes well, the server can receive the message send from android client. The problem is, then I close WIFI of phone, but the socket can write without exception.
The code of Android client:
public class MyActivity extends Activity {
private SocketHandlerThread thread;
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
thread = new SocketHandlerThread("ScoketTest");
thread.start();
}
class SocketHandlerThread extends HandlerThread {
private Socket socket;
private Handler handler;
public SocketHandlerThread(String name) {
super(name);
}
public SocketHandlerThread(String name, int priority) {
super(name, priority);
}
#Override
public void run() {
try {
socket = new Socket("192.168.60.184", 1990);
} catch (IOException e) {
Log.e("SocketTest", e.getLocalizedMessage(), e);
}
super.run();
}
Handler getHandler() {
if (handler == null) {
handler = new Handler(getLooper());
}
return handler;
}
void send(final String text) {
Runnable runnable = new Runnable() {
public void run() {
Log.e("SocketTest", "Start send text: " + text);
try {
socket.getOutputStream().write((text + "\n").getBytes());
socket.getOutputStream().flush();
} catch (Exception e) {
Log.e("SocketTest", e.getLocalizedMessage(), e);
}
Log.e("SocketTest", "Text has been send:" + text);
}
};
getHandler().post(runnable);
}
#Override
protected void onLooperPrepared() {
runOnUiThread(new Runnable() {
#Override
public void run() {
findViewById(R.id.button).setEnabled(true);
}
});
}
}
public void send(View view) {
String text = ((EditText) findViewById(R.id.text)).getText().toString();
thread.send(text);
}
}
The code of Server:
public class SocketTestServer {
ServerSocket serverSocket;
SocketTestServer() throws IOException {
serverSocket = new ServerSocket(1990);
}
void start() throws IOException {
Socket clientSocket = serverSocket.accept();
clientSocket.getInputStream();
PrintWriter out = new PrintWriter(System.out, true);
BufferedReader in =
new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
out.println(inputLine);
}
}
public static void main(String[] args) throws IOException {
SocketTestServer server = new SocketTestServer();
server.start();
}
}
I try on several phones. On Galaxy Nexus(4.2.1) an exception was thrown as expected, but on some MOTO or HTC phones socket can still write without any exception, which means I may loss some messages that I thought has been received successfully.
How could I get known that the socket connection was broken on any type of phone?
Any suggestion will be appreciated.
p.s
I know the Connective Change Broadcast, but before receive the broadcast the client may have write some message through the broken socket.
Though adding receive check on application protocol can solve the message lossing problem, I prefer to guarantee reliability on transport layer which the TCP protocol promise to do.
I use Writer & IOException in my socket, it works fine. Try this:
public static void sendUTF(String str)
{
try
{
outWriter.write(str + '\n');
outWriter.flush();
}
catch (IOException e)
{
e.printStackTrace();
outServ.setText("Connection lost!");
}
}
public static Writer outWriter = new OutputStreamWriter(outputStream, "UTF-8");
This is a problem with the TCP stack (OS level) and is a problem difficult to solve.
It happens also if the server is the one which lose connection (try to restart your server after opening the socket and no client will notice)... you will suffer a "broken pipe error" next time you send a packet through the socket.
So, there are two scenarios:
1- broken pipe when client sends information.
In this scenario you should capture the IOException and retry to open the connection (you will have to define your retry policies).
2- broken pipe when the server lose connectivity
When the server lose connection the client doesn't notice so, In this scenario you should send packets to the server in a regular basis (polling technique) and try to reconnect just in case the connection is lost.
This is needed just if you receive from the server updates, if you traffic is always client-->server only the scenario 1 applies.