简体   繁体   中英

Is async-await possible to be used with tcp file transfer in C#?

First of all, don't judge me, please, I'm pretty new to programming. I am trying to make a PC -> android app that sends files I select to my phone. It works, everything is fine, except that the methods aren't asynchronous. So, if I send something to my phone, my UI freezes until the sending is done. Can anyone help me? I've been reading all the docs for the async methods but i just can't get it, how do I implement it?

Here's some needed code:

This is the event that "sends" files, I want it to be async.

        private void button2_Click(object sender, EventArgs e) {
            await push();
        }

        public async Task push() {
            if (folder != null) {
                string ip = textBox1.Text + "." + textBox2.Text + "." + textBox3.Text + "." + textBox4.Text;
                listBox1.Items.Add("Creating a test server on the machine on port " + 8001 + ". Host ip: " + ip);
                server = new Server(ip, 8001);
                listBox1.Items.Add("Server running.");
                int i = 0;
                listBox1.Items.Add("Fetching files...");
                getFileNum(folder, ref i);
                listBox1.Items.Add("Files scanned. Current number: " + i + " files.");

                //send number of files to be transmitted
                listBox1.Items.Add("Getting your files ready... please standby.");
                server.send(i.ToString());
                listBox1.Items.Add("Your files are ready, beggining the transmitting process.");

                //actual method for sending files
                sendFiles(folder);
                listBox1.Items.Add(".");
                listBox1.Items.Add(".");
                listBox1.Items.Add("Files sent!");

            }
            else {
                MessageBox.Show("No directory path selected.", "Error_null_path");
            }
        }
        public async Task<Task> sendFiles(DirectoryInfo dir) {

            FileInfo[] files = dir.GetFiles();
            DirectoryInfo[] dirs = dir.GetDirectories();
            string path = dir.FullName + "/";
            foreach (FileInfo file in files) {
                await server.sendFile(path + file.Name, folder.FullName.Replace(folder.Name,""));
                listBox1.Items.Add("Sent file: " + file.Name);
            }
            foreach (DirectoryInfo subDir in dirs) {
                await sendFiles(subDir);
            }
            return null;
        }

this is the sending part of the code, in the "Server" class:

        public async Task sendFile(string filePath, string root) {
            FileInfo file = new FileInfo(filePath);
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            server.Bind(new IPEndPoint(IPAddress.Parse(ip), port));

            //send song name length

            server.Listen(0);
            Socket client = server.Accept();
            byte[] send;
            if (filePath.Replace(root, "").Length < 10)
                send = new UTF8Encoding(true).GetBytes("0" + filePath.Replace(root, "").Length);
            else
                send = new UTF8Encoding(true).GetBytes(filePath.Replace(root, "").Length.ToString());
            client.Send(send);
            client.Close();
            server.Close();

            //send song name

            server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            server.Bind(new IPEndPoint(IPAddress.Parse(ip), port));
            server.Listen(0);
            client = server.Accept();
            send = new UTF8Encoding(true).GetBytes(filePath.Replace(root,""));
            client.Send(send);
            client.Close();
            server.Close();

            //send the song

            server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            server.Bind(new IPEndPoint(IPAddress.Parse(ip), port));
            server.Listen(0);
            client = server.Accept();
            client.SendFile(file.FullName);
            client.Close();

            server.Close();
            //return null;
        }
        public async Task send(string data) {
            switch (data.Length) {
                case 1:
                    data = "000" + data;
                    break;
                case 2:
                    data = "00" + data;
                    break;
                case 3:
                    data = "0" + data;
                    break;
                default:
                    data = "0000";
                    break;
            }
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            server.Bind(new IPEndPoint(IPAddress.Parse(ip), port));
            server.Listen(0);
            Socket client = server.Accept();
            byte[] send = new UTF8Encoding(true).GetBytes(data);
            client.Send(send);
            client.Close();
            server.Close();
            //return null;
        }

I'm pretty new to programming

Tip: pay attention to compiler warnings. They'll point out very useful things. Like for this code, there will be a compiler warning telling you that you marked a method async but it will run synchronously.

TCP/IP sockets are very complex for a beginner. Heck, they're very complex for advanced developers. Asynchronous code is also complex, and asynchronous sockets are a firestorm of complexity.

So, for your case, since you already have a solution working, I would say to use Task.Run to push your existing synchronous code to a thread pool thread. This is an acceptable pattern for UI applications, but you wouldn't want to do this for any kind of a server app (eg, ASP.NET).

public async Task push() {
  ...
  var progress = new Progress<string>(report => listBox1.Items.Add(report));
  await Task.Run(() => sendFiles(folder, progress));
  listBox1.Items.Add(".");
  listBox1.Items.Add(".");
  listBox1.Items.Add("Files sent!");
  ...
}

public void sendFiles(DirectoryInfo dir, IProgress<string> progress) {
  ...
  foreach (FileInfo file in files) {
    server.sendFile(path + file.Name, folder.FullName.Replace(folder.Name,""));
    progress?.Report("Sent file: " + file.Name);
  }
  foreach (DirectoryInfo subDir in dirs) {
    sendFiles(subDir);
  }
}

public void sendFile(string filePath, string root) {
  ...
}

This is not a production-quality solution, but it should be a pattern that can get you started towards one.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM