关于linux与android传输代码tcp -传文件

关于linux与android传输代码tcp -传文件

感谢原作者

Android(客户端)与Linux(服务器端)进行TCP数据通信__Programmer_的博客-CSDN博客_linux+安卓apptcp图传

原作者提供了linux与android 通过tcp连接 的代码

经过尝试,遇到一些bug,但最终解决,感谢作者的源代码!

本文主要将提取出传输部分,并改造成可以传输文件的代码

源代码(传输msg版)

默认是服务器先启动监听,然后客户端运行,建立连接,然后客户端发什么信息,服务端接受到会显示出来,并且重新发送给客户端。已经内置到自己的应用中,很成功。

服务端

采用的是8080端口

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>

#define PORT 8080            //服务器端监听端口号
#define MAX_BUFFER 1024         //数据缓冲区最大值

int main()
{
    struct sockaddr_in server_addr, client_addr;
    int server_sockfd, client_sockfd;
    int size, write_size;
    char buffer[MAX_BUFFER];

    if ((server_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)    //创建Socket
    {
        perror("Socket Created Failed!\n");
        exit(1);
    }
    printf("Socket Create Success!\n");

    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(PORT);
    bzero(&(server_addr.sin_zero), 8);

    int opt = 1;
    int res = setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));    //设置地址复用
    if (res < 0)
    {
        perror("Server reuse address failed!\n");
        exit(1);
    }

    if (bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)  //绑定本地地址
    {
        perror("Socket Bind Failed!\n");
        exit(1);
    }
    printf("Socket Bind Success!\n");

    if (listen(server_sockfd, 5) == -1)                 //监听
    {
        perror("Listened Failed!\n");
        exit(1);
    }
    printf("Listening ....\n");

    socklen_t len = sizeof(client_addr);

    printf("waiting connection...\n");
    if ((client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &len)) == -1)  //等待客户端连接
    {
        perror("Accepted Failed!\n");
        exit(1);
    }
    printf("connection established!\n");
    printf("waiting message...\n");

    while (1)
    {
        memset(buffer, 0, sizeof(buffer));                             //清空数据缓冲区

        if ((size = read(client_sockfd, buffer, MAX_BUFFER)) == -1)    //读取客户端的数据
        {
            perror("Recv Failed!\n");
            exit(1);
        }

        if (size != 0)                                               
        {
            buffer[size] = '\0';
            printf("Recv msg from client: %s\n", buffer);
            if ((write_size = write(client_sockfd, buffer, MAX_BUFFER)) > 0)   //把收到的数据回发给客户端
            {
                printf("Sent msg to client successfully!\n");
            }

        }
    }

    close(client_sockfd);   //关闭Socket
    close(server_sockfd);

    return 0;
}

编译出可执行文件

g++ con.cpp -o server

运行

./server  
移动端

TcpClientConnector.java

package com.google.ar.core.examples.java.helloar;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;

public class TcpClientConnector {

    private static TcpClientConnector mTcpClientConnector;
    private Socket mClient;
    private ConnectListener mListener;
    private Thread mConnectThread;

    public interface ConnectListener {
        void onReceiveData(String data);
    }

    public void setOnConnectListener(ConnectListener listener) {
        this.mListener = listener;
    }

    public static TcpClientConnector getInstance() {
        if (mTcpClientConnector == null)
            mTcpClientConnector = new TcpClientConnector();
        return mTcpClientConnector;
    }

    Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 100:
                    if (mListener != null) {
                        mListener.onReceiveData(msg.getData().getString("data"));
                    }
                    break;
            }
        }
    };

    public void createConnect(final String mSerIP, final int mSerPort) {
        if (mConnectThread == null) {
            mConnectThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        connect(mSerIP, mSerPort);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
            mConnectThread.start();
        }
    }

    /**
     * 与服务端进行连接
     *
     * @throws IOException
     */
    private void connect(String mSerIP, int mSerPort) throws IOException {
        if (mClient == null) {
            mClient = new Socket(mSerIP, mSerPort);
        }
        InputStream inputStream = mClient.getInputStream();
        byte[] buffer = new byte[1024];
        int len = -1;
        while ((len = inputStream.read(buffer)) != -1) {
            String data = new String(buffer, 0, len);
            Message message = new Message();
            message.what = 100;
            Bundle bundle = new Bundle();
            bundle.putString("data", data);
            message.setData(bundle);
            mHandler.sendMessage(message);
        }
    }

    /**
     * 发送数据
     *
     * @param data 需要发送的内容
     */
    public void send(String data) throws IOException {
        if(mClient!=null)
        {
            Log.d("socket",mClient.toString());
            OutputStream outputStream = mClient.getOutputStream();
            outputStream.write(data.getBytes());
        }
    }

    /**
     * 断开连接
     *
     * @throws IOException
     */
    public void disconnect() throws IOException {
        if (mClient != null) {
            mClient.close();
            mClient = null;
        }
    }
}

