没有合适的资源?快使用搜索试试~ 我知道了~
资源详情
资源评论
资源推荐

C#网络编程
引言
C#网络编程系列文章计划简单地讲述网络编程方面的基础知识,由于本人在这方面功
力有限,所以只能提供一些初步的入门知识,希望能对刚开始学习的朋友提供一些帮助。如
果想要更加深入的内容,可以参考相关书籍。
本文是该系列第一篇,主要讲述了基于套接字(Socket)进行网络编程的基本概念,其
中包括 TCP 协议、套接字、聊天程序的三种开发模式,以及两个基本操作:侦听端口、连
接远程服务端;第二篇讲述了一个简单的范例:从客户端传输字符串到服务端,服务端接收
并打印字符串,将字符串改为大写,然后再将字符串回发到客户端,客户端最后打印传回的
字符串;第三篇是第二篇的一个强化,讲述了第二篇中没有解决的一个问题,并使用了异步
传输的方式来完成和第二篇同样的功能;第四篇则演示了如何在客户端与服务端之间收发文
件;第五篇实现了一个能够在线聊天并进行文件传输的聊天程序,实际上是对前面知识的一
个综合应用。
与本文相关的还有一篇文章是:C#编写简单的聊天程序,但这个聊天程序不及本系列
中的聊天程序功能强大,实现方式也不相同。
网络编程基本概念
1.面向连接的传输协议:TCP
对于 TCP 协议我不想说太多东西,这属于大学课程,又涉及计算机科学,而我不是“学
院派”,对于这部分内容,我觉得作为开发人员,只需要掌握与程序相关的概念就可以了,
不需要做太艰深的研究。
我们首先知道 TCP 是面向连接的,它的意思是说两个远程主机(或者叫进程,因为实
际上远程通信是进程之间的通信,而进程则是运行中的程序),必须首先进行一个握手过程,
确认连接成功,之后才能传输实际的数据。比如说进程 A 想将字符串“It's a fine day today”
发给进程 B,它首先要建立连接。在这一过程中,它首先需要知道进程 B 的位置(主机地
址和端口号)。随后发送一个不包含实际数据的请求报文,我们可以将这个报文称之为
“hello”。如果进程 B 接收到了这个“hello”,就向进程 A 回复一个“hello”,进程 A 随后
才发送实际的数据“It's a fine day today”。
关于 TCP 第二个需要了解的,就是它是全双工的。意思是说如果两个主机上的进程(比
如进程 A、进程 B),一旦建立好连接,那么数据就既可以由 A 流向 B,也可以由 B 流向 A。
Generated by Foxit PDF Creator © Foxit Software
http://www.foxitsoftware.com For evaluation only.

除此以外,它还是点对点的,意思是说一个 TCP 连接总是两者之间的,在发送中,通过一
个连接将数据发给多个接收方是不可能的。TCP 还有一个特性,就是称为可靠的数据传输,
意思是连接建立后,数据的发送一定能够到达,并且是有序的,就是说发的时候你发了 ABC,
那么收的一方收到的也一定是 ABC,而不会是 BCA 或者别的什么。
编程中与 TCP 相关的最重要的一个概念就是套接字。我们应该知道网络七层协议,如
果我们将上面的应用程、表示层、会话层笼统地算作一层(有的教材便是如此划分的),那
么我们编写的网络应用程序就位于应用层,而大家知道 TCP 是属于传输层的协议,那么我
们在应用层如何使用传输层的服务呢(消息发送或者文件上传下载)?大家知道在应用程序
中我们用接口来分离实现,在应用层和传输层之间,则是使用套接字来进行分离。它就像是
传输层为应用层开的一个小口,应用程序通过这个小口向远程发送数据,或者接收远程发来
的数据;而这个小口以内,也就是数据进入这个口之后,或者数据从这个口出来之前,我们
是不知道也不需要知道的,我们也不会关心它如何传输,这属于网络其它层次的工作。
举个例子,如果你想写封邮件发给远方的朋友,那么你如何写信、将信打包,属于应用
层,信怎么写,怎么打包完全由我们做主;而当我们将信投入邮筒时,邮筒的那个口就是套
接字,在进入套接字之后,就是传输层、网络层等(邮局、公路交管或者航线等)其它层次
的工作了。我们从来不会去关心信是如何从西安发往北京的,我们只知道写好了投入邮筒就
OK 了。可以用下面这两幅图来表示它:
Generated by Foxit PDF Creator © Foxit Software
http://www.foxitsoftware.com For evaluation only.

注意在上面图中,两个主机是对等的,但是按照约定,我们将发起请求的一方称为客
户端,将另一端称为服务端。可以看出两个程序之间的对话是通过套接字这个出入口来完成
的,实际上套接字包含的最重要的也就是两个信息:连接至远程的本地的端口信息(本机地
址和端口号),连接到的远程的端口信息(远程地址和端口号)。注意上面词语的微妙变化,一
个是本地地址,一个是远程地址。
这里又出现了了一个名词端口。一般来说我们的计算机上运行着非常多的应用程序,它
们可能都需要同远程主机打交道,所以远程主机就需要有一个 ID 来标识它想与本地机器上
的哪个应用程序打交道,这里的 ID 就是端口。将端口分配给一个应用程序,那么来自这个
端口的数据则总是针对这个应用程序的。有这样一个很好的例子:可以将主机地址想象为电
话号码,而将端口号想象为分机号。
在.NET 中,尽管我们可以直接对套接字编程,但是.NET 提供了两个类将对套接字的编
程进行了一个封装,使我们的使用能够更加方便,这两个类是 TcpClient 和 TcpListener,它
与套接字的关系如下:
从上面图中可以看出 TcpClient 和 TcpListener 对套接字进行了封装。从中也可以看出,
TcpListener 用于接受连接请求,而 TcpClient 则用于接收和发送流数据。这幅图的意思是
Generated by Foxit PDF Creator © Foxit Software
http://www.foxitsoftware.com For evaluation only.

