Reputation: 2591
I'm trying to make a socket server on my desktop and connect to it from my android, however, android can't read from the socket unless the server shuts the connection down after sending data.
Desktop server code:
#define SERVER_BUFLEN 512
#define SERVER_PORT "27033"
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
int main () {
setvbuf(stdout, NULL, _IONBF, 0);
WSADATA wsaData;
int iResult;
struct addrinfo *result = NULL;
struct addrinfo hints;
int iSendResult;
char recvbuf[SERVER_BUFLEN];
int recvbuflen = SERVER_BUFLEN;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Resolve the server address and port
iResult = getaddrinfo(NULL, SERVER_PORT, &hints, &result);
if ( iResult != 0 ) {
printf("getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
return 1;
}
// Create a SOCKET for connecting to server
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// Setup the TCP listening socket
iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);
closesocket(ListenSocket);
ListenSocket = INVALID_SOCKET;
WSACleanup();
return 1;
}
freeaddrinfo(result);
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
printf("listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
ListenSocket = INVALID_SOCKET;
WSACleanup();
return 1;
}
printf ("waiting for new client\n");
// Accept a client socket
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed with error: %d\n", WSAGetLastError());
}
printf ("new client connected\n");
//int flag = 1;
//setsockopt(ClientSocket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
do {
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
if (iResult > 0) {
printf("Bytes received: %d\n", iResult);
iSendResult = send( ClientSocket, recvbuf, iResult, 0 );
if (iSendResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
}
printf("Bytes sent: %d\n", iSendResult);
//iResult = shutdown(ClientSocket, SD_SEND);
} else if (iResult == 0) {
printf("Connection closing...\n");
} else {
printf("recv failed with error: %d\n", WSAGetLastError());
}
} while (iResult > 0);
// shutdown the connection since we're done
iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %d\n", WSAGetLastError());
}
closesocket(ClientSocket);
printf ("client left\n");
if (ListenSocket != INVALID_SOCKET) {
closesocket(ListenSocket);
}
WSACleanup();
return 0;
}
Android client code:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate");
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
try {
Socket sock = new Socket();
//sock.setReceiveBufferSize(10);
sock.connect(new InetSocketAddress("192.168.0.101", 27033));
OutputStream os = sock.getOutputStream();
InputStream is = sock.getInputStream();
String msg = "Hello World!\r\n\0";
byte[] arr = msg.getBytes(Charset.forName("UTF-8"));
os.write(arr);
os.flush();
Log.d(TAG, "RECV MSG: " + is.read ());
sock.close();
} catch (Exception e) {
Log.d(TAG, e.getMessage());
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
}
The output from the server is:
waiting for new client
new client connected
Bytes received: 15
Bytes sent: 15
recv failed with error: 10060
client left
The output from the client is:
08-23 18:29:55.997 26472-26472/com.example.greg.systemremote D/MainActivity: onCreate
08-23 18:29:55.997 26472-26472/com.example.greg.systemremote D/libc-netbsd: [getaddrinfo]: hostname=192.168.0.101; servname=(null); netid=0; mark=0
08-23 18:29:55.997 26472-26472/com.example.greg.systemremote D/libc-netbsd: [getaddrinfo]: ai_addrlen=0; ai_canonname=(null); ai_flags=4; ai_family=0
08-23 18:33:54.254 26472-26472/com.example.greg.systemremote D/MainActivity: recvfrom failed: ETIMEDOUT (Connection timed out)
I tried disabling Nagle with
int flag = 1;
setsockopt(ClientSocket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
But it didn't change anything, so I tried inspecting the network traffic with Wireshark and as you can see from the output, the data is being sent to android: (192.168.0.183 is the android device)
However, if I uncomment the line
iResult = shutdown(ClientSocket, SD_SEND);
and thus close the connection after sending the data, it is received on the android side and the network traffic is as follows:
and the android output is
08-23 18:47:41.984 13164-13164/com.example.greg.systemremote D/MainActivity: onCreate
08-23 18:47:41.985 13164-13164/com.example.greg.systemremote D/libc-netbsd: [getaddrinfo]: hostname=192.168.0.101; servname=(null); netid=0; mark=0
08-23 18:47:41.985 13164-13164/com.example.greg.systemremote D/libc-netbsd: [getaddrinfo]: ai_addrlen=0; ai_canonname=(null); ai_flags=4; ai_family=0
08-23 18:47:42.318 13164-13164/com.example.greg.systemremote D/MainActivity: RECV MSG: 72
(which is nominal)
So, my question is, how can I send data back and fourth on this socket connection, without closing the connection? Also please note that I'm planing on sending binary data, and not text.
Upvotes: 0
Views: 1065
Reputation: 19158
The source of your error at C++ side is : do-while loop
.
do {
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
if (iResult > 0) {
printf("Bytes received: %d\n", iResult);
iSendResult = send( ClientSocket, recvbuf, iResult, 0 );
if (iSendResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
}
printf("Bytes sent: %d\n", iSendResult);
//iResult = shutdown(ClientSocket, SD_SEND);
} else if (iResult == 0) {
printf("Connection closing...\n");
} else {
printf("recv failed with error: %d\n", WSAGetLastError());
}
} while (iResult > 0);
For the first iteration, it gets inside the loop, receives data and sends data successfully to Android-device. Everything goes perfectly. And, then at Android side, you close the connection only after accepting one-response.
try {
Socket sock = new Socket();
//sock.setReceiveBufferSize(10);
sock.connect(new InetSocketAddress("192.168.0.101", 27033));
OutputStream os = sock.getOutputStream();
InputStream is = sock.getInputStream();
String msg = "Hello World!\r\n\0";
byte[] arr = msg.getBytes(Charset.forName("UTF-8"));
os.write(arr);
os.flush();
Log.d(TAG, "RECV MSG: " + is.read ());
sock.close();
} catch (Exception e) {
Log.d(TAG, e.getMessage());
}
Since C++ code is in a do-while loop, with iResult>0, so it will go inside the loop, and it will again try to receive data; but, unfortunately the connection has been closed at the Android side.
So, you need to check if the connection is open, then only you can proceed with receiving data. Currently, your coding logic stands poor to receive data.
It'll keep on throwing exception in the current state.
Also, from the error code Windows Sockets Error Code 10060 :
WSAETIMEDOUT 10060
Connection timed out.
A connection attempt failed because the connected party did not properly respond after a period of time, or the established connection failed because the connected host has failed to respond.
EDIT :
The source of error at Android side seems to be your creation of network-connection in the onCreate() method! This is discouraged everywhere(in Android).
You should use the onCreate()
method only to create and instantiate the objects that you will be using in your application. There should not be any blocking call, as in your case(sock.connect(IP,port)).
From Android's Developer Guide on Service :
A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use.
Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work(emphasis mine).
Also, from Processes and Threads :
Android might decide to shut down a process at some point, when memory is low and required by other processes that are more immediately serving the user.
When deciding which processes to kill, the Android system weighs their relative importance to the user. For example, it more readily shuts down a process hosting activities that are no longer visible on screen, compared to a process hosting visible activities. The decision whether to terminate a process, therefore, depends on the state of the components running in that process.
Also, there are simply two rules to Android's single thread model:
Do not block the UI thread
Do not access the Android UI toolkit from outside the UI thread.
Suggested Solution :
Create a separate service to achieve network-connection establishment(check local service sample). You can refer to the link to how to achieve your network connection through a service, not in the activity itself!
Upvotes: 1