Server
MainActivity.java
// MainActivity.java
package com.example.server;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";
private static final String str = "com.example.server.SERVER_SERVICE";
private Button start, stop;
public TextView displayText;
private Intent intent = null;
MyServiceConn myServiceConn;
ServerService.MyBinder binder = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate()");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start = (Button) findViewById(R.id.startBtn);
stop = (Button) findViewById(R.id.stopBtn);
displayText = (TextView) findViewById(R.id.displayText);
myServiceConn = new MyServiceConn();
intent = new Intent(this, ServerService.class);
intent.setPackage(this.getPackageName());
start.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
startService(intent);
bindService(intent, myServiceConn, Context.BIND_AUTO_CREATE);
stop.setEnabled(true);
start.setEnabled(false);
}
});
stop.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
if (binder != null) unbindService(myServiceConn);
stopService(intent);
start.setEnabled(true);
stop.setEnabled(false);
}
});
}
class MyServiceConn implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
binder = (ServerService.MyBinder) service;
binder.getService().setDataCallback(new ServerService.DataCallback() {
@Override
public void dataChanged(String str) {
Message msg = new Message();
Bundle bundle = new Bundle();
bundle.putString("str", str);
msg.setData(bundle);
handler.sendMessage(msg);
}
});
}
@SuppressLint("HandlerLeak")
Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
Log.i(TAG, "returned msg = " + msg.getData().getString("str"));
displayText.setText(msg.getData().getString("str"));
}
};
@Override
public void onServiceDisconnected(ComponentName name) {
binder = null;
}
}
}
ServerService.java
// ServerService.java
package com.example.server;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerService extends Service {
final static String TAG = "ServerService";
public static Boolean mainThreadFlag;
public static Boolean ioThreadFlag;
ServerSocket serverSocket = null;
final int SERVER_PORT = 8080;
public static String data = null;
@Override
public void onCreate() {
Log.i(TAG, "onCreate()");
super.onCreate();
mainThreadFlag = true;
ioThreadFlag = true;
new Thread() {
public void run() {
doListen();
}
}.start();
}
private void doListen() {
Log.i(TAG, "doListen()");
serverSocket = null;
try {
serverSocket = new ServerSocket(SERVER_PORT);
while (mainThreadFlag) {
Log.i(TAG, "inside while loop");
Socket client = serverSocket.accept();
new Thread(new handlerWithClient(client)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand()");
//data = intent.getStringExtra("data");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy()");
mainThreadFlag = false;
ioThreadFlag = false;
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind()");
return new MyBinder();
}
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind");
return super.onUnbind(intent);
}
public class MyBinder extends Binder {
ServerService getService() {
return ServerService.this;
}
public void setData(String data) {
Log.i(TAG, "setData()");
ServerService.this.data = data;
}
}
public static DataCallback dataCallback = null;
public DataCallback getDataCallback() {
Log.i(TAG, "getDataCallback()");
return dataCallback;
}
public void setDataCallback(DataCallback dataCallback) {
Log.i(TAG, "setDataCallback()");
this.dataCallback = dataCallback;
}
public interface DataCallback {
void dataChanged(String str);
}
}
handlerWithClient.java
// handlerWithClient.java
package com.example.server;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import static com.example.server.ServerService.data;
import static com.example.server.ServerService.dataCallback;
public class handlerWithClient implements Runnable {
public final static String TAG = "handlerWithClient";
private Socket client;
final static int MAX_BYTES = 1024 * 4;
public handlerWithClient(Socket client) {
this.client = client;
}
@Override
public void run() {
BufferedInputStream in;
try {
in = new BufferedInputStream(client.getInputStream());
ServerService.ioThreadFlag = true;
while (ServerService.ioThreadFlag) {
if (!client.isConnected()) {
break;
}
data = readFromSocket(in);
Log.i(TAG, "ServerService.data=" + data);
if (dataCallback != null && data != null) {
dataCallback.dataChanged(data);
}
}
in.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (client != null) {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private String readFromSocket(InputStream in) {
Log.i(TAG, "readFromSocket()");
String msg = null;
byte[] buffer = new byte[MAX_BYTES];
try {
int readBytes = 0;
if ((readBytes = in.read(buffer, 0, MAX_BYTES)) >= 0) {
Log.i(TAG, "read bytes = " + readBytes);
msg = new String(buffer, 0, readBytes, "utf-8");
Log.i(TAG, "msg=" + msg);
buffer = null;
}
} catch (IOException e) {
e.printStackTrace();
}
return msg;
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.server">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<service
android:name=".ServerService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.server.SERVER_SERVICE"/>
</intent-filter>
</service>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:layout_editor_absoluteX="1dp"
tools:layout_editor_absoluteY="1dp"
tools:ignore="MissingConstraints">
<Button
android:id="@+id/startBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/start_service"
android:textAllCaps="false"
/>
<Button
android:id="@+id/stopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/stop_service"
android:textAllCaps="false"
android:enabled="false"
/>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tip_message"
android:textSize="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="MissingConstraints"
tools:layout_editor_absoluteY="49dp" />
<TextView
android:id="@+id/displayText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/display_received"
android:background="@drawable/back"
android:textAllCaps="false"
tools:ignore="MissingConstraints" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
运行如下:
Client
MainActivity.java
package com.example.client;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class MainActivity extends AppCompatActivity {
final static String TAG = "MainActivity";
private Button btn1, btn2;
private EditText et1, et2;
private static final int SERVER_PORT = 8080;
private Socket server = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG, "onCreate()");
init();
}
private void init() {
Log.i(TAG, "init()");
et1 = (EditText) findViewById(R.id.connEdit);
btn1 = (Button) findViewById(R.id.connectButton);
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String s = et1.getText().toString();
new Thread() {
public void run() {
try {
server = connectToServer(s);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
});
et2 = (EditText) findViewById(R.id.editText);
btn2 = (Button) findViewById(R.id.sendButton);
btn2.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
final String s = et2.getText().toString();
new Thread() {
public void run() {
try {
sendMessage(server, s);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
});
}
private Socket connectToServer(String ip) throws IOException {
Log.i(TAG, "connectToServer()");
Socket server = null;
try {
server = new Socket(ip, SERVER_PORT);
} catch (UnknownHostException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), "Connection Fail!",
Toast.LENGTH_LONG).show();
}
if (server != null) {
et1.setText("Connected to " + ip);
}
return server;
}
private void sendMessage(Socket server, String s) throws IOException {
Log.i(TAG, "sendMessage()");
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(server.getOutputStream()));
writer.write(s);
writer.flush();
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.client">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/connEdit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/ip_address"
android:textAllCaps="false" />
<Button
android:id="@+id/connectButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/connect_server"
android:textAllCaps="false" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/linearLayout1"
android:orientation="horizontal">
<EditText
android:id="@+id/editText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="textLongMessage"
android:text="@string/input_message" />
<Button
android:id="@+id/sendButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/send_button"
android:textAllCaps="false" />
</LinearLayout>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
运行如下:
注意事项
实践过程中的坑
- android.os.NetworkOnMainThreadException
- java.net.SocketException: socket failed: EPERM (Operation not permitted)
关于网络编程的提示
Android官方说明:Connect to the network