main_activity.java

 // 连接
  private TcpClientConnector connector;
....
  @Override
  protected void onCreate(Bundle savedInstanceState) {
      ....
         // 创建 连接
          
           // 尝试连接
        connector = TcpClientConnector.getInstance();   //获取connector实例
        if(true){
          //连接Tcp服务器端
          connector.createConnect("10.112.146.228",8080);   //调试使用
          connector.setOnConnectListener(new TcpClientConnector.ConnectListener() {
            @Override
            public void onReceiveData(String data) {
              //Received Data,do somethings.
              System.out.println(data);
            }
          });
        }else{
          try{   //断开与服务器的连接
            connector.disconnect();
          }catch(IOException e){
            e.printStackTrace();
          }
        }
    .....
  }

把发送信息的函数,加到你想加的函数里。记得android7以后,tcp连接必须放在线程中

 new Thread(new Runnable(){
      @Override
      public void run() {
        try {
          
          connector.send("hello world");

        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }).start();

源代码(传输文件版)

需求:需要从服务器传输一个文件,到客户端,客户端接收到后,保存至手机中。

服务器需要传输多个文件,客户端需要对每个文件分别命名并进行保存

服务器首先发送启示符”START" 表明传输的开始

然后 下一个块 发送文件名

接下来所有的块都发送文件的块

发送完毕后 ,发送中止符 “END”

代表该文件的发送结束

传输一个文件版

tips

android 8以后禁止往外部存储写文件,使用openFileOutput 可以

自动在data/data/包…/files/…下自动写文件(经尝试,该方法在主线程下可以创建哦)

OutputStream os = openFileOutput("file.txt", Activity.MODE_PRIVATE);
String str1 = "abc";
os.write(str1.getBytes("utf-8"));

tips

bug,当服务器发送字节块,采用的是1024字节的byte数组

比如start只会占前5位,后面全填0

而客户端接受的会将整个字节数组转化为字符串,就变成这个样子

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3q63bnuL-1668068832148)(%E5%85%B3%E4%BA%8Elinux%E4%B8%8Eandroid%E4%BC%A0%E8%BE%93%E4%BB%A3%E7%A0%81tcp%20-%E4%BC%A0%E6%96%87%E4%BB%B6.assets/image-20221110102121564.png)]

解决这个bug有两个方向

一个是找到android端能够截取byte数组的函数

感谢作者[Java中去掉byte]中填充的0,并且转为String_银龙gg的博客-CSDN博客

但这样会有点担心,(因为平常的字节流为有0,会被提前中止)

经过探寻 该0为彼0(实际上传递的是0的assci码 48)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HF6EAdki-1668068832149)(%E5%85%B3%E4%BA%8Elinux%E4%B8%8Eandroid%E4%BC%A0%E8%BE%93%E4%BB%A3%E7%A0%81tcp%20-%E4%BC%A0%E6%96%87%E4%BB%B6.assets/image-20221110104956116.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m09BDfRH-1668068832150)(%E5%85%B3%E4%BA%8Elinux%E4%B8%8Eandroid%E4%BC%A0%E8%BE%93%E4%BB%A3%E7%A0%81tcp%20-%E4%BC%A0%E6%96%87%E4%BB%B6.assets/image-20221110105506360.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4EBQI8db-1668068832150)(%E5%85%B3%E4%BA%8Elinux%E4%B8%8Eandroid%E4%BC%A0%E8%BE%93%E4%BB%A3%E7%A0%81tcp%20-%E4%BC%A0%E6%96%87%E4%BB%B6.assets/image-20221110105515496.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-srDbWKnt-1668068832151)(%E5%85%B3%E4%BA%8Elinux%E4%B8%8Eandroid%E4%BC%A0%E8%BE%93%E4%BB%A3%E7%A0%81tcp%20-%E4%BC%A0%E6%96%87%E4%BB%B6.assets/image-20221110105555830.png)]

而ascci码为0的是字符null,根本不需要有这方面的担心(后续还是有bug)

/指定长度100
private byte[] bytes = new byte[100];

//此处省略获取数据  byte[]中的数据实际长度为30   剩余的70将会自动填充0
//直接转为String
String str = new String(bytes);
//str中前30位为正常字符  后70位为填充  直接页面展示会乱码
System.out.print(str);
//通过下面的方法去掉自动填充的0
String str1 = StringUtils.byteToStr(bytes);
System.out.print(str1); //正确数据 只有实际的30位  没有填充的乱码

 /**
     * 去掉byte[]中填充的0 转为String
     * @param buffer
     * @return
     */
    public static String byteToStr(byte[] buffer) {
        try {
            int length = 0;
            for (int i = 0; i < buffer.length; ++i) {
                if (buffer[i] == 0) {
                    length = i;
                    break;
                }
            }
            return new String(buffer, 0, length, "UTF-8");
        } catch (Exception e) {
            return "";
        }
    }

