您的位置:首页 > 理论基础 > 计算机网络

C#网络编程《四》下

2015-10-13 09:40 411 查看


4.客户端接收文件


4.1服务端的实现

对于服务端,我们只需要实现上一章遗留的sendFile()方法就可以了,它起初在handleProtocol中是注释掉的。另外,由于创建连接、获取流等操作与receiveFile()是没有区别的,所以我们将它提出来作为一个公共方法getStreamToClient()。下面是服务端的代码,只包含新增改过的代码,对于原有方法我只给出了签名:

class Server {

static void Main(string[] args) {

Console.WriteLine("Server is running ... ");

IPAddress ip = IPAddress.Parse("127.0.0.1");

TcpListener listener = new TcpListener(ip, 8500);

listener.Start(); // 开启对控制端口 8500 的侦听

Console.WriteLine("Start Listening ...");

while (true) {

// 获取一个连接,同步方法,在此处中断

TcpClient client = listener.AcceptTcpClient();

RemoteClient wapper = new RemoteClient(client);

wapper.BeginRead();

}

}

}

public class RemoteClient {

// 字段 略

public RemoteClient(TcpClient client) {}

// 开始进行读取

public void BeginRead() { }

// 再读取完成时进行回调

private void OnReadComplete(IAsyncResult ar) { }

// 处理protocol

private void handleProtocol(object obj) {

string pro = obj as string;

ProtocolHelper helper = new ProtocolHelper(pro);

FileProtocol protocol = helper.GetProtocol();

if (protocol.Mode == FileRequestMode.Send) {

// 客户端发送文件,对服务端来说则是接收文件

receiveFile(protocol);

} else if (protocol.Mode == FileRequestMode.Receive) {

// 客户端接收文件,对服务端来说则是发送文件

sendFile(protocol);

}

}

// 发送文件

private void sendFile(FileProtocol protocol) {

TcpClient localClient;

NetworkStream streamToClient = getStreamToClient(protocol, out localClient);

// 获得文件的路径

string filePath = Environment.CurrentDirectory + "/" +
protocol.FileName;

// 创建文件流

FileStream fs = new FileStream(filePath,
FileMode.Open, FileAccess.Read);

byte[] fileBuffer = new byte[1024]; //
每次传1KB

int bytesRead;

int totalBytes = 0;

// 创建获取文件发送状态的类

SendStatus status = new SendStatus(filePath);

// 将文件流转写入网络流

try {

do {

Thread.Sleep(10); // 为了更好的视觉效果,暂停10毫秒:-)

bytesRead = fs.Read(fileBuffer, 0, fileBuffer.Length);

streamToClient.Write(fileBuffer, 0, bytesRead);

totalBytes += bytesRead; // 发送了的字节数

status.PrintStatus(totalBytes); // 打印发送状态

} while (bytesRead > 0);

Console.WriteLine("Total {0} bytes sent, Done!", totalBytes);

} catch {

Console.WriteLine("Server has lost...");

}

streamToClient.Dispose();

fs.Dispose();

localClient.Close();

}

// 接收文件

private void receiveFile(FileProtocol protocol) { }

// 获取连接到远程的流 -- 公共方法

private NetworkStream getStreamToClient(FileProtocol protocol, out TcpClient localClient) {

// 获取远程客户端的位置

IPEndPoint endpoint = client.Client.RemoteEndPoint as IPEndPoint;

IPAddress ip = endpoint.Address;

// 使用新端口号,获得远程用于接收文件的端口

endpoint = new IPEndPoint(ip, protocol.Port);

// 连接到远程客户端

try {

localClient = new TcpClient();

localClient.Connect(endpoint);

} catch {

Console.WriteLine("无法连接到客户端 --> {0}", endpoint);

localClient = null;

return null;

}

// 获取发送文件的流

NetworkStream streamToClient = localClient.GetStream();

return streamToClient;

}

// 随机获取一个图片名称

private string generateFileName(string fileName) {}

}

服务端的sendFile方法和客户端的SendFile()方法完全类似,上面的代码几乎是一次编写成功的。另外注意我将客户端使用的SendStatus类也拷贝到了服务端。接下来我们看下客户端。


4.2客户端的实现