TcpListener 持续地保持对端口的侦听,一旦收到一个连接请求后,就可以获得一个 TcpClient
对象,而对于数据的发送和接收都有 TcpClient 去完成。此时,TcpListener 并没有停止工作,
它始终持续地保持对端口的侦听状态。
我们考虑这样一种情况:两台主机,主机 A 和主机 B,起初它们谁也不知道谁在哪儿,
当它们想要进行对话时,总是需要有一方发起连接,而另一方则需要对本机的某一端口进
行侦听。而在侦听方收到连接请求、并建立起连接以后,它们之间进行收发数据时,发起
连接的一方并不需要再进行侦听。因为连接是全双工的,它可以使用现有的连接进行收发
数据。而我们前面已经做了定义:将发起连接的一方称为客户端,另一段称为服务端,则现
在可以得出:总是服务端在使用 TcpListener 类,因为它需要建立起一个初始的连接。
基本操作
1.服务端对端口进行侦听
接下来我们开始编写一些实际的代码,第一步就是开启对本地机器上某一端口的侦听。首先
创建一个控制台应用程序,将项目名称命名为 ServerConsole,它代表我们的服务端。如果
想要与外界进行通信,第一件要做的事情就是开启对端口的侦听,这就像为计算机打开了一
个“门”,所有向这个“门”发送的请求(“敲门”)都会被系统接收到。在 C#中可以通过下
面几个步骤完成,首先使用本机 Ip 地址和端口号创建一个 System.Net.Sockets.TcpListener 类
型的实例,然后在该实例上调用 Start()方法,从而开启对指定端口的侦听。
using System.Net; // 引入这两个命名空间,以下同
using System.Net.Sockets;
using ... // 略
class Server {
static void Main(string[] args) {
Console.WriteLine("Server is running ... ");
IPAddress ip = new IPAddress(new byte[] { 127, 0, 0, 1 });
TcpListener listener = new TcpListener(ip, 8500);
listener.Start(); // 开始侦听
Console.WriteLine("Start Listening ...");
Console.WriteLine("\n\n 输入\"Q\"键退出。");
ConsoleKey key;
Generated by Foxit PDF Creator © Foxit Software
http://www.foxitsoftware.com For evaluation only.

do {
key = Console.ReadKey(true).Key;
} while (key != ConsoleKey.Q);
}
}
// 获得 IPAddress 对象的另外几种常用方法:
IPAddress ip = IPAddress.Parse("127.0.0.1");
IPAddress ip = Dns.GetHostEntry("localhost").AddressList[0];
上面的代码中,我们开启了对 8500 端口的侦听。在运行了上面的程序之后,然后打开“命
令提示符”,输入“netstat-a”,可以看到计算机器中所有打开的端口的状态。可以从中找到
8500 端口,看到它的状态是 LISTENING,这说明它已经开始了侦听:
TCP jimmy:1030 0.0.0.0:0 LISTENING
TCP jimmy:3603 0.0.0.0:0 LISTENING
TCP jimmy:8500 0.0.0.0:0 LISTENING
TCP jimmy:netbios-ssn 0.0.0.0:0 LISTENING
在打开了对端口的侦听以后,服务端必须通过某种方式进行阻塞(比如 Console.ReadKey()),
使得程序不能够因为运行结束而退出。否则就无法使用“netstat -a”看到端口的连接状态,
因为程序已经退出,连接会自然中断,再运行“netstat -a”当然就不会显示端口了。所以程
序最后按“Q”退出那段代码是必要的,下面的每段程序都会含有这个代码段,但为了节省
空间,我都省略掉了。
2.2多个客户端与服务端连接
那么既然一个服务器端口可以应对多个客户端连接,那么接下来我们就看一下,如何让多个
客户端与服务端连接。如同我们上面所说的,一个 TcpClient 就是一个 Socket,所以我们只
要创建多个 TcpClient,然后再调用 Connect()方法就可以了:
class Client {
static void Main(string[] args) {
Console.WriteLine("Client Running ...");
TcpClient client;
for (int i = 0; i <= 2; i++) {
try {
client = new TcpClient();
Generated by Foxit PDF Creator © Foxit Software
http://www.foxitsoftware.com For evaluation only.
剩余67页未读,继续阅读



















y_ch_d
- 粉丝: 0
- 资源: 4
上传资源 快速赚钱
我的内容管理 展开
我的资源 快来上传第一个资源
我的收益
登录查看自己的收益我的积分 登录查看自己的积分
我的C币 登录后查看C币余额
我的收藏
我的下载
下载帮助


安全验证
文档复制为VIP权益,开通VIP直接复制

评论0