【问题标题】:Socket - C server , Android clientSocket - C 服务器 , Android 客户端
【发布时间】:2014-02-19 19:46:04
【问题描述】:

您好,我已经制作了一个 C 服务器和一个用 java 制作的 android 客户端。 ,我试图让服务器发送字符串振动并让手机振动一段时间。我可以看到客户端已连接,但我认为服务器实际上根本没有发送任何数据,在我按 Enter 退出 getchar() 程序后,logcat 中会弹出此错误。

logcat 错误

01-28 14:10:50.400: W/System.err(30663): java.net.SocketException: recvfrom failed: ECONNRESET (Connection reset by peer)
01-28 14:10:50.400: W/System.err(30663):    at libcore.io.IoBridge.maybeThrowAfterRecvfrom(IoBridge.java:552)
01-28 14:10:50.400: W/System.err(30663):    at libcore.io.IoBridge.recvfrom(IoBridge.java:516)
01-28 14:10:50.400: W/System.err(30663):    at java.net.PlainSocketImpl.read(PlainSocketImpl.java:488)
01-28 14:10:50.400: W/System.err(30663):    at java.net.PlainSocketImpl.access$000(PlainSocketImpl.java:46)
01-28 14:10:50.400: W/System.err(30663):    at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:240)
01-28 14:10:50.400: W/System.err(30663):    at java.io.InputStreamReader.read(InputStreamReader.java:244)
01-28 14:10:50.410: W/System.err(30663):    at java.io.BufferedReader.fillBuf(BufferedReader.java:130)
01-28 14:10:50.410: W/System.err(30663):    at java.io.BufferedReader.readLine(BufferedReader.java:390)
01-28 14:10:50.410: W/System.err(30663):    at com.example.rat.RatService$ClientThread.run(RatService.java:76)
01-28 14:10:50.410: W/System.err(30663):    at java.lang.Thread.run(Thread.java:864)
01-28 14:10:50.410: W/System.err(30663): Caused by: libcore.io.ErrnoException: recvfrom failed: ECONNRESET (Connection reset by peer)
01-28 14:10:50.420: W/System.err(30663):    at libcore.io.Posix.recvfromBytes(Native Method)
01-28 14:10:50.420: W/System.err(30663):    at libcore.io.Posix.recvfrom(Posix.java:131)
01-28 14:10:50.420: W/System.err(30663):    at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:164)
01-28 14:10:50.420: W/System.err(30663):    at libcore.io.IoBridge.recvfrom(IoBridge.java:513)
01-28 14:10:50.420: W/System.err(30663):    ... 8 more

C 服务器代码。

int main(int argc, char *argv[])
{
  WSADATA wsa;
  SOCKET s, new_socket;
  struct sockaddr_in server, client;
  int c;
  char *message;

  if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
  {
    printf("ERROR initlizaing winsock2 dll : %d", WSAGetLastError());
    return 1; // exit program with error.
  }

  printf("INitialised.\n");

  // create a socket
  s = socket(AF_INET, SOCK_STREAM, 0);
  if (s == INVALID_SOCKET)
  {
    printf("error at creating socket :%d", WSAGetLastError());
    WSACleanup();
    return 1;
  }

  printf("socket created");

  //Sockad_addr in strucsture prepare
  memset(&server, 0, sizeof server);
  server.sin_family = AF_INET;
  server.sin_addr.s_addr = INADDR_ANY;
  server.sin_port = htons(5000);

  // bind the socket

  if (bind(s, (struct sockaddr *) &server, sizeof(server)) == SOCKET_ERROR)
  {
    printf("Bind fialed with errror code : %d", WSAGetLastError());
    closesocket(s);
    WSACleanup();
    return 1;
  }

  listen(s, 3);

  printf("waiting for incoming connections");
  // this part is what i dont understand.
  c = sizeof(struct sockaddr_in);
  new_socket = accept(s, (struct sockaddr *) &client, &c);
  if (new_socket == INVALID_SOCKET)
  {
    printf("accept fialed with error code : %d", WSAGetLastError());

    closesocket(s);
    WSACleanup();
    return 1;
  }

  printf("Connection acceptëd");

  //reply to CLIENTCREATESTRUCT
  message = "vibrate";
  send(new_socket, message, strlen(message), 0);
  getchar();
  closesocket(s);
  WSACleanup();
  return 0;
}

安卓java客户端,