首先要注意的是客户端的SendFile()接收的参数是文件全路径,但是在写入到协议时只获取了路径中的文件名称。这是因为服务端不需要知道文件在客户端的路径,所以协议中只写文件名;而为了使客户端的SendFile()方法更通用,所以它接收本地文件的全路径。

客户端的ReceiveFile()的实现也和服务端的receiveFile()方法类似,同样,由于要保存到本地,为了避免文件名重复,我将服务端的generateFileName()方法复制了过来。

public class ServerClient :IDisposable {

// 字段略

public ServerClient() {}

// 发送消息到服务端

public void SendMessage(string msg) {}

// 发送文件 - 异步方法

public void BeginSendFile(string filePath) { }

private void SendFile(object obj) { }

// 发送文件 -- 同步方法

public void SendFile(string filePath) {}

// 接收文件 -- 异步方法

public void BeginReceiveFile(string fileName) {

ParameterizedThreadStart start =

new ParameterizedThreadStart(ReceiveFile);

start.BeginInvoke(fileName, null, null);

}

public void ReceiveFile(object obj) {

string fileName = obj as string;

ReceiveFile(fileName);

}

// 接收文件 -- 同步方法

public void ReceiveFile(string fileName) {

IPAddress ip = IPAddress.Parse("127.0.0.1");

TcpListener listener = new TcpListener(ip, 0);

listener.Start();

// 获取本地侦听的端口号

IPEndPoint endPoint = listener.LocalEndpoint as IPEndPoint;

int listeningPort = endPoint.Port;

// 获取发送的协议字符串

FileProtocol protocol =

new FileProtocol(FileRequestMode.Receive, listeningPort, fileName);

string pro = protocol.ToString();

SendMessage(pro); // 发送协议到服务端

// 中断,等待远程连接

TcpClient localClient = listener.AcceptTcpClient();

Console.WriteLine("Start sending file...");

NetworkStream stream = localClient.GetStream();

// 获取文件保存的路劲

string filePath =

Environment.CurrentDirectory + "/" + generateFileName(fileName);

// 创建文件流

FileStream fs = new FileStream(filePath,
FileMode.CreateNew, FileAccess.Write);

byte[] fileBuffer = new byte[1024]; //
每次传1KB

int bytesRead;

int totalBytes = 0;

// 从缓存buffer中读入到文件流中

do {

bytesRead = stream.Read(buffer, 0, BufferSize);

fs.Write(buffer, 0, bytesRead);

totalBytes += bytesRead;

Console.WriteLine("Receiving {0} bytes ...", totalBytes);

} while (bytesRead > 0);

Console.WriteLine("Total {0} bytes received, Done!", totalBytes);

fs.Dispose();

stream.Dispose();

localClient.Close();

listener.Stop();

}

// 随机获取一个图片名称

private string generateFileName(string fileName) {}

public void Dispose() {

if (streamToServer != null)

streamToServer.Dispose();

if (client != null)

client.Close();

}

}

上面关键的一句就是创建协议那句,注意到将mode由Send改为了Receive,同时传去了想要接收的服务端的文件名称。


4.3程序测试

现在我们已经完成了所有收发文件的步骤,可以看到服务端的所有操作都是被动的,接下来我们修改客户端的Main()程序,创建一个菜单,然后根据用户输入发送或者接收文件。

class Program {

static void Main(string[] args) {

ServerClient client = new ServerClient();

string input;

string path = Environment.CurrentDirectory + "/";

do {

Console.WriteLine("Send File: S1 - Client01.jpg,
S2 - Client02.jpg, S3 - Client03.jpg");

Console.WriteLine("Receive File: R1 - Server01.jpg,
R1 - Server02.jpg, R3- Server03.jpg");

Console.WriteLine("Press 'Q' to exit. \n");

Console.Write("Enter your choice: ");

input = Console.ReadLine();

switch(input.ToUpper()){

case "S1":

client.BeginSendFile(path + "Client01.jpg");

break;

case "S2":

client.BeginSendFile(path + "Client02.jpg");

break;

case "S3":

client.BeginSendFile(path + "Client02.jpg");

break;

case "R1":

client.BeginReceiveFile("Server01.jpg");

break;

case "R2":

client.BeginReceiveFile("Server01.jpg");

break;

case "R3":

client.BeginReceiveFile("Server01.jpg");

break;

}

} while (input.ToUpper() != "Q");

client.Dispose();

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: