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.
Related
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 !
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.
Using socket connection i need to have two threads, one for reading and one for writing. I found other questions about socket connections but I don't understand how i can use the same socket in two different threads.
I have to create a socket in a different thread from the UI thread, so i need to start a thread to create the socket. Where can i start the two threads?
Sample code structure to give you an idea.
public class SocketActivity extends Activity {
Socket s;
OutputStream dout;
String ip = "127.0.0.1";
int port = 8080;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new Thread(new Runnable() {
#Override
public void run() {
try {
s = new Socket(ip, port);
new Thread(new ReaderRunnable(s));
new Thread(new WriteRunnable(s));
} catch (IOException e) {
e.printStackTrace();
//Handle error state
}
}
});
}
// You can put this class outside activity with public scope
class ReaderRunnable implements Runnable {
Socket socket;
public ReaderRunnable(Socket socket) {
this.socket = socket;
}
#Override
public void run() {
if (socket != null && socket.isConnected()) {
try {
OutputStream out = new BufferedOutputStream(socket.getOutputStream());
//Do reader code
} catch (IOException e) {
e.printStackTrace();
}
} else {
//Handle error case
}
}
}
// You can put this class outside activity with public scope
class WriteRunnable implements Runnable {
Socket socket;
public WriteRunnable(Socket socket) {
this.socket = socket;
}
#Override
public void run() {
if (socket != null && socket.isConnected()) {
try {
InputStream out = new BufferedInputStream(socket.getInputStream());
//Do writer code
} catch (IOException e) {
e.printStackTrace();
}
} else {
//Handle error case
}
}
}
}
Judging by your question this is client side. You don't have to use the socket itself in two different threads. For the read thread you use the InputStream of the socket, and for the write thread you use the OutputStream.
That way you don't have to create a seperate thread just for the socket. Both the read and write threads can be started from the UI thread. For creating the threads i refer you to the Android Documentation Processes and Threads.
im trying to implement a tcp socket connection between an android app (as server) and a java based client running on windows. (short version below, without code)
Im using some sensor listener to implement a game movement (everybody knows this sensor based movement of racing games.
Ive implemented a service for that purpose, which is started out of the first activity. This service is implemented as follows (im just pasting the relevant code snippets, not the whole class):
public class ServerService extends Service {
ConnectionHandler conHandler;
#Override
public void onCreate() {
startListener();
}
private void startListener() {
conHandler = new ConnectionHandler(this);
conHandler.execute();
}
private void sendMessage(String s)
{
conHandler.write(s);
}
public void messageNotify(String s) {
//Log.d("receivedMessage", s);
}
}
The ConnectionHandler class:
public class ConnectionHandler extends AsyncTask<Void, Void, Void>{
public static int serverport = 11111;
ServerSocket s;
Socket c;
ConnectionListening conListening;
ConnectionWriting conWriting;
DataOutputStream dos;
DataInputStream dis;
ServerService server;
public ConnectionHandler(ServerService server)
{
this.server = server;
}
#Override
protected Void doInBackground(Void... params) {
try {
Log.i("AsyncTank", "doInBackgoung: Creating Socket");
s = new ServerSocket(serverport);
} catch (Exception e) {
Log.i("AsyncTank", "doInBackgoung: Cannot create Socket");
}
try {
//this is blocking until client connects
c = s.accept();
Log.d("ConnectionHandler", "client connected");
dis = new DataInputStream(c.getInputStream());
dos = new DataOutputStream(c.getOutputStream());
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
conWriting = new ConnectionWriting(this.c, this.dos);
conWriting.execute();
conListening = new ConnectionListening(this.c, this.dis, this.server);
if(this.c != null)
{
Timer timer = new Timer();
timer.schedule(conListening, 0, 10);
}
Log.i("AsyncTank", "doInBackgoung: Socket created, Streams assigned");
return null;
}
public void write(String s)
{
conWriting.writeToStream(s);
}
public void messageNotify(String s) {
// TODO method stub
}
}
The ConnectionHandler ist implemented as AsyncTask similarly to the ConnectionWriting, so that the blocking of tcp methods doenst affect the whole communication.
The client is able to send messages to the server to. Because i dont know when this messages will arrive, im using a TimerTask which is executed every 10ms, to check if there is a new message.
ConnectionWriting looks as follows:
public class ConnectionWriting extends AsyncTask<Context, Void, Boolean>{
public DataOutputStream dos;
Socket c;
public ConnectionWriting(Socket c, DataOutputStream dos) {
this.dos = dos;
this.c = c;
}
#Override
protected Boolean doInBackground(Context... params) {
return true;
}
public void writeToStream(String s) {
try {
if (c != null){
//Log.i("AsynkTask", "writeToStream");
dos.writeBytes(s+"\n");
dos.flush();
Log.i("AsynkTask", "write: " +s);
} else {
Log.i("AsynkTask", "writeToStream : Cannot write to stream, Socket is closed");
}
} catch (Exception e) {
Log.i("AsynkTask", "writeToStream : Writing failed");
}
}
}
And the ConnectionListening class:
public class ConnectionListening extends TimerTask{
public DataInputStream dis;
Socket c;
ServerService server;
public ConnectionListening(Socket c, DataInputStream dis, ServerService server)
{
this.c = c;
this.dis = dis;
this.server = server;
}
#Override
public void run() {
String message = "";
try {
if (c != null) {
//Log.i("AsynkTask", "readFromStream : Reading message");
message = dis.readLine();
Log.i("AsynkTask", "read: " + message);
} else {
Log.i("AsynkTask", "readFromStream : Cannot Read, Socket is closed");
}
} catch (Exception e) {
Log.i("AsynkTask", "readFromStream : Writing failed");
}
if(message != null)
{
this.server.messageNotify(message);
}
}
}
I choose this complex, asynchronous way because the server is almost continuous sending data to the client and there are situations where the client has to send data back.
With the traditional way of using tcp sockets, it is not possible to realise a non blocking communication, so that means if the server is sending (writing), the read function blocks and i will never get the client message.
to keep it short:
Ive tested my approach but the server is always sending his data first and then getting the client messages. It is not asynchronous!? :-/
Maybe anybody can help me to solve this problem.
Or is there even a simpler way to implement that approach?
It is necessary that the communication is asynchronous! And the read has to be done automatically (what i tried to implement with this polling approach).
Ive read that i can use a single thread for the reading and one for the writing, but then i have a problem with using the write functionality (dont know how to call a function in a running thread) and with calling functions in my activities.
Im thankful for every help!
regards