由于网上关于这两个语言间的代码较少而且混乱不好看懂,我就把自己这次项目的过程和问题在这里记录下来,希望能帮到大家吧。
首先,在进行编码之前我们应该找好服务器的IP地址和可用的端口号。
以我自己的电脑为例,win10系统,打开CMD窗口,输入ipconfig就可以查看电脑本机所使用的IP地址
选择手机与电脑同在的一个局域网的IP作为socket绑定的IP地址。端口号一般选择1024-65535之间的一个空闲号码就好。
这里需要注意的有两点:
1.电脑防火墙:
Win 10 系统默认打开防火墙阻止一切外部应用与本机进行数据传输,当然我们的应用也包含在内。可以选择添加信任的IP地址和端口号进行通信,不过我为了省事直接关掉了防火墙。
2.IP地址选择:
有时候电脑可能同时存在多个局域网IP地址,如何判断哪个是手机所在的IP地址也是一个问题。这里我是用手机安装一款名为Ping的手机应用
输入电脑的IP地址点击Start便可以查看网络是否通畅。如果这部分成功那么通信问题也就解决了一半。
我在之前尝试的过程中使用电脑打开Wifi共享工具或者手机和电脑连接一起连接学校的wifi但是都没有办法Ping通,一直误导了我很久,后来选择使用手机给电脑开热点总算是把问题解决了。不过原因目前还没有找到,有知道原因或者知道其他解决方法的大佬们可以劳烦在评论中分享下吗?
现在来介绍代码部分:
服务端:
print("正在创建SOCKET...")
sk = socket.socket()
print("创建成功,绑定端口...")
# 获取本地主机名
host ="你选择的IP地址"
prot = 12321
sk.bind((host,prot))
print("绑定成功,正在监听....")
sk.listen(5)
print(sk)
while True:
print("wating for connect...")
conn, address = sk.accept() # 新创建的套接字
now = time.time()
print("conn:" ,conn,"address:",address)
size = conn.recv(10) # 以10为单位进行接收图片大小信息
size_str = str(size, encoding="utf-8") #将Byte流转string
size_str = size_str.strip()
file_size = int(size_str) #获取图片文件大小
print(size_str)
if file_size != 0:
print("接收到数据", file_size, "int")
picpath = "img/"+str(now) + ".png"
f = open(picpath, "wb")#打开文件准备写入
has_size = 0 #已接收数据大小
while True:
if file_size == has_size:#如果接收的数据足够
break
res_data = file_size - has_size
if res_data >= 1024:
data = conn.recv(1024)
else:
data = conn.recv(res_data)
f.write(data)#写入文件
data_size = len(data)
has_size = has_size + data_size
print("接收",has_size)
f.close()#关闭文件
print("成功接收")
print("pic in path:",picpath)
print("begin to send back")
size = os.stat(picpath).st_size
print("pic size:",size)
ss = str(size)
while(len(ss)!=10):
ss = ss + " "
conn.sendall(bytes(ss,encoding='utf-8'))
print("send pic size")
print("wait for signal...")
# #返回图片测试*****************************
sum = 0
with open(picpath, "rb") as f:
for line in f:
sum = len(line) + sum
if(sum >1024):
time.sleep(0.1)#等待客户端接收数据
sum = sum - 1024
print(sum)
conn.sendall(line)
# #***********************
# 等待客户端接收数据
time.sleep(1)
conn.close()
sk.close()
代码的具体使用方法可以参考注释,函数的意思可以去网上找,这里不详细说了。我挑几个问题记录下
1:图片文件大小问题:
可能有的朋友不清楚为什么需要首先传送文件大小信息。这里是因为socket接收数据信息函数每次接收的数据量无法确定。由于网络的原因可能会有一定时间接收不到信息。这时候接收的数据一直为0。这就导致无法确定文件传输是否完成,所以需要提前接收文件大小,用于判断文件传输是否完成。
2:客户端等待时间问题:
因为手机端接收文件可能因为网络或者设备的原因,无法将发送的数据及时接收,导致丢包。出现客户端接收到的实际数据少于实际发送的数据量
客户端代码:
new Thread() {
public void run() {
try {
// Toast.makeText(MainActivity.this, "开始上传", Toast.LENGTH_SHORT).show();
socket = new Socket(HOST, SPORT);
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
FileInputStream fis = new FileInputStream(imgPaht);
//发送图片大小
int size = fis.available();
String s = String.valueOf(size);
while(s.length()<10){
s = s + " ";
}
byte[] bytes = s.getBytes();
out.write(bytes);
out.flush();
//发送图片
//读取图片到ByteArrayOutputStream
byte[] sendBytes = new byte[1024];
int length = 0;
while ((length = fis.read(sendBytes, 0, sendBytes.length)) > 0) {
out.write(sendBytes, 0, length);
out.flush();
}
fis.close();
String simg = imgPaht;
simg = simg.split("\\.")[0] + "D." + simg.split("\\.")[1];
//准备读入
DataInputStream dataInput = new DataInputStream(socket.getInputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
char[] num = new char[10];
int message;
message = br.read(num,0,10);
s = String.valueOf(num).trim();
size = Integer.parseInt(s);
byte[] data = new byte[size];
int len = 0;
// DataOutputStream dataOutput = new DataOutputStream(new FileOutputStream(simg));
while (len < size) {
if(size - len <=1024){
message = dataInput.read(data, len, size - len);
}else{
message = dataInput.read(data, len, 1024);
}
len += message;
if(message == -1)
{
break;
}
// dataOutput.write(data);
// dataOutput.flush();
}
ByteArrayOutputStream outPut = new ByteArrayOutputStream();
bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
bmp.compress(Bitmap.CompressFormat.PNG, 100, outPut);
out.close();
dataInput.close();
// dataOutput.close();
socket.close();
mhandler.sendEmptyMessage(0);
// break;
// }
} catch (UnknownHostException e) {
// Toast.makeText(MainActivity.this, "连接失败", Toast.LENGTH_SHORT).show();
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
//绑定控件
public void init() {
ivPic = findViewById(R.id.iv_bpicture);
ivCPic = findViewById(R.id.iv_cpicture);
btSelectPic = findViewById(R.id.bt_selectpic);
btSendPic = findViewById(R.id.bt_sendpic);
btSelectPic.setOnClickListener(MyListener);
btSendPic.setOnClickListener(MyListener);
mhandler = new Handler(){
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
switch (msg.what){
case 0:
ivCPic.setImageBitmap(bmp);
break;
}
}
};
}
Android拒绝在主页面使用耗时工作,socket网络连接操作就是其中一种。所以需要新建线程运行网络传输工作。并且使用Handler来进行不同线程之间的消息传输工作。