Android——socket連線(客戶端發訊息給服務端)
阿新 • • 發佈:2018-11-09
準備工作
一臺手機作服務端,一臺手機作客戶端,兩部手機連線同一個wifi
服務端
佈局
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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="com.example.jie.socketdemo.MainActivity">
<TextView
android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf ="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
app:layout_constraintTop_toBottomOf="@+id/info"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.ConstraintLayout>
MainActivity
public class MainActivity extends AppCompatActivity {
private TextView mTvInfo;
private TextView mTvGetData;
private ServerSocket mServerSocket = null;
private InputStream mInputSteam;
private String ip;
private int port;
StringBuffer mStringBuffer = new StringBuffer();
public Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
//本地ip和port資訊
mTvInfo.setText(msg.obj.toString());
break;
case 2:
//從客戶端獲取到的訊息
mTvGetData.setText("CustomInfo:"+msg.obj.toString());
mStringBuffer.setLength(0);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTvInfo = findViewById(R.id.info);
mTvGetData = findViewById(R.id.data);
receiveData();
}
/**
* 伺服器端接收資料
* 需要注意以下一點:
* 伺服器端應該是多執行緒的,因為一個伺服器可能會有多個客戶端連線在伺服器上;
*/
public void receiveData() {
Thread thread = new Thread() {
@Override
public void run() {
super.run();
/*指明伺服器端的埠號*/
try {
mServerSocket = new ServerSocket(8000);
} catch (IOException e) {
e.printStackTrace();
}
getLocalIpAddress(mServerSocket);
Message msg = handler.obtainMessage();
msg.what = 1;
msg.obj = "IP:" + ip + " , PORT: " + port;
handler.sendMessage(msg);
//持續獲取訊息
while (true) {
Socket socket = null;
try {
if (mServerSocket != null) {
socket = mServerSocket.accept();
mInputSteam = socket.getInputStream();
}
} catch (IOException e) {
e.printStackTrace();
}
new ServerThread(socket, mInputSteam).start();
}
}
};
thread.start();
}
private void getLocalIpAddress(ServerSocket serverSocket) {
try {
//for(;;)
for (Enumeration<NetworkInterface> mEnumNetwork = NetworkInterface.getNetworkInterfaces(); mEnumNetwork.hasMoreElements();) {
NetworkInterface mNetwork = mEnumNetwork.nextElement();
for (Enumeration<InetAddress> mEnumIpAddress = mNetwork.getInetAddresses(); mEnumIpAddress.hasMoreElements();) {
InetAddress mIpAddress = mEnumIpAddress.nextElement();
String mIp = mIpAddress.getHostAddress().substring(0, 3);
if(mIp.equals("192")){
ip = mIpAddress.getHostAddress(); //獲取本地IP
port = serverSocket.getLocalPort(); //獲取本地的PORT
}
}
}
} catch (SocketException e) {
e.printStackTrace();
}
}
class ServerThread extends Thread {
private Socket socket;
private InputStream inputStream;
private StringBuffer stringBuffer = mStringBuffer;
public ServerThread(Socket socket, InputStream inputStream) {
this.socket = socket;
this.inputStream = inputStream;
}
@Override
public void run() {
int len;
byte[] bytes = new byte[24];
boolean isString = false;
try {
if (inputStream != null) {
//輸入流關閉時迴圈才會停止
//資料讀完了,再讀是等於0
while ((len = inputStream.read(bytes)) != -1) {
for (int i = 0; i < len; i++) {
if (bytes[i] != '\0') {
stringBuffer.append((char) bytes[i]);
} else {
isString = true;
break;
}
}
if (isString) {
Message msg = handler.obtainMessage();
msg.what = 2;
msg.obj = stringBuffer;
handler.sendMessage(msg);
isString = false;
}
}
}
} catch (IOException e) {
//當這個異常發生時,說明客戶端那邊的連線已經斷開
e.printStackTrace();
try {
inputStream.close();
socket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
/*當按返回鍵時,關閉相應的socket資源*/
@Override
public void onBackPressed() {
super.onBackPressed();
try {
mServerSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客戶端
佈局
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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="com.example.jie.socketcustomdemo.MainActivity">
<EditText
android:id="@+id/et_ip"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginLeft="40dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="connect"
android:onClick="connect"
app:layout_constraintTop_toTopOf="@id/et_ip"
app:layout_constraintLeft_toRightOf="@+id/et_ip" />
<EditText
android:id="@+id/et_data"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginLeft="40dp"
app:layout_constraintTop_toBottomOf="@id/et_ip"
app:layout_constraintLeft_toLeftOf="parent" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="send"
android:onClick="send"
app:layout_constraintTop_toTopOf="@id/et_data"
app:layout_constraintLeft_toRightOf="@+id/et_data" />
</android.support.constraint.ConstraintLayout>
MainActivity
public class MainActivity extends AppCompatActivity {
private EditText mEtIp,mEtData;
private OutputStream mOutputStream = null;
private Socket mSocket = null;
private String ip;
private String data;
private boolean socketStatus = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEtIp = findViewById(R.id.et_ip);
mEtData = findViewById(R.id.et_data);
}
public void connect(View view){
ip = mEtIp.getText().toString();
Toast.makeText(MainActivity.this,""+ip,Toast.LENGTH_SHORT).show();
Thread thread = new Thread(){
@Override
public void run() {
super.run();
if (!socketStatus) {
try {
//連線服務端
mSocket = new Socket(ip,8000);
if(mSocket != null){
socketStatus = true;
}
mOutputStream = mSocket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
thread.start();
}
public void send(View view){
data = mEtData.getText().toString();
Toast.makeText(MainActivity.this,""+data,Toast.LENGTH_SHORT).show();
if (data != null) {
//在後面加上 '\0' ,是為了在服務端方便我們去解析;
data = data + '\0';
}
Thread thread = new Thread(){
@Override
public void run() {
super.run();
if(socketStatus){
try {
mOutputStream.write(data.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
thread.start();
}
/*當客戶端介面返回時,關閉相應的socket資源*/
@Override
public void onBackPressed() {
super.onBackPressed();
/*關閉相應的資源*/
try {
mOutputStream.close();
mSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意
服務端和客戶端都要新增許可權
<!--允許應用程式改變網路狀態-->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<!--允許應用程式改變WIFI連線狀態-->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<!--允許應用程式訪問有關的網路資訊-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!--允許應用程式訪問WIFI網絡卡的網路資訊-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<!--允許應用程式完全使用網路-->
<uses-permission android:name="android.permission.INTERNET"/>
效果
客戶端
服務端