AndroidStudioSocket客戶端


Android Studio 3.1.4

Build #AI-173.4907809, built on July 24, 2018
JRE: 1.8.0_152-release-1024-b02 amd64
JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
Windows 10 10.0

1.前言

 1 從這個帖子往后,開頭聲明版本就不用截圖了,用復制出來的文字,反正效果一樣的
 2 
 3 這次要說的是SocketTcp協議的客戶端
 4 
 5 有些小伙伴可能要問了,昨天才到本地注冊和登錄,今天怎么就開始Socket了,是不是進度太快了
 6 
 7 這個問題,我想說一下哈,我並不是按照教程或者目錄啥的做的
 8 
 9 我是對啥感興趣就做啥,但我也是小白,能學會的東西,對於大家來說沒問題的
10 
11 好了,話不多說,開始筆記!Lucky~

2.我們還是先創建一個項目,項目名是TCPclient,布局界面文件名為activity_tcpclient.xml

界面設計最終樣式如下

只后的UI會一次比一次復雜,以后就不口頭描述了,直接貼xml

  1 <?xml version="1.0" encoding="utf-8"?>
  2 <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3     xmlns:app="http://schemas.android.com/apk/res-auto"
  4     xmlns:tools="http://schemas.android.com/tools"
  5     android:layout_width="match_parent"
  6     android:layout_height="match_parent"
  7     tools:context=".TCPclient">
  8 
  9     <Button
 10         android:id="@+id/Button"
 11         style="@android:style/Widget.Material.Light.Button.Small"
 12         android:layout_width="175dp"
 13         android:layout_height="49dp"
 14         android:layout_marginBottom="8dp"
 15         android:layout_marginEnd="8dp"
 16         android:layout_marginTop="8dp"
 17         android:onClick="link"
 18         android:text="連 接 服 務 器"
 19         app:layout_constraintBottom_toBottomOf="parent"
 20         app:layout_constraintEnd_toEndOf="parent"
 21         app:layout_constraintHorizontal_bias="0.96"
 22         app:layout_constraintStart_toStartOf="parent"
 23         app:layout_constraintTop_toTopOf="parent"
 24         app:layout_constraintVertical_bias="0.593" />
 25 
 26     <EditText
 27         android:id="@+id/portEdit"
 28         android:layout_width="245dp"
 29         android:layout_height="43dp"
 30         android:layout_marginBottom="8dp"
 31         android:layout_marginEnd="16dp"
 32         android:layout_marginStart="8dp"
 33         android:layout_marginTop="156dp"
 34         android:ems="10"
 35         android:inputType="textPersonName"
 36         android:text="80"
 37         app:layout_constraintBottom_toBottomOf="parent"
 38         app:layout_constraintEnd_toEndOf="parent"
 39         app:layout_constraintHorizontal_bias="1.0"
 40         app:layout_constraintStart_toStartOf="parent"
 41         app:layout_constraintTop_toTopOf="parent"
 42         app:layout_constraintVertical_bias="0.016" />
 43 
 44     <TextView
 45         android:layout_width="145dp"
 46         android:layout_height="51dp"
 47         android:layout_marginStart="16dp"
 48         android:fontFamily="casual"
 49         android:text="TcpClient"
 50         android:textSize="30sp"
 51         app:layout_constraintBottom_toBottomOf="parent"
 52         app:layout_constraintHorizontal_bias="0.051"
 53         app:layout_constraintLeft_toLeftOf="parent"
 54         app:layout_constraintRight_toRightOf="parent"
 55         app:layout_constraintStart_toStartOf="parent"
 56         app:layout_constraintTop_toTopOf="parent"
 57         app:layout_constraintVertical_bias="0.029" />
 58 
 59     <TextView
 60         android:id="@+id/textView"
 61         android:layout_width="96dp"
 62         android:layout_height="37dp"
 63         android:layout_marginBottom="8dp"
 64         android:layout_marginEnd="8dp"
 65         android:layout_marginStart="16dp"
 66         android:layout_marginTop="8dp"
 67         android:text="IP、域名"
 68         android:textAlignment="center"
 69         android:textSize="22sp"
 70         app:layout_constraintBottom_toBottomOf="parent"
 71         app:layout_constraintEnd_toEndOf="parent"
 72         app:layout_constraintHorizontal_bias="0.0"
 73         app:layout_constraintStart_toStartOf="parent"
 74         app:layout_constraintTop_toTopOf="parent"
 75         app:layout_constraintVertical_bias="0.159" />
 76 
 77     <TextView
 78         android:id="@+id/textView2"
 79         android:layout_width="96dp"
 80         android:layout_height="37dp"
 81         android:layout_marginBottom="8dp"
 82         android:layout_marginEnd="8dp"
 83         android:layout_marginStart="16dp"
 84         android:layout_marginTop="8dp"
 85         android:text="端口"
 86         android:textAlignment="center"
 87         android:textSize="22sp"
 88         app:layout_constraintBottom_toBottomOf="parent"
 89         app:layout_constraintEnd_toEndOf="parent"
 90         app:layout_constraintHorizontal_bias="0.0"
 91         app:layout_constraintStart_toStartOf="parent"
 92         app:layout_constraintTop_toTopOf="parent"
 93         app:layout_constraintVertical_bias="0.3" />
 94 
 95     <TextView
 96         android:id="@+id/textView3"
 97         android:layout_width="96dp"
 98         android:layout_height="37dp"
 99         android:layout_marginBottom="8dp"