一个是增加服务端与客户端发送的逻辑(比如也发送字长,或者为每个块添加中止符什么的)

前面方法已经可以了,因此放弃该思路

bug1

把发送文件块的全部截取成0了…

通过增加逻辑,对于文件名,启示符,结束符进行截取

对于文件块进行不截取的方式,解决问题。

bug2

按说应该传完start和文件名,再传文件块

文件块传完毕后,会传送最后一个文件块,以END开头

但不知为什么,END的块,与最后一个文件包混到了一起

发送完文件块后,等待1s

服务端代码
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include<string>
#include<iostream>


#define PORT 8080            //服务器端监听端口号
#define MAX_BUFFER 1024         //数据缓冲区最大值
using namespace std;
int main()
{
    struct sockaddr_in server_addr, client_addr;
    int server_sockfd, client_sockfd;
    int size, write_size;
    char buffer[MAX_BUFFER];

    if ((server_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)    //创建Socket
    {
        perror("Socket Created Failed!\n");
        exit(1);
    }
    printf("Socket Create Success!\n");

    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(PORT);
    bzero(&(server_addr.sin_zero), 8);

    int opt = 1;
    int res = setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));    //设置地址复用
    if (res < 0)
    {
        perror("Server reuse address failed!\n");
        exit(1);
    }

    if (bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)  //绑定本地地址
    {
        perror("Socket Bind Failed!\n");
        exit(1);
    }
    printf("Socket Bind Success!\n");

    if (listen(server_sockfd, 5) == -1)                 //监听
    {
        perror("Listened Failed!\n");
        exit(1);
    }
    printf("Listening ....\n");

    socklen_t len = sizeof(client_addr);

    printf("waiting connection...\n");
    if ((client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &len)) == -1)  //等待客户端连接
    {
        perror("Accepted Failed!\n");
        exit(1);
    }
    printf("connection established!\n");

    // 开始传文件
    //发送一个文件名
    //bytes[]

    
    string start="START";
    char buf[1024] = { 0 };
    strcpy(buf, start.c_str());
    write(client_sockfd,buf,MAX_BUFFER);
    memset(buf, 0, sizeof(buf)); 


    string name ="1.txt";
    strcpy(buf, name.c_str());
    write(client_sockfd,buf,MAX_BUFFER);
    memset(buf, 0, sizeof(buf)); 
    int bytes;
    FILE* fp = fopen("test1.txt", "rb");

    while ((bytes = fread(buf, 1, 1024, fp) )> 0)//读取文件内容,每次一个1024字节
    {
        cout<<bytes<<endl;
        // for(auto byte:buf)
        // {
        //     cout<<byte<<" ";
        // }
        // cout<<endl;
        write(client_sockfd,buf,MAX_BUFFER);//把buf里存的东西send给服务端
        memset(buf, 0, sizeof(buf)); 
    }

    sleep(1);
    string end="END";
    strcpy(buf, end.c_str());
    for(auto byte:buf)
        {
            cout<<byte<<" ";
        }
        cout<<endl;
    write(client_sockfd,buf,MAX_BUFFER);
    memset(buf, 0, sizeof(buf)); 
    


    // printf("waiting message...\n");

    // while (1)
    // {
    //     memset(buffer, 0, sizeof(buffer));                             //清空数据缓冲区

    //     if ((size = read(client_sockfd, buffer, MAX_BUFFER)) == -1)    //读取客户端的数据
    //     {
    //         perror("Recv Failed!\n");
    //         exit(1);
    //     }

    //     if (size != 0)                                               
    //     {
    //         buffer[size] = '\0';
    //         printf("Recv msg from client: %s\n", buffer);
    //         if ((write_size = write(client_sockfd, buffer, MAX_BUFFER)) > 0)   //把收到的数据回发给客户端
    //         {
    //             printf("Sent msg to client successfully!\n");
    //         }
    //     }
    // }

    close(client_sockfd);   //关闭Socket
    close(server_sockfd);

    return 0;
}
客户端代码