public class Service extends Service{
    private boolean isRunning = false;
    private static final  String SERVER_IP = "192.168.178.11";
    private static final int SERVERPORT = 5000;
    private Socket socket;
    Thread ClientThread = null;
    String st = null;



    public void onCreate() {

    }


    public int onStartCommand(Intent intent, int flags, int startId){
        super.onStartCommand(intent, flags, startId);

        // Annoucment about starting serivce

        // Start a Thread Called MyThread
        isRunning = true;
        this.ClientThread = new Thread(new ClientThread());
        this.ClientThread.start();

        // Keep running until it explicitly stopped.
        // stopped so returns sicky
        return START_STICKY;
    }
    // code


    public void onDestroy(){
        super.onDestroy();
        // stop background Thread
        isRunning = false;


    }

    public class ClientThread implements Runnable {
        public void run() {


            try{
                InetAddress serverAddr = InetAddress.getByName(SERVER_IP);

                socket = new Socket(serverAddr,SERVERPORT);


                BufferedReader input =  new BufferedReader(new InputStreamReader(socket.getInputStream()));


                String st = input.readLine();

                if (st.equals("vibrate")){
                    Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
                    v.vibrate(3000);
                }
                st = null;

            } catch (Exception e) {
                e.printStackTrace();
            }
        } 
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }
}

Android 客户端清单

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.rat"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.Service.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".Service" />
    </application>

</manifest>

【问题讨论】:

    标签: java android c sockets


    【解决方案1】:

    你可以试试java.util.Scannerjava.io.BufferedReader 一样阅读,我的意思是:

    java.util.Scanner sc = new java.util.Scanner(socket.getInputStream(), "US-ASCII").useDelimiter("\\n");
    String st = sc.nextLine();
    if (st.equals("vibrate")){
        // ...
    }
    

    在服务器中你必须添加换行符:

    message = "vibrate\n";
    

    注意:如果您希望将分隔字符从“\n”更改为任何其他字符,只需在 useDelimiter 上指定并将其添加到服务器的消息中即可。

    注意:这是Scanner vs BufferedReader之间的比较。

    【讨论】:

    • 我真的不介意使用什么我只是想知道为什么它现在不工作,因为据我所知缓冲阅读器应该可以工作。
    • 所以,尝试使用new BufferedReader(new InputStreamReader(socket.getInputStream()), "US-ASCII"); 并在message = "vibrate\n" 处附加“\n”。
    • 构造函数BufferedReader(InputStreamReader, String)未定义
    • Ups,它在 InputStreamReader(..., "US-ASCII") 里面。 docs.oracle.com/javase/7/docs/api/java/io/…
    • BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream())); new BufferedReader(new InputStreamReader(socket.getInputStream(),"US-ASCII"));像这样 ?顺便说一句,我可以看到wireshark说发送了一个格式错误的数据包。
    【解决方案2】:

    BufferedReader.readLine() 阻塞,直到收到换行符。

    来自BufferedReader's documenation

    读取一行文本。一行被认为是由换行符 ('\n')、回车符 ('\r') 或紧跟换行符的回车符中的任何一个终止的。

    所以要解决这个问题,只需通过更改此行来让服务器发送一个换行符:

    message = "vibrate";
    

    变成:

    message = "vibrate\n";
    

    更新:

    客户端记录的错误信息清楚地表明服务器由于结束而关闭了连接。

    这意味着send() 已返回。知道send() 返回了哪个值会很有趣。

    所以我建议你修改这一行:

    send(new_socket, message, strlen(message), 0);
    

    变成:

    {
      int result = send(new_socket, message, strlen(message), 0);
    
      if (SOCKET_ERROR == result)
      {
        fprintf(stderr, "send() failed with error code: %d\n", WSAGetLastError());
      }
      else
      {
        fprintf(stderr, "Sent %d bytes out of %u.\n", result, strlen(message));
      }
    }
    

    【讨论】:

    • @user3244534:readLine() 会返回吗?如果是:它返回什么?请注意,换行符是作为从服务器读取的字符串的一部分返回的,因此 java 代码应使用 st.equals("vibrate\n") 进行测试
    • 对不起,你的意思是 readline() 返回?我刚开始开发,我将 java 更改为 st.equals("vibrate\n") 仍然可以工作:/ 这就是整个代码,除了 mainactivity。
    • @user3244534 使用调试器运行服务器代码,通过单步执行检查变量值的代码来查看实际发生的情况。对 java 代码做同样的事情。