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~