on create中创建连接

 // 尝试连接
    connector = TcpClientConnector.getInstance();   //获取connector实例
    if(true){
      //连接Tcp服务器端
      connector.createConnect("10.112.146.228",8080);   //调试使用
      connector.setOnConnectListener(new TcpClientConnector.ConnectListener() {
        @Override
        public void onReceiveData(String data) {

          //Received Data,do somethings.
          Log.i("received", String.valueOf(data.length()));
          Log.i("received",data);


          if(receive_state==0 && data.equals("START")) //准备接受文件名
          {
            Log.i("file1","文件传输开始,准备接受文件名噢");
            os = null;
            receive_state=1;
          }
          else if (receive_state==1)//接受文件名
          {

            file_name=byteToStr(data.getBytes());
            Log.i("file1","接受到文件名:"+file_name);
            try {
              os = openFileOutput(file_name, Context.MODE_PRIVATE);
            } catch (IOException e) {
              e.printStackTrace();
            }
            receive_state=2;
          }
          else if(receive_state==2 && !data.equals("END") && data.length()!=0) //接受文件块
          {
            Log.i("file1","正在写入块:"+data.getBytes().length);
            try {
              os.write(data.getBytes());
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
          else if(data.equals("END")) //该文件传输结束
          {
            Log.i("file1","文件写入结束");
            try {
              os.close();
            } catch (IOException e) {
              e.printStackTrace();
            }
            os=null;
            receive_state=0;
            file_name=null;
          }
        }
      });
    }else{
      try{   //断开与服务器的连接
        connector.disconnect();
      }catch(IOException e){
        e.printStackTrace();
      }
    }

截取函数

 public String byteToStr(byte[] buffer) {
    try {
      int length = 0;
      for (int i = 0; i < buffer.length; ++i) {
        if (buffer[i] == 0) {
          length = i;
          break;
        }
      }
      return new String(buffer, 0, length, "UTF-8");
    } catch (Exception e) {
      return "";
    }
  }

TcpClientConnector

package com.google.ar.core.examples.java.helloar;

import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;


import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;


public class TcpClientConnector {

    private static TcpClientConnector mTcpClientConnector;
    private Socket mClient;
    private ConnectListener mListener;
    private Thread mConnectThread;
    private BufferedOutputStream bos= null;

    public interface ConnectListener {
        void onReceiveData(String data);
    }

    public void setOnConnectListener(ConnectListener listener) {
        this.mListener = listener;
    }

    public static TcpClientConnector getInstance() {
        if (mTcpClientConnector == null)
            mTcpClientConnector = new TcpClientConnector();
        return mTcpClientConnector;
    }

    Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 100:
                    if (mListener != null) {
                        mListener.onReceiveData(msg.getData().getString("data"));
                    }
                    break;
            }
        }
    };

    public void createConnect(final String mSerIP, final int mSerPort) {
        if (mConnectThread == null) {
            mConnectThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        connect(mSerIP, mSerPort);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
            mConnectThread.start();
        }
    }
    /**
     * 去掉byte[]中填充的0 转为String
     * @param buffer
     * @return
     */
    public static String byteToStr(byte[] buffer) {
        try {
            int length = 0;
            for (int i = 0; i < buffer.length; ++i) {
                if (buffer[i] == 0) {
                    length = i;
                    break;
                }
            }
            return new String(buffer, 0, length, "UTF-8");
        } catch (Exception e) {
            return "";
        }
    }
    /**
     * 与服务端进行连接
     *
     * @throws IOException
     */
    private void connect(String mSerIP, int mSerPort) throws IOException {
        if (mClient == null) {
            mClient = new Socket(mSerIP, mSerPort);
        }
        InputStream inputStream = mClient.getInputStream();
        byte[] buffer = new byte[1024];
        int len = -1;


        while ((len = inputStream.read(buffer)) != -1) {
//            String data = new String(buffer, 0, len);
            Log.i("real_string", String.valueOf(len));//len_byte能看出接受到的块大小
            String real_data=new String(buffer,0,len);

            // 如果是start,end 就放截取后的
            // 其他 放未截取的 文件名则主线程截取
            String data =byteToStr(buffer);
            Message message = new Message();
            message.what = 100;
            Bundle bundle = new Bundle();
            Log.i("real_string","一块:");
            Log.i("real_string",real_data);//real_string表明实际接收到的数据
            if (data.equals("START") || data.equals("END"))
            {
                bundle.putString("data", data);
            }
            else
            {
                bundle.putString("data", real_data);
            }
            message.setData(bundle);
            mHandler.sendMessage(message);
        }
        if (bos!=null){
            bos.close();
        }

    }

    /**
     * 发送数据
     *
     * @param data 需要发送的内容
     */
    public void send(String data) throws IOException {
        if(mClient!=null)
        {
            Log.d("socket",mClient.toString());
            OutputStream outputStream = mClient.getOutputStream();
            outputStream.write(data.getBytes());
        }
    }

    /**
     * 断开连接
     *
     * @throws IOException
     */
    public void disconnect() throws IOException {

        if (mClient != null) {
            mClient.close();
            mClient = null;
        }
    }
}

传输多个文件版
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值