gpt4 book ai didi

c# - C#中如何通过套接字发送文件

转载 作者:可可西里 更新时间:2023-11-01 02:32:31 24 4
gpt4 key购买 nike

我有服务器和客户端控制台应用程序,它们可以正常通信并发送一些字符串。这是代码...

服务器

public static void Main()
{
try
{
IPAddress ipAd = IPAddress.Parse("127.0.0.1");

/* Initializes the Listener */
TcpListener myList = new TcpListener(ipAd, 1234);

/* Start Listeneting at the specified port */
myList.Start();

Console.WriteLine("The server is running at port 8001...");
Console.WriteLine("The local End point is :" + myList.LocalEndpoint);
Console.WriteLine("Waiting for a connection.....");

Socket s = myList.AcceptSocket();
Console.WriteLine("Connection accepted from " + s.RemoteEndPoint);

byte[] b = new byte[100];
int k = s.Receive(b);
Console.WriteLine("Recieved...");
for (int i = 0; i < k; i++)
Console.Write(Convert.ToChar(b[i]));

ASCIIEncoding asen = new ASCIIEncoding();
s.Send(asen.GetBytes("The string was recieved by the server."));
Console.WriteLine("\nSent Acknowledgement");
/* clean up */
s.Close();
myList.Stop();

}
catch (Exception e)
{
Console.WriteLine("Error..... " + e.StackTrace);
}
}

客户端

public static void Main()
{
try
{
TcpClient tcpclnt = new TcpClient();
Console.WriteLine("Connecting...");

tcpclnt.Connect("127.0.0.1", 1234);

Console.WriteLine("Connected");
Console.Write("Enter the string to be transmitted: ");

String str = Console.ReadLine();
Stream stm = tcpclnt.GetStream();

ASCIIEncoding asen = new ASCIIEncoding();
byte[] ba = asen.GetBytes(str);
Console.WriteLine("Transmitting...");

stm.Write(ba, 0, ba.Length);

byte[] bb = new byte[100];
int k = stm.Read(bb, 0, 100);

for (int i = 0; i < k; i++)
Console.Write(Convert.ToChar(bb[i]));

tcpclnt.Close();
}

catch (Exception e)
{
Console.WriteLine("Error... " + e.StackTrace);
}
}

现在,我需要添加通过相同应用程序发送文件的代码算法。要实现的关键是在客户端中使用 Socket.Send。我不确定文件的序列化和反序列化。

任何提示、建议、建议都非常受欢迎。谢谢。

最佳答案

根据要求,您可以在客户端执行以下操作 - 按照 this post 的建议,除了你可能想考虑将同步 Send 部分更改为异步 Send ,如下所示:

clientSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, endSendCallback, clientSocket); //use async

您的回调可能如下所示:

private static void endSendCallback(IAsyncResult ar) {
try {
SocketError errorCode;
int result = clientSocket.EndSend(ar, out errorCode);
Console.WriteLine(errorCode == SocketError.Success ?
"Successful! The size of the message sent was :" + result.ToString() :
"Error with error code: " + errorCode.ToString() //you probably want to consider to resend if there is error code, but best practice is to handle the error one by one
);
} catch (Exception e) { //exception
Console.WriteLine("Unhandled EndSend Exception! " + e.ToString());
//do something like retry or just report that the sending fails
//But since this is an exception, it probably best NOT to retry
}
}

至于经过上述改动后完整的客户端代码如何:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace TcpClientConsoleApplication {
class Program {
const int PORT_NO = 2201;
const string SERVER_IP = "127.0.0.1";
static Socket clientSocket; //put here
static void Main(string[] args) {
//Similarly, start defining your client socket as soon as you start.
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
loopConnect(3, 3); //for failure handling
string result = "";
do {
result = Console.ReadLine(); //you need to change this part
if (result.ToLower().Trim() != "exit") {
byte[] bytes = Encoding.ASCII.GetBytes(result); //Again, note that your data is actually of byte[], not string
//do something on bytes by using the reference such that you can type in HEX STRING but sending thing in bytes
clientSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, endSendCallback, clientSocket); //use async
//clientSocket.Send(bytes); use this for sync send
}
} while (result.ToLower().Trim() != "exit");
}

private static void endSendCallback(IAsyncResult ar) {
try {
SocketError errorCode;
int result = clientSocket.EndSend(ar, out errorCode);
Console.WriteLine(errorCode == SocketError.Success ?
"Successful! The size of the message sent was :" + result.ToString() :
"Error with error code: " + errorCode.ToString() //you probably want to consider to resend if there is error code, but best practice is to handle the error one by one
);
} catch (Exception e) { //exception
Console.WriteLine("Unhandled EndSend Exception! " + e.ToString());
//do something like retry or just report that the sending fails
//But since this is an exception, it probably best NOT to retry
}
}

static void loopConnect(int noOfRetry, int attemptPeriodInSeconds) {
int attempts = 0;
while (!clientSocket.Connected && attempts < noOfRetry) {
try {
++attempts;
IAsyncResult result = clientSocket.BeginConnect(IPAddress.Parse(SERVER_IP), PORT_NO, endConnectCallback, null);
result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(attemptPeriodInSeconds));
System.Threading.Thread.Sleep(attemptPeriodInSeconds * 1000);
} catch (Exception e) {
Console.WriteLine("Error: " + e.ToString());
}
}
if (!clientSocket.Connected) {
Console.WriteLine("Connection attempt is unsuccessful!");
return;
}
}

private const int BUFFER_SIZE = 4096;
private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message
private static void endConnectCallback(IAsyncResult ar) {
try {
clientSocket.EndConnect(ar);
if (clientSocket.Connected) {
clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), clientSocket);
} else {
Console.WriteLine("End of connection attempt, fail to connect...");
}
} catch (Exception e) {
Console.WriteLine("End-connection attempt is unsuccessful! " + e.ToString());
}
}

const int MAX_RECEIVE_ATTEMPT = 10;
static int receiveAttempt = 0;
private static void receiveCallback(IAsyncResult result) {
System.Net.Sockets.Socket socket = null;
try {
socket = (System.Net.Sockets.Socket)result.AsyncState;
if (socket.Connected) {
int received = socket.EndReceive(result);
if (received > 0) {
receiveAttempt = 0;
byte[] data = new byte[received];
Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //There are several way to do this according to https://stackoverflow.com/questions/5099604/any-faster-way-of-copying-arrays-in-c in general, System.Buffer.memcpyimpl is the fastest
//DO ANYTHING THAT YOU WANT WITH data, IT IS THE RECEIVED PACKET!
//Notice that your data is not string! It is actually byte[]
//For now I will just print it out
Console.WriteLine("Server: " + Encoding.UTF8.GetString(data));
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
} else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //not exceeding the max attempt, try again
++receiveAttempt;
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
} else { //completely fails!
Console.WriteLine("receiveCallback is failed!");
receiveAttempt = 0;
clientSocket.Close();
}
}
} catch (Exception e) { // this exception will happen when "this" is be disposed...
Console.WriteLine("receiveCallback is failed! " + e.ToString());
}
}
}
}

关于上述代码段如何工作的完整解释(注意第 7 点已更改,并为异步 Send 添加了第 8 点):

客户:

  1. 同样,将 Socket 类放在类上下文而不是方法上下文中,并在启动程序时立即对其进行初始化

    const int PORT_NO = 2201;
    const string SERVER_IP = "127.0.0.1";
    static Socket clientSocket; //put here
    static void Main(string[] args) {
    //Similarly, start defining your client socket as soon as you start.
    clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    //your other main routines
    }
  2. 然后通过ASync BeginConnect开始连接。我通常会通过 LoopConnect 更进一步,只是为了像这样处理故障。

    static void loopConnect(int noOfRetry, int attemptPeriodInSeconds) {
    int attempts = 0;
    while (!clientSocket.Connected && attempts < noOfRetry) {
    try {
    ++attempts;
    IAsyncResult result = clientSocket.BeginConnect(IPAddress.Parse(SERVER_IP), PORT_NO, endConnect, null);
    result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(attemptPeriodInSeconds));
    System.Threading.Thread.Sleep(attemptPeriodInSeconds * 1000);
    } catch (Exception e) {
    Console.WriteLine("Error: " + e.ToString());
    }
    }
    if (!clientSocket.Connected) {
    Console.WriteLine("Connection attempt is unsuccessful!");
    return;
    }
    }
  3. 与您对服务器 BeginAccept 执行的操作类似,您需要为 ASync BeginConnect< 定义 endConnectCallback/ 你使用。但是在这里,不像服务器需要重新调用BeginAccept,一旦你连接上了,你不需要做任何新的BeginConnect,因为你只需要连接一次

  4. 你可能想要声明buffer等。然后,在你连接之后,不要忘记接下来的ASync BeginReceive处理消息检索部分(与服务器类似)

    private const int BUFFER_SIZE = 4096;
    private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message
    private static void endConnectCallback(IAsyncResult ar) {
    try {
    clientSocket.EndConnect(ar);
    if (clientSocket.Connected) {
    clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), clientSocket);
    } else {
    Console.WriteLine("End of connection attempt, fail to connect...");
    }
    } catch (Exception e) {
    Console.WriteLine("End-connection attempt is unsuccessful! " + e.ToString());
    }
    }
  5. 自然地,您需要定义您的receiveCallback,就像您为服务器所做的那样。是的,正如您所猜到的,它与您为服务器所做的几乎相同!

  6. 您可以对数据做任何您想做的事情。请注意,您收到的数据实际上是 byte[],而不是 string。所以你可以用它做任何事情。但是为了示例,我将只使用string 来显示。

    const int MAX_RECEIVE_ATTEMPT = 10;
    static int receiveAttempt = 0;
    private static void receiveCallback(IAsyncResult result) {
    System.Net.Sockets.Socket socket = null;
    try {
    socket = (System.Net.Sockets.Socket)result.AsyncState;
    if (socket.Connected) {
    int received = socket.EndReceive(result);
    if (received > 0) {
    receiveAttempt = 0;
    byte[] data = new byte[received];
    Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //copy the data from your buffer
    //DO ANYTHING THAT YOU WANT WITH data, IT IS THE RECEIVED PACKET!
    //Notice that your data is not string! It is actually byte[]
    //For now I will just print it out
    Console.WriteLine("Server: " + Encoding.UTF8.GetString(data));
    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
    } else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //not exceeding the max attempt, try again
    ++receiveAttempt;
    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
    } else { //completely fails!
    Console.WriteLine("receiveCallback is failed!");
    receiveAttempt = 0;
    clientSocket.Close();
    }
    }
    } catch (Exception e) { // this exception will happen when "this" is be disposed...
    Console.WriteLine("receiveCallback is failed! " + e.ToString());
    }
    }
  7. 下一个(在最后一个之前)-- 是的,正如您已经猜到的那样,您只需要在主例程上做一些事情 - 假设您想将它用于 BeginSend 数据。因为您使用 Console 但您希望它以 byte[] 的形式发送内容,所以您需要进行转换(参见 linked post 的服务器 9 中的解释)。

    static void Main(string[] args) {
    //Similarly, start defining your client socket as soon as you start.
    clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    loopConnect(3, 3); //for failure handling
    string result = "";
    do {
    result = Console.ReadLine(); //you need to change this part
    if (result.ToLower().Trim() != "exit") {
    byte[] bytes = Encoding.ASCII.GetBytes(result); //Again, note that your data is actually of byte[], not string
    //do something on bytes by using the reference such that you can type in HEX STRING but sending thing in bytes
    clientSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, endSendCallback, clientSocket); //use async
    //clientSocket.Send(bytes); use this for sync send
    }
    } while (result.ToLower().Trim() != "exit");
    }
  8. 最后,在最后,您只需声明异步 EndSend 回调函数即可!

    private static void endSendCallback(IAsyncResult ar) {
    try {
    SocketError errorCode;
    int result = clientSocket.EndSend(ar, out errorCode);
    Console.WriteLine(errorCode == SocketError.Success ?
    "Successful! The size of the message sent was :" + result.ToString() :
    "Error with error code: " + errorCode.ToString() //you probably want to consider to resend if there is error code, but best practice is to handle the error one by one
    );
    } catch (Exception e) { //exception
    Console.WriteLine("Unhandled EndSend Exception! " + e.ToString());
    //do something like retry or just report that the sending fails
    //But since this is an exception, it probably best NOT to retry
    }
    }

关于c# - C#中如何通过套接字发送文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36600012/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com