【发布时间】:2019-06-24 22:51:26
【问题描述】:
我目前正在尝试从我的 Android 智能手机向树莓派发送消息。 我通过此处给出的示例代码实现了一个 Android 客户端:
我将 ip 地址更改为一个端口,以便它可以与我在 pi 上运行的服务器一起使用。该服务器是用 c 编写的,在切换到 android 之前,我实现了一个 C-Client,它到目前为止工作没有任何问题。
服务器只需要收到一条消息,然后打印并停止,所以很简单。
编辑 3
我为调试和分析创建了新的输出,我现在可能知道什么不起作用。据我所知,SendMessageTask 永远不会被执行。
服务器输出现在是:
成功 - 创建套接字
成功 - 绑定
聆听
成功 - 接受
现在它停止了,“阅读...”永远不会被打印出来! (应该发生在while循环中)
在 Android 中,我收到以下必要的输出:
D/TCP 客户端:C:连接中...
D/TcpOptimizer:TcpOptimizer-ON
--> 连接发生
当我按下发送按钮时:
D/myMessage1:按下按钮
D/myMessage:测试
但我没有收到应该在SendMessageTask 中记录的消息
当我单击断开连接按钮时,服务器会打印:
客户端断开
现在是 Android 日志:
来自服务器的 D/RESPONSE:S:收到的消息:'null'
D/sendMessageTask: 尝试发送消息
D/TcpClient: 无法发送 --> mBufferOut 为空
因此我的猜测是,SendMessageTask 永远不会运行,直到由于断开连接而失败,但为什么呢?
编辑 3 - 结束
这是(已编辑的)服务器代码:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
int main(){
int welcomeSocket, newSocket, read_size;
char buffer[10000];
struct sockaddr_in serverAddr;
struct sockaddr_storage serverStorage;
socklen_t addr_size;
/*---- Create the socket. The three arguments are: ----*/
/* 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) */
welcomeSocket = socket(AF_INET, SOCK_STREAM, 0);
if (welcomeSocket == -1) {
printf("Could not create socket\n");
}else {
printf("Success - create socket\n");
}
/*---- Configure settings of the server address struct ----*/
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(7891);
serverAddr.sin_addr.s_addr = INADDR_ANY;
/* Set all bits of the padding field to 0 */
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
/*---- Bind the address struct to the socket ----*/
if(bind(welcomeSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0){
printf("Error - Binding\n");
}else {
printf("Success - Binding\n");
}
/*---- Listen on the socket, with 5 max connection requests queued ----*/
if(listen(welcomeSocket,5)==0)
printf("Listening\n");
else
printf("Error\n");
/*---- Accept call creates a new socket for the incoming connection ----*/
addr_size = sizeof serverStorage;
newSocket = accept(welcomeSocket, (struct sockaddr *) &serverStorage, &addr_size);
if(newSocket < 0){
printf("Error - Accept\n");
}else {
printf("Success - Accept\n");
}
while( (read_size = recv(newSocket, buffer, 10000, 0)) > 0){
write(newSocket, buffer, strlen(buffer));
printf("reading...");
}
if(read_size == 0){
printf("Client disconnected");
fflush(stdout);
}
else if(read_size == -1){
printf("Error - recv failed");
}
return 0;
}
我不太确定里面的参数
welcomeSocket = socket(AF_INET, SOCK_STREAM, 0);
仍然正确吗?
Android 中的客户端代码是:
import android.util.Log;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
public class TcpClient {
public static final String TAG = TcpClient.class.getSimpleName();
public static final String SERVER_IP = "192.168.4.1"; //server IP address
public static final int SERVER_PORT = 7891;
// message to send to the server
private String mServerMessage;
// sends message received notifications
private OnMessageReceived mMessageListener = null;
// while this is true, the server will continue running
private boolean mRun = false;
// used to send messages
private PrintWriter mBufferOut;
// used to read messages from the server
private BufferedReader mBufferIn;
/**
* Constructor of the class. OnMessagedReceived listens for the messages received from server
*/
public TcpClient(OnMessageReceived listener) {
mMessageListener = listener;
}
/**
* Sends the message entered by client to the server
*
* @param message text entered by client
*/
public void sendMessage(final String message) {
Runnable runnable = new Runnable() {
@Override
public void run() {
if (mBufferOut != null) {
Log.d(TAG, "Sending: " + message);
mBufferOut.println(message);
mBufferOut.flush();
}else{
Log.d(TAG, "Cannot send --> mBufferOut is null");
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
/**
* Close the connection and release the members
*/
public void stopClient() {
mRun = false;
if (mBufferOut != null) {
mBufferOut.flush();
mBufferOut.close();
}
mMessageListener = null;
mBufferIn = null;
mBufferOut = null;
mServerMessage = null;
}
public void run() {
mRun = true;
try {
//here you must put your computer's IP address.
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
Log.d("TCP Client", "C: Connecting...");
//create a socket to make the connection with the server
Socket socket = new Socket(serverAddr, SERVER_PORT);
try {
//sends the message to the server
mBufferOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
//receives the message which the server sends back
mBufferIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//in this while the client listens for the messages sent by the server
while (mRun) {
/*
mServerMessage = mBufferIn.readLine();
if (mServerMessage != null && mMessageListener != null) {
//call the method messageReceived from MyActivity class
mMessageListener.messageReceived(mServerMessage);
}
*/
}
Log.d("RESPONSE FROM SERVER", "S: Received Message: '" + mServerMessage + "'");
} catch (Exception e) {
Log.e("TCP", "S: Error", e);
} finally {
//the socket must be closed. It is not possible to reconnect to this socket
// after it is closed, which means a new socket instance has to be created.
socket.close();
}
} catch (Exception e) {
Log.e("TCP", "C: Error", e);
}
}
//Declare the interface. The method messageReceived(String message) will must be implemented in the Activity
//class at on AsyncTask doInBackground
public interface OnMessageReceived {
public void messageReceived(String message);
}
}
我的(已编辑的)Mainactivity 包括用于连接的 AsyncTask 是
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Button;
public class MyActivity extends AppCompatActivity {
private TextView mTextMessage;
private TextView myAwesomeTextView;
private TcpClient mTcpClient;
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
mTextMessage.setText(R.string.title_home);
return true;
case R.id.navigation_dashboard:
mTextMessage.setText(R.string.title_dashboard);
return true;
case R.id.navigation_notifications:
mTextMessage.setText(R.string.title_notifications);
return true;
}
return false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_myActivity);
mTextMessage = (TextView) findViewById(R.id.message);
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
myAwesomeTextView = (TextView)findViewById(R.id.editText);
//TCP connect
new ConnectTask().execute("");
//Buttons
final Button button_Send = findViewById(R.id.button_Send);
button_Send.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//Set text (debugging)
myAwesomeTextView.setText("My Awesome Text");
Log.d("myMessage1", "button pushed ");
//sends the message to the server
String message = "testing\0";
//String message = "testing\n\r";
if (mTcpClient != null) {
new SendMessageTask().execute(message);
//not needed just for debugging
Log.d("myMessage", "testing ");
}
}
});
final Button button_Disconnect = findViewById(R.id.button_Disconnect);
button_Disconnect.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (mTcpClient != null) {
mTcpClient.stopClient();
}
}
});
}
@Override
protected void onPause() {
super.onPause();
if (mTcpClient != null) {
// disconnect
new DisconnectTask().execute();
}
}
/**
* Sends a message using a background task to avoid doing long/network operations on the UI thread
*/
public class SendMessageTask extends AsyncTask<String, Void, Void> {
@Override
protected Void doInBackground(String... params) {
// send the message
mTcpClient.sendMessage(params[0]);
Log.d("sendMessageTask", "try to send message ");
return null;
}
@Override
protected void onPostExecute(Void nothing) {
super.onPostExecute(nothing);
//nothing to do yet
}
}
/**
* Disconnects using a background task to avoid doing long/network operations on the UI thread
*/
public class DisconnectTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
// disconnect
mTcpClient.stopClient();
mTcpClient = null;
return null;
}
@Override
protected void onPostExecute(Void nothing) {
super.onPostExecute(nothing);
//nothing to do yet
}
}
public class ConnectTask extends AsyncTask<String, String, TcpClient> {
@Override
protected TcpClient doInBackground(String... message) {
//we create a TCPClient object and
mTcpClient = new TcpClient(new TcpClient.OnMessageReceived() {
@Override
//here the messageReceived method is implemented
public void messageReceived(String message) {
//this method calls the onProgressUpdate
publishProgress(message);
}
});
mTcpClient.run();
return null;
}
@Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
Log.d("test", "response " + values[0]);
//process server response here....
}
}
}
EDIT #2(失败的行为仍然存在)
-
在 TcpClient 的运行方法中取消注释的 while(mRun) 循环 --> 认为套接字可能在消息发送之前被 finally 关闭。
-
将初始连接移到 onCreate() 中,所以按钮只会 处理发送并建立之前的连接。
-
删除发送消息后关闭套接字
【问题讨论】:
-
您忘记检查大多数服务器程序中的错误,包括检查“连接关闭”(我相信这里发生了什么)。而且您不会终止您阅读的“字符串”。
-
感谢您的回复,我应该在我的代码中的哪个位置检查连接是否关闭?像打印消息后一样?终止字符串意味着“\n”?抱歉,我对这些东西很陌生。
-
在 C 中,
char字符串实际上称为 null-terminated 字节字符串。您需要将该 null-terminator 添加到您读取的字符串中。至于另一个问题,请阅读 C 源代码中所有函数的手册页。还有一些网络编程教程。他们都应该告诉你你需要知道的一切。 -
好的,谢谢你,我会在完成后提供答案或进一步的问题。
-
我想我应该应用您提到的所有更改,但输出仍然没有发送的文本消息。我还提供了 android 给我的输出,我看不出这里有什么问题吗?服务器只打印一条空消息并关闭,就像他收到一个空字符串一样。我不太确定“\0”在“Java”中的含义是否与在 c 中的含义相同?但我的猜测是,c-server 读取它以便它可以工作?
标签: android c tcp client-server