100         android:layout_marginEnd="8dp"
101         android:layout_marginStart="16dp"
102         android:layout_marginTop="8dp"
103         android:text="發送數據"
104         android:textAlignment="center"
105         android:textSize="22sp"
106         app:layout_constraintBottom_toBottomOf="parent"
107         app:layout_constraintEnd_toEndOf="parent"
108         app:layout_constraintHorizontal_bias="0.0"
109         app:layout_constraintStart_toStartOf="parent"
110         app:layout_constraintTop_toTopOf="parent"
111         app:layout_constraintVertical_bias="0.45" />
112 
113     <EditText
114         android:id="@+id/IPEdit"
115         android:layout_width="245dp"
116         android:layout_height="43dp"
117         android:layout_marginBottom="8dp"
118         android:layout_marginEnd="16dp"
119         android:layout_marginStart="8dp"
120         android:layout_marginTop="88dp"
121         android:ems="10"
122         android:inputType="textPersonName"
123         android:text="127.0.0.1"
124         app:layout_constraintBottom_toBottomOf="parent"
125         app:layout_constraintEnd_toEndOf="parent"
126         app:layout_constraintHorizontal_bias="1.0"
127         app:layout_constraintStart_toStartOf="parent"
128         app:layout_constraintTop_toTopOf="parent"
129         app:layout_constraintVertical_bias="0.004" />
130 
131     <EditText
132         android:id="@+id/dataEdit"
133         android:layout_width="245dp"
134         android:layout_height="43dp"
135         android:layout_marginBottom="8dp"
136         android:layout_marginEnd="16dp"
137         android:layout_marginStart="8dp"
138         android:layout_marginTop="156dp"
139         android:ems="10"
140         android:inputType="textPersonName"
141         android:text="GET /index.html HTTP/1.1"
142         app:layout_constraintBottom_toBottomOf="parent"
143         app:layout_constraintEnd_toEndOf="parent"
144         app:layout_constraintHorizontal_bias="1.0"
145         app:layout_constraintStart_toStartOf="parent"
146         app:layout_constraintTop_toTopOf="parent"
147         app:layout_constraintVertical_bias="0.23" />
148 
149     <Button
150         android:id="@+id/button"
151         style="@android:style/Widget.Material.Light.Button.Small"
152         android:layout_width="175dp"
153         android:layout_height="49dp"
154         android:layout_marginBottom="8dp"
155         android:layout_marginEnd="8dp"
156         android:layout_marginTop="8dp"
157         android:onClick="play"
158         android:text="發 送 數 據"
159         app:layout_constraintBottom_toBottomOf="parent"
160         app:layout_constraintEnd_toEndOf="parent"
161         app:layout_constraintHorizontal_bias="0.084"
162         app:layout_constraintStart_toStartOf="parent"
163         app:layout_constraintTop_toTopOf="parent"
164         app:layout_constraintVertical_bias="0.593" />
165 
166     <EditText
167         android:id="@+id/editText"
168         android:layout_width="354dp"
169         android:layout_height="177dp"
170         android:layout_marginBottom="8dp"
171         android:layout_marginTop="24dp"
172         android:ems="10"
173         android:inputType="textMultiLine"
174         app:layout_constraintBottom_toBottomOf="parent"
175         app:layout_constraintEnd_toEndOf="parent"
176         app:layout_constraintHorizontal_bias="0.513"
177         app:layout_constraintStart_toStartOf="parent"
178         app:layout_constraintTop_toTopOf="parent"
179         app:layout_constraintVertical_bias="0.977" />
180 
181 
182 </android.support.constraint.ConstraintLayout>

3.首先我們得給程序增加兩條權限,打開AndroidManifest.xml文件

加入兩條:

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

一定不要加錯了位置~

3.打開TCPclient項目java文件開始碼代碼

  1 package com.shawna.tcpclient;
  2 
  3 import android.content.DialogInterface;
  4 import android.support.v7.app.AlertDialog;
  5 import android.support.v7.app.AppCompatActivity;
  6 import android.os.Bundle;
  7 import android.view.View;
  8 import android.widget.Button;
  9 import android.widget.EditText;
 10 
 11 import java.io.InputStream;
 12 import java.io.OutputStream;
 13 import java.net.InetAddress;
 14 import java.net.Socket;
 15 
 16 public class TCPclient extends AppCompatActivity {
 17 
 18     EditText iptoedit;//ip編輯框對象
 19     EditText porttoedit;//端口編輯框對象
 20     EditText datatoedit;//數據編輯框對象
 21     Button Button;//連接服務器按鈕對象
 22     EditText edittotext;//接收的數據顯示編輯框對象
 23     Socket Socket = null;//Socket
 24     boolean buttontitle = true;//定義一個邏輯變量,用於判斷連接服務器按鈕狀態
 25     boolean RD = false;//用於控制讀數據線程是否執行
 26 
 27     OutputStream OutputStream = null;//定義數據輸出流,用於發送數據
 28     InputStream InputStream = null;//定義數據輸入流,用於接收數據
 29 
 30     @Override
 31     protected void onCreate(Bundle savedInstanceState) {
 32         super.onCreate(savedInstanceState);
 33         setContentView(R.layout.activity_tcpclient);
 34 
 35         iptoedit = (EditText)findViewById(R.id.IPEdit);//獲取ip地址編輯框對象
 36         porttoedit = (EditText)findViewById(R.id.portEdit);//獲取端口編輯框對象
 37         datatoedit = (EditText)findViewById(R.id.dataEdit);//獲取欲發送的數據編輯框對象
 38         Button = (Button)findViewById(R.id.Button);//獲取連接服務器按鈕對象
 39         edittotext = (EditText)findViewById(R.id.editText);//獲取接收數據顯示編輯框對象
 40 
 41     }
 42 
 43     //發送數據按鈕按下
 44     public void play(View view){
 45         //驗證編輯框用戶輸入內容是否合法
 46         if (thisif()) {
 47             //啟動一個新的線程,用於發送數據
 48             ThreadSendData t1 = new ThreadSendData();
 49             t1.start();
 50         } else{
 51             //這個地方默認是沒有反應,可以自行修改成信息框提示
 52             return;
 53         }
 54     }
 55 
 56     //連接服務器按鈕按下
 57     public void link(View view){
 58         //判斷按鈕狀態
 59         if (buttontitle == true){
 60             //如果按鈕沒有被按下,則按鈕狀態改為按下
 61             buttontitle = false;
 62             //讀數據線程可以執行
 63             RD = true;
 64             //並創建一個新的線程,用於初始化socket
 65             Connect_Thread Connect_thread = new Connect_Thread();
 66             Connect_thread.start();
 67             //改變按鈕標題
 68             Button.setText("斷 開 連 接");
 69         }else{
 70             //如果按鈕已經被按下,則改變按鈕標題
 71             Button.setText("連 接 服 務 器");
 72             //儲存狀態的變量反轉
 73             buttontitle = true;
 74             try{
 75                 //取消socket
 76                 Socket.close();
 77                 //socket設置為空
 78                 Socket = null;
 79                 //讀數據線程不執行
 80                 RD = false;
 81             }catch (Exception e){
 82                 //如果想寫的嚴謹一點,可以自行改動
 83                 e.printStackTrace();
 84             }
 85         }
 86     }
 87 
 88     //用線程創建Socket連接
 89     class Connect_Thread extends Thread{
 90         public void run(){
 91             //定義一個變量用於儲存ip
 92             InetAddress ipAddress;
 93             try {
 94                 //判斷socket的狀態,防止重復執行
 95                 if (Socket == null) {
 96                     //如果socket為空則執行
 97                         //獲取輸入的IP地址
 98                     ipAddress = InetAddress.getByName(iptoedit.getText().toString());
 99                         //獲取輸入的端口
100                     int port = Integer.valueOf(porttoedit.getText().toString());
101                     //新建一個socket
102                     Socket = new Socket(ipAddress, port);
103                     //獲取socket的輸入流和輸出流
104                     InputStream = Socket.getInputStream();
105                     OutputStream = Socket.getOutputStream();
106 
107                     //新建一個線程讀數據
108                     ThreadReadData t1 = new ThreadReadData();
109                     t1.start();
110                 }
111             } catch (Exception e) {
112                 //如果有錯誤則在這里返回
113                 e.printStackTrace();
114             }
115         }
116     }
117 
118     //用線程執行讀取服務器發來的數據
119     class ThreadReadData extends Thread{
120         public void run() {
121             //定義一個變量用於儲存服務器發來的數據
122             String textdata;
123             //根據RD變量的值判斷是否執行讀數據
124             while (RD) {
125                 try {
126                     //定義一個字節集,存放輸入的數據,緩存區大小為2048字節
127                     final byte[] ReadBuffer = new byte[2048];
128                     //用於存放數據量
129                     final int ReadBufferLengh;
130 
131                     //從輸入流獲取服務器發來的數據和數據寬度
132                         //ReadBuffer為參考變量,在這里會改變為數據
133                         //輸入流的返回值是服務器發來的數據寬度
134                     ReadBufferLengh = InputStream.read(ReadBuffer);
135 
136                     //驗證數據寬度,如果為-1則已經斷開了連接
137                     if (ReadBufferLengh == -1) {
138                         //重新歸位到初始狀態
139                         RD = false;
140                         Socket.close();
141                         Socket = null;
142                         buttontitle = true;
143                         Button.setText("連 接 服 務 器");
144                     } else {
145                         //如果有數據正常返回則進行處理顯示
146 
147                         /*
148                             這個地方有個很大的坑,讓我搞了不少的時間
149                             我用其他語言寫的Web服務器程序,默認編碼是gb2312
150                             AS的默認編碼是utf-8
151                             在獲取服務器發來的數據的時候,程序已經對這段gb2312的數據進行編碼...
152                             至於編碼是什么就不知道了
153                             我研究了很長時間,怎么轉碼也不對,越轉越亂
154                             最后測試出來是gb2312編碼已經被轉碼了,我就先恢復gb2312編碼
155                             然后轉成程序不會亂碼的utf-8
156                             如果目標服務器編碼是utf8的話就不用轉了
157                         */
158 
159                         //先恢復成GB2312編碼
160                         textdata = new String(ReadBuffer,0,ReadBufferLengh,"GB2312");//原始編碼數據
161                         //轉為UTF-8編碼后顯示在編輯框中
162                         edittotext.setText(new String(textdata.getBytes(),"UTF-8"));
163                     }
164                 } catch (Exception e) {
165                     e.printStackTrace();
166                 }
167             }
168         }
169     }
170 
171     //用線程發送數據
172     class ThreadSendData extends Thread{
173         public void run(){
174             try {
175                 //用輸出流發送數據
176                 OutputStream.write(datatoedit.getText().toString().getBytes());
177                 //發送數據之后會自動斷開連接,所以,恢復為最初的狀態
178                     //有個坑要說一下,因為發送完數據還得等待服務器返回,所以,不能把Socket也注銷掉
179                 buttontitle = true;
180                 //改變按鈕標題
181                 Button.setText("連 接 服 務 器");
182             }catch (Exception e){
183                 e.printStackTrace();
184             }
185         }
186     }
187 
188     //驗證編輯框內容是否合法
189     public boolean thisif (){
190         //定義一個信息框留作備用
191         AlertDialog.Builder message = new AlertDialog.Builder(this);
192         message.setPositiveButton("確定",click1);
193 
194         //分別獲取ip、端口、數據這三項的內容
195         String ip = iptoedit.getText().toString();
196         String port = porttoedit.getText().toString();
197         String data = datatoedit.getText().toString();
198 
199         //判斷是否有編輯框為空
200         if (ip == null || ip.length() == 0 || port == null || port.length() == 0 || data == null || data.length() == 0){
201             //如果有空則彈出提示
202             message.setMessage("各數據不能為空!");
203             AlertDialog m1 = message.create();
204             m1.show();
205             return false;
206         }else{
207             return true;
208         }
209     }
210 
211     //信息框按鈕按下事件
212     public DialogInterface.OnClickListener click1 = new DialogInterface.OnClickListener() {
213         @Override
214         public void onClick(DialogInterface dialog, int which) {
215             dialog.cancel();
216         }
217     };
218 
219 }

4.終於還是在長篇大論下,完成了記錄~

5.補充知識點

1 /*
2 
3   Socket在Android4.0版本開始,不能在主線程中使用!
4 
5   一些關鍵操作,不要忘記增加權限
6 
7   某些命令自帶轉碼效果,一定要注意轉碼
8 
9 */
 1 //線程的創建與使用
 2     //繼承Thread的線程
 3 class 線程名 extends Thread{
 4     public void run(){
 5         //執行的代碼
 6     }
 7 }
 8     //使用線程
 9 public void 某個函數名(參數類型 參數名){
10     線程名 自定義名稱 = new 線程名();
11     自定義名稱.start();
12 }

6.Lucky~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM