【Android Socket專題】: TCP通訊伺服器端app的demo的實現
----這篇實現以下TCP 伺服器,另外由於實在不是想在重複工作了,所以關於tcp多連線伺服器程式碼直接提供思路放在這邊了,單一使用請自行刪除和移位相關程式碼即可,原理是一樣的,實現方法也有很多種,個人這裡只實現其中一種。
首先規劃一下頁面:
附上對應XML程式碼:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:weightSum="5" > <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:orientation="vertical" android:layout_weight="1"> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="本機ip:"/> <TextView android:layout_width="0dp" android:layout_weight="2" android:layout_height="match_parent" android:gravity="center" android:id="@+id/txt_Server_Ip"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="port"/> <EditText android:id="@+id/edit_Server_Port" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:hint="1234" android:inputType="number" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:orientation="horizontal" android:layout_weight="1"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="ID(英文或數字):"/> <EditText android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:id="@+id/edit_Server_ID"/> <Button android:layout_width="wrap_content" android:layout_height="match_parent" android:text="隨機生成" android:id="@+id/btn_tcpServerRandomID"/> </LinearLayout> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="開啟" android:id="@+id/btn_tcpServerConn"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="關閉" android:id="@+id/btn_tcpServerClose"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="清除接收區" android:id="@+id/btn_tcpCleanServerRecv"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="清除傳送區" android:id="@+id/btn_tcpCleanServerSend"/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:orientation="vertical" android:layout_weight="2"> <TextView android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:gravity="bottom" android:text="接收區:" /> <TextView android:layout_width="match_parent" android:layout_weight="5" android:layout_height="0dp" android:id="@+id/txt_ServerRcv" android:background="@android:color/holo_blue_light" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:orientation="vertical" android:layout_weight="2"> <TextView android:layout_width="wrap_content" android:layout_height="0dp" android:layout_weight="1" android:text="傳送區:" /> <TextView android:id="@+id/txt_ServerSend" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="7" android:background="@android:color/holo_purple" /> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:orientation="horizontal" android:layout_weight="2"> <EditText android:layout_width="0dp" android:layout_weight="5" android:id="@+id/edit_Send" android:layout_height="match_parent" /> <Button android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" android:layout_gravity="right" android:id="@+id/btn_tcpServerSend" android:text="傳送"/> </LinearLayout> </LinearLayout> </LinearLayout>
介面規劃完成,其中關於隨機ID這塊我沒有寫程式碼,有空再補了;
實現TCP 伺服器端的關鍵類在於以下幾個類:
1、ServerSocket;
2、Socket;
3、InputSteam;
4、OutputSteam;
5、PrintWriter;
不懂得希望自己搜尋查一下,我這就不費話了;
TCP關於伺服器建立的流程設計如下:
1、初始化ServerSocket的埠,阻塞監聽;
2、監聽到連線後,開闢執行緒,建立對應通訊socket流執行緒,每一個監聽到一個物件對應一個新的執行緒;
3、socket執行緒內部阻塞監聽接收資料,收到後傳送給主介面顯示;
4、結束迴圈,對應關閉socket物件;
5、結束監聽,關閉ServerSocket物件(同時要注意關閉還在執行的Socket執行緒及物件);
關於此部分有一些提醒需要說明:
1、serverSocket最好設定超時,因為最終結束時候,還會被阻塞一次,當然也可以利用api強制結束監聽異常;
2、Socket最好設定超時,理由同上;
3、關閉ServerSocket時,記得關閉所有的Socket物件,它們還在執行中;
TCP關於伺服器傳送訊息流程:
1、呼叫相對應的Socket執行緒中的Send方法即可,利用PrintWriter實現;
具體實現見程式碼:
package jason.tcpdemo.coms;
import android.content.Intent;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import jason.tcpdemo.funcs.FuncTcpServer;
/**
* Created by Jason Zhu on 2017-04-24.
* Email: [email protected]
*/
public class TcpServer implements Runnable{
private String TAG = "TcpServer";
private int port = 1234;
private boolean isListen = true; //執行緒監聽標誌位
public ArrayList<ServerSocketThread> SST = new ArrayList<ServerSocketThread>();
public TcpServer(int port){
this.port = port;
}
//更改監聽標誌位
public void setIsListen(boolean b){
isListen = b;
}
public void closeSelf(){
isListen = false;
for (ServerSocketThread s : SST){
s.isRun = false;
}
SST.clear();
}
private Socket getSocket(ServerSocket serverSocket){
try {
return serverSocket.accept();
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG, "run: 監聽超時");
return null;
}
}
@Override
public void run() {
try {
ServerSocket serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(5000);
while (isListen){
Log.i(TAG, "run: 開始監聽...");
Socket socket = getSocket(serverSocket);
if (socket != null){
new ServerSocketThread(socket);
}
}
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public class ServerSocketThread extends Thread{
Socket socket = null;
private PrintWriter pw;
private InputStream is = null;
private OutputStream os = null;
private String ip = null;
private boolean isRun = true;
ServerSocketThread(Socket socket){
this.socket = socket;
ip = socket.getInetAddress().toString();
Log.i(TAG, "ServerSocketThread:檢測到新的客戶端聯入,ip:" + ip);
try {
socket.setSoTimeout(5000);
os = socket.getOutputStream();
is = socket.getInputStream();
pw = new PrintWriter(os,true);
start();
} catch (IOException e) {
e.printStackTrace();
}
}
public void send(String msg){
pw.println(msg);
pw.flush(); //強制送出資料
}
@Override
public void run() {
byte buff[] = new byte[4096];
String rcvMsg;
int rcvLen;
SST.add(this);
while (isRun && !socket.isClosed() && !socket.isInputShutdown()){
try {
if ((rcvLen = is.read(buff)) != -1 ){
rcvMsg = new String(buff,0,rcvLen);
Log.i(TAG, "run:收到訊息: " + rcvMsg);
Intent intent =new Intent();
intent.setAction("tcpServerReceiver");
intent.putExtra("tcpServerReceiver",rcvMsg);
FuncTcpServer.context.sendBroadcast(intent);//將訊息傳送給主介面
if (rcvMsg.equals("QuitServer")){
isRun = false;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
try {
socket.close();
SST.clear();
Log.i(TAG, "run: 斷開連線");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
核心程式碼完成。
主介面程式碼(幾個類和UDP介紹介面程式碼時是一樣的):
package jason.tcpdemo.funcs;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import jason.tcpdemo.R;
import jason.tcpdemo.coms.TcpServer;
/**
* Created by Jason Zhu on 2017-04-24.
* Email: [email protected]
*/
public class FuncTcpServer extends Activity {
private Button btnStartServer,btnCloseServer, btnCleanServerSend, btnCleanServerRcv,btnServerSend,btnServerRandom;
private TextView txtRcv,txtSend,txtServerIp;
private EditText editServerSend,editServerRandom, editServerPort;
private static TcpServer tcpServer = null;
private MyBtnClicker myBtnClicker = new MyBtnClicker();
private final MyHandler myHandler = new MyHandler(this);
private MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
@SuppressLint("StaticFieldLeak")
public static Context context;
ExecutorService exec = Executors.newCachedThreadPool();
private class MyHandler extends android.os.Handler{
private final WeakReference<FuncTcpServer> mActivity;
MyHandler(FuncTcpServer activity){
mActivity = new WeakReference<FuncTcpServer>(activity);
}
@Override
public void handleMessage(Message msg) {
FuncTcpServer activity = mActivity.get();
if (activity!= null){
switch (msg.what){
case 1:
txtRcv.append(msg.obj.toString());
break;
case 2:
txtSend.append(msg.obj.toString());
break;
}
}
}
}
private class MyBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String mAction = intent.getAction();
switch (mAction){
case "tcpServerReceiver":
String msg = intent.getStringExtra("tcpServerReceiver");
Message message = Message.obtain();
message.what = 1;
message.obj = msg;
myHandler.sendMessage(message);
break;
}
}
}
private void bindReceiver(){
IntentFilter intentFilter = new IntentFilter("tcpServerReceiver");
registerReceiver(myBroadcastReceiver,intentFilter);
}
private class MyBtnClicker implements View.OnClickListener{
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn_tcpServerConn:
Log.i("A", "onClick: 開始");
btnStartServer.setEnabled(false);
btnCloseServer.setEnabled(true);
btnServerSend.setEnabled(true);
tcpServer = new TcpServer(getHost(editServerPort.getText().toString()));
exec.execute(tcpServer);
break;
case R.id.btn_tcpServerClose:
tcpServer.closeSelf();
btnStartServer.setEnabled(true);
btnCloseServer.setEnabled(false);
btnServerSend.setEnabled(false);
break;
case R.id.btn_tcpCleanServerRecv:
txtRcv.setText("");
break;
case R.id.btn_tcpCleanServerSend:
txtSend.setText("");
break;
case R.id.btn_tcpServerRandomID:
break;
case R.id.btn_tcpServerSend:
Message message = Message.obtain();
message.what = 2;
message.obj = editServerSend.getText().toString();
myHandler.sendMessage(message);
exec.execute(new Runnable() {
@Override
public void run() {
tcpServer.SST.get(0).send(editServerSend.getText().toString());
}
});
break;
}
}
}
private int getHost(String msg){
if (msg.equals("")){
msg = "1234";
}
return Integer.parseInt(msg);
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tcp_server);
context = this;
bindID();
bindListener();
bindReceiver();
ini();
}
private void ini(){
btnCloseServer.setEnabled(false);
btnServerSend.setEnabled(false);
txtServerIp.setText(getHostIP());
}
private void bindListener() {
btnStartServer.setOnClickListener(myBtnClicker);
btnCloseServer.setOnClickListener(myBtnClicker);
btnCleanServerRcv.setOnClickListener(myBtnClicker);
btnCleanServerSend.setOnClickListener(myBtnClicker);
btnServerRandom.setOnClickListener(myBtnClicker);
btnServerSend.setOnClickListener(myBtnClicker);
}
private void bindID() {
btnStartServer = (Button) findViewById(R.id.btn_tcpServerConn);
btnCloseServer = (Button) findViewById(R.id.btn_tcpServerClose);
btnCleanServerRcv = (Button) findViewById(R.id.btn_tcpCleanServerRecv);
btnCleanServerSend = (Button) findViewById(R.id.btn_tcpCleanServerSend);
btnServerRandom = (Button) findViewById(R.id.btn_tcpServerRandomID);
btnServerSend = (Button) findViewById(R.id.btn_tcpServerSend);
txtRcv = (TextView) findViewById(R.id.txt_ServerRcv);
txtSend = (TextView) findViewById(R.id.txt_ServerSend);
txtServerIp = (TextView) findViewById(R.id.txt_Server_Ip);
editServerRandom = (EditText) findViewById(R.id.edit_Server_ID);
editServerSend = (EditText) findViewById(R.id.edit_tcpClientSend);
editServerPort = (EditText)findViewById(R.id.edit_Server_Port);
}
/**
* 獲取ip地址
* @return
*/
public String getHostIP() {
String hostIp = null;
try {
Enumeration nis = NetworkInterface.getNetworkInterfaces();
InetAddress ia = null;
while (nis.hasMoreElements()) {
NetworkInterface ni = (NetworkInterface) nis.nextElement();
Enumeration<InetAddress> ias = ni.getInetAddresses();
while (ias.hasMoreElements()) {
ia = ias.nextElement();
if (ia instanceof Inet6Address) {
continue;// skip ipv6
}
String ip = ia.getHostAddress();
if (!"127.0.0.1".equals(ip)) {
hostIp = ia.getHostAddress();
break;
}
}
}
} catch (SocketException e) {
Log.i("FuncTcpServer", "SocketException");
e.printStackTrace();
}
return hostIp;
}
}
再次強調,許可權:
<uses-permission android:name="android.permission.INTERNET"/>
結束,看效果圖:
和寫的TCP Client通訊效果如下:
開發環境:Android Studio
demo包地址:
相關推薦
【Android Socket專題】: TCP通訊伺服器端app的demo的實現
----這篇實現以下TCP 伺服器,另外由於實在不是想在重複工作了,所以關於tcp多連線伺服器程式碼直接提供思路放在這邊了,單一使用請自行刪除和移位相關程式碼即可,原理是一樣的,實現方法也有很多種,個人這裡只實現其中一種。 首先規劃一下頁面: 附上
【Android Socket專題】: TCP通訊客戶端app的demo的實現
------- Android TCP 客戶端實現,規劃介面如下: 對應XML程式碼如下: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" a
【Android Socket專題】:UDP通訊客戶端app的demo的實現
Android Socket 專題: 關於UDP通訊其實可以不用多做累述,多數像我一樣的朋友在此基礎上也只是為了應用,需要了解下該瞭解的就可以了,具體的想要對這個協議研究深入的,可以自己努力!我這兒只做Android客戶端的應用實現,注意是客戶端,不是伺服器,
【Wireshark抓包】:TCP的狀態型別
承接上篇:https://blog.csdn.net/hxcaifly/article/details/85339299 1. 前言 在分析wireshark抓包時,封包詳情資訊裡展現出了TCP的狀態型別。除了我們比較熟悉的SYN, ACK的三次握手時見到的狀態之外。突
【Android---專案中】複製到剪貼簿功能的實現
專案中需要複製銀行卡賬號到剪貼簿,於是需要用到此功能。 button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Clipbo
【Android 進階】CSDN新聞類客戶端-練手專案
無圖無真相: 宣告: 本專案資料全部來自CSDN官網,純練手之作,個人未從中獲取任何利益,資料的獲取與共享可能會侵犯到CSDN的權益,若被告知需停止共享與使用,本人會立即刪除整個專案。
Android 進階12:程序通訊之 Socket (順便回顧 TCP UDP)
不要害怕困難,這是你進步的機會! 讀完本文你將瞭解: 前面幾篇文章我們介紹了 AIDL 、Binder、Messenger 以及 ContentProvider 實現程序通訊的方式,這篇文章將介紹“使用 Socket 進行跨程序通訊”。 在介紹
【Android應用開發技術:基礎構建】命令行下的Android應用開發
star andro all class文件 abs 2.3 pil data 資源 作者:郭孝星 微博:郭孝星的新浪微博 郵箱:[email protected]/* */ 博客:http://blog.csdn.net/allenw
【背包專題】C - The trouble of Xiaoqian hdu3591【混合背包:多重背包+完全背包】
back 無法 name spa int receive with out man In the country of ALPC , Xiaoqian is a very famous mathematician. She is immersed in calculate,
【Java TCP/IP Socket程式設計】----NIO----TCP通道
NIO介紹 基本Java套接字對於小規模系統可以很好執行,涉及同時有上千個客戶端,就會出現問題,其中一客戶一執行緒的方式線上程的建立,維護和切換需要系統開銷較大,而使用執行緒池的方式雖然節省了一定的系統開銷,但是對於連線生存期比較長的協議,執行緒池的大小限制了系統可以同時服務的客戶端總數。隨著執
【android學習筆記】activity間的通訊案例之高德地圖實現天氣查詢
【概述】app實現天氣查詢是再正常不過的功能了,又因為往往不止一個activity去獲取資料,那就想到封裝一個類,需要時去呼叫獲取即可。 【注】因為看文件還有點懵,故將自己抓腦寫的程式碼記錄下,以便查詢 【思路】activity傳送請求--獲取地址--根據地址獲取天氣
Python學習【第20篇】:互斥鎖以及程序之間的三種通訊方式(IPC)以及生產者個消費者模型 python併發程式設計之多程序1-----------互斥鎖與程序間的通訊
python併發程式設計之多程序1-----------互斥鎖與程序間的通訊 一、互斥鎖 程序之間資料隔離,但是共享一套檔案系統,因而可以通過檔案來實現程序直接的通訊,
網路程式設計基礎【林老師版】:簡單的 套接字通訊(一)
一、服務端程式碼 import socket #1、買手機 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # print(phone) #2、繫結手機卡 phone.bind(('127.0.0.1',8081)) #0-
Linux網路程式設計【三】:TCP伺服器多程序和多執行緒(http訪問)版本
為了讓伺服器同時接受多個客戶端訪問,所以需要多程序或者多執行緒 多程序版本: #include<unistd.h> #include<stdio.h> #include<stdlib.h> #include<sys/types.h
【Android 進階】Android自定義系列:邊緣凹凸的卡劵效果
所謂前人栽樹,後人乘涼,在此感謝博主的貢獻。 原文:邊緣凹凸的卡劵效果 先上效果圖: 我實現的效果和原博主實現的效果是不一樣的,我是左右邊緣凹凸,而博主是上下邊緣凹凸。其實理解了原理,哪個邊緣實現這個效果都是可以的。 實現原理: 直接
【架構拾集】: Android 移動應用架構設計
在這一個多月裡,我工作在一個採用外掛化的原生 Android 應用專案上。隨著新技術的引入,及編
【Android 併發程式設計】執行緒間通訊的三種基本方式,android執行緒
1. 使用管道流Pipes “管道”是java.io包的一部分。它是Java的特性,而不是Android特有的。一條“管道”為兩個執行緒建立一個單向的通道。生產者負責寫資料,消費者負責讀取資料。 下面是一個使用管道流進行通訊的例子。 public class Pip
【Android 進階】淘寶頭條:向上滾動廣告條ViewFlipper
所謂前人栽樹,後人乘涼,在此感謝博主的貢獻。 參考博文: 仿淘寶首頁的淘寶頭條View垂直滾動 歡迎關注我的微信公眾號 不只是原創技術文章,更多的是對生活的思考總結 我在博主的基礎上做了如下工作: 修復了滾動條第二條點選事件無法觸發這個
【Android開發-5】界面裝修,五大布局你選誰
比例 技術分享 article 嵌套 content java lin layout mark 前言:假設要開一家店,門店裝修是非常重要的事情。有錢都請專門的建築設計公司來設計裝修,沒錢的僅僅能自己瞎折騰。好不好看全憑自己的感覺。像Android開發。在移動端大家看到的
進擊的Python【第九章】:paramiko模塊、線程與進程、各種線程鎖、queue隊列、生產者消費者模型
password locking form maxsize 廁所 sorted [0 hostname nbsp 一、paramiko模塊 他是什麽東西? paramiko模塊是用python語言寫的一個模塊,遵循SSH2協議,支持以加密和認證的方式,進行遠程服務器的連