写在前面的话:每一个实例的代码都会附上相应的代码片或者图片,保证代码完整展示在博客中。最重要的是保证例程的完整性!!!方便自己也方便他人~欢迎大家交流讨论~
今天又写了一篇Android客户端,是为了和前一篇的Python服务端进行传输,这一篇只写了这两个的通信,传输文件的操作还在学习中orz……
Android客户端
新建项目
名为client主要文件如下图
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
//第3个一般没什么用……因为在Android Studio中给我加了红色的波浪,但又不影响运行
//大家可以试一下如果没有第3个会不会报错
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
MainActivity
package com.example.administrator.client;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.button1)Button button1;
@BindView(R.id.editText)EditText editText;
private static final int port=12567;//connection另一端的端口
//hostip写服务端的IP
private static final String hostip="*****";//connection另一端的ip
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
@OnClick(R.id.button1)
public void runtcpClient(){
Log.i("Client","按钮监听事件有效");
Thread thread=new Thread(){
@Override
public void run(){ tcpClient();}
};
thread.start();
}
private void tcpClient(){
try{
Socket socketClient = new Socket(hostip, port);
Log.i("Client","新建套接字有效");
BufferedReader in=new BufferedReader(new InputStreamReader(socketClient.getInputStream()));
BufferedWriter out=new BufferedWriter(new OutputStreamWriter(socketClient.getOutputStream()));
String outMsg = "TCP connecting to " + port + System.getProperty("line.separator");//发出的数据
Log.i("Client","得到发出数据");
out.write(outMsg);//发送数据
Log.i("Client","发送数据有效");
out.flush();
Log.i("TcpClient", "sent: " + outMsg);
String inMsg = in.readLine() + System.getProperty("line.separator");//服务器返回的数据
Log.i("TcpClient", "received: " + inMsg);
socketClient.close();
}catch (UnknownHostException e){e.printStackTrace();}
catch (IOException e){e.printStackTrace();}
}
}
activity_main.xml
布局太简单了。就一个Button,我就不放展示图了
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/button1_name"
android:id="@+id/button1"/>
</LinearLayout>
strings.xml
添加<string name="button1_name">client</string>
Python服务端
import socket
BUFSIZE=4096
tcpServerSocket=socket.socket()#创建socket对象
hostname= socket.gethostname()#获取本地主机名
sysinfo = socket.gethostbyname_ex(hostname)
hostip=sysinfo[2][0]
port=12567#设置端口
tcpServerSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)#让端口可以复用
tcpServerSocket.bind((hostip,port))#将地址与套接字绑定,且套接字要求是从未被绑定过的
tcpServerSocket.listen(5)#代办事件中排队等待connect的最大数目
while True:
print("等待连接")
#建立客户端连接,接受connection,返回两个参数,c是该connection上可以发送和接收数据的新套接字对象
#addr是与connection另一端的套接字绑定的地址
clientSocket, addr = tcpServerSocket.accept()
print ('连接地址:', addr)
while True:
data=clientSocket.recv(BUFSIZE).decode()
if not data:
break
#向客户端表示接收到数据
str='来自服务端的消息!'
clientSocket.send(str.encode())#字符串编码为字节
#套接字在垃圾收集garbage-collected时会自动close
#close关闭该connection上的资源但不一定马上断开connection
#想要立即断开connection,要先调用shutdown再close
clientSocket.close() # 关闭连接
tcpServerSocket.close()
执行结果
和python服务端的通信结果
在cmd中输入netstat -a可查看当前tcp的状况,左边是本机地址,右边是外部地址,如下
是我的服务端打开从监听到和客户端建立连接的过程
有坑请注意!
问题1:原来gethostname得到的IP是虚拟机的IP,因为我的电脑安装了虚拟机,而且我发现之前一篇Python客户端服务端TCP传输套接字都是用的那个IP运行的很正常,但是现在客户端是Android用那个IPAndroid端会报出ETIMEOUT的错误
解决方法:因为我网上查了半天也不知道怎么解决,而且虚拟机那个IP经常会换,这意味着每次都要查IP然后改到Android非常费事而且不实际,所以就不用那个IP了,换成用本机IP(如果有什么解决方法麻烦大家告诉我orz……,谢谢了)
问题2: IPython console报错了个winerror:计算机积极地阻拦什么的,在Android端也有EREFUSED什么的
解决方法:打开控制面板——windows防火墙——点击如下图红框处——找到安全套接字隧道协议,打勾,确定
问题3:下图错误直指data=clientSocket.recv(BUFSIZE).decode()
这句代码
解决方法:但是我查了一下clientSocket的type,分明就是套接字类型。原因是我把clientSocket.close() # 关闭连接
放在第二个while循环中了,上面代码中已更正。