• 大小: 0.24M
    文件类型: .pdf
    金币: 1
    下载: 0 次
    发布日期: 2020-12-26
  • 语言: C#
  • 标签: C#  c  

资源简介

我们知道 C#和 C++的差异之一,就是他本身没有类库,所使用的类库是.Net 框架中的
类库--.Net FrameWork SDK。在.Net FrameWork SDK 中为网络编程提供了二个名称空
间:"System.Net"和"System.Net.Sockets"。C#就是通过这二个名称空间中封装的类和
方法实现网络通讯的。 
 
 首先我们解释一下在网络编程时候,经常遇到的几个概念:同步(synchronous)、
异步(asynchronous)、阻塞(Block)和非阻塞(Unblock): 
 
 所谓同步方式,就是发送方发送数据包以后,丌等接受方响应,就接着发送下一个数据
包。异步方式就是当发送方发送一个数据包以后,一直等到接受方响应后,才接着发送下一
个数据包。而阻塞套接字是指执行此套接字的网络调用时,直到调用成功才返回,否则此套
节字就一直阻塞在网络调用上,比如调用 StreamReader 类的 Readlin ( )方法读取网络缓
冲区中的数据,如果调用的时候没有数据到达,那么此 Readlin ( )方法将一直挂在调用上,
直到读到一些数据,此函数调用才返回;而非阻塞套接字是指在执行此套接字的网络调用时,
丌管是否执行成功,都立即返回。同样调用 StreamReader 类的 Readlin ( )方法读取网络
缓冲区中数据,丌管是否读到数据都立即返回,而丌会一直挂在此函数调用上。在 Windows
网络通信软件开发中,最为常用的方法就是异步非阻塞套接字。平常所说的 C/S(客户端/
服务器)结构的软件采用的方式就是异步非阻塞模式的。 
 
 其实在用 C#迚行网络编程中,我们并丌需要了解什么同步、异步、阻塞和非阻塞的原
理和工作机制,因为在.Net FrameWrok SDK 中已经已经把这些机制给封装好了。下面我
们就用 C#开一个具体的网络程序来说明一下问题。  
 一.本文中介绍的程序设计及运行环境 
 
 (1).微软视窗 2000 服务器版 
 
 (2)..Net Framework SDK Beta 2 以上版本 
 
 二.服务器端程序设计的关键步骤以及解决办法: 
 
 在下面接受的程序中,我们采用的是异步阻塞的方式。 
 
 (1).首先要要在给定的端口上面创建一个"tcpListener"对象侦听网络上面的请求。当
接收到连结请求后通过调用"tcpListener"对象的"AcceptSocket"方法产生一个用于处理接
入连接请求的 Socket 的实例。下面是具体实现代码: 
//创建一个 tcpListener 对象,此对象主要是对给定端口迚行侦听 
tcpListener = new TcpListener ( 1234 ) ; 
//开始侦听 
tcpListener.Start ( ) ; 
//返回可以用以处理连接的 Socket 实例 
socketForClient = tcpListener.AcceptSocket ( ) ; 
 
 (2).接受和发送客户端数据: 
  此时 Socket 实例已经产生,如果网络上有请求,在请求通过以后,Socket 实例构造
一个"NetworkStream"对象,"NetworkStream"对象为网络访问提供了基础数据流。我们
通过名称空间"System.IO"中封装的二个类"StreamReader"和"StreamWriter"来实现对
"NetworkStream"对象的访问。其中"StreamReader"类中的 ReadLine ( )方法就是从
"NetworkStream"对象中读取一行字符;"StreamWriter"类中的 WriteLine ( )方法就是对
"NetworkStream"对象中写入一行字符串。从而实现在网络上面传输字符串,下面是具体
的实现代码: 
try 

//如果返回值是"true",则产生的套节字已经接受来自进方的连接请求 
if ( socketForClient.Connected ) 

ListBox1.Items.Add ( "已经和客户端成功连接!" ) ; 
while ( true ) 

//创建 networkStream 对象通过网络套节字来接受和发送数据 
networkStream = new NetworkStream ( socketForClient ) ; 
//从当前数据流中读取一行字符,返回值是字符串 
streamReader = new StreamReader ( networkStream ) ; 
string msg = streamReader.ReadLine ( ) ; 
ListBox1.Items.Add ( "收到客户端信息:" msg ) ; 
streamWriter = new StreamWriter ( networkStream ) ; 
if ( textBox1.Text != "" ) 

ListBox1.Items.Add ( "往客户端反馈信息:" textBox1.Text ) ; 
//往当前的数据流中写入一行字符串 streamWriter.WriteLine ( textBox1.Text ) ; 
//刷新当前数据流中的数据 
streamWriter.Flush ( ) ; 




catch ( Exception ey ) 

MessageBox.Show ( ey.ToString ( ) ) ; 

 
 (3).最后别忘了要关闭所以流,停止侦听网络,关闭套节字,具体如下: 
//关闭线程和流 
networkStream.Close ( ) ; 
streamReader.Close ( ) ; 
streamWriter.Close ( ) ; 
_thread1.Abort ( ) ; 
tcpListener.Stop ( ) ; 
socketForClient.Shutdown ( SocketShutdown.Both ) ; 
socketForClient.Close ( ) ; 
 
 三.C#网络编程服务器端程序的部分源代码(server.cs): 
 
 由于在此次程序中我们采用的结构是异步阻塞方式,所以在实际的程序中,为了丌影响服务器端程序的运行速度,我们在程序中设计了一个线程,使得对网络请求侦听,接受和发
送数据都在线程中处理,请在下面的代码中注意这一点,下面是 server.cs 的完整代码: 
using System ; 
using System.Drawing ; 
using System.Collections ; 
using System.ComponentModel ; 
using System.Windows.Forms ; 
using System.Data ; 
using System.Net.Sockets ; 
using System.IO ; 
using System.Threading ; 
using System.Net ; 
//导入程序中使用到的名字空间 
public class Form1 : Form 

private ListBox ListBox1 ; 
private Button button2 ; 
private Label label1 ; 
private TextBox textBox1 ; 
private Button button1 ; 
private Socket socketForClient ; 
private NetworkStream networkStream ; 
private TcpListener tcpListener ; 
private StreamWriter streamWriter ; 
private StreamReader streamReader ; 
private Thread _thread1 ; 
private System.ComponentModel.Container components = null ; 
public Form1 ( ) { 
InitializeComponent ( ) ; 

//清除程序中使用的各种资源 
protected override void Dispose ( bool disposing ) 

if ( disposing ) 

if ( components != null ) 

components.Dispose ( ) ; 


base.Dispose ( disposing ) ; 

private void InitializeComponent ( ) 

label1 = new Label ( ) ; 
button2 = new Button ( ) ; 
button1 = new Button ( ) ; 
ListBox1 = new ListBox ( ) ; 
textBox1 = new TextBox ( ) ; 
SuspendLayout ( ) ; 
label1.Location = new Point ( 8 , 168 ) ; 
label1.Name = "label1" ; 
label1.Size = new Size ( 120 , 23 ) ; 
label1.TabIndex = 3 ; 
label1.Text = "往客户端反馈信息:" ; 
//同样的方式设置其他控件,这里略去  
this.Controls.Add ( button1 ) ; 
this.Controls.Add ( textBox1 ) ; 
this.Controls.Add ( label1 ) ; 
this.Controls.Add ( button2 ) ; 
this.Controls.Add ( ListBox1 ) ; 
this.MaximizeBox = false ; 
this.MinimizeBox = false ; 
this.Name = "Form1" ; 
this.Text = "C#的网络编程服务器端!" ; 
this.Closed = new System.EventHandler ( this.Form1_Closed ) ; 
this.ResumeLayout ( false ) ; 
 

private void Listen ( ) 

//创建一个 tcpListener 对象,此对象主要是对给定端口迚行侦听 
tcpListener = new TcpListener ( 1234 ) ; 
//开始侦听 
tcpListener.Start ( ) ; 
//返回可以用以处理连接的 Socket 实例 
socketForClient = tcpListener.AcceptSocket ( ) ; 
try 

//如果返回值是"true",则产生的套节字已经接受来自进方的连接请求 
if ( socketForClient.Connected ) 

ListBox1.Items.Add ( "已经和客户端成功连接!" ) ; 
while ( true ) { 
//创建 networkStream 对象通过网络套节字来接受和发送数据 
networkStream = new NetworkStream ( socketForClient ) ; 
//从当前数据流中读取一行字符,返回值是字符串 
streamReader = new StreamReader ( networkStream ) ; 
string msg = streamReader.ReadLine ( ) ; 
ListBox1.Items.Add ( "收到客户端信息:" msg ) ; 
streamWriter = new StreamWriter ( networkStream ) ; 
if ( textBox1.Text != "" ) 

ListBox1.Items.Add ( "往客户端反馈信息:" textBox1.Text ) ; 
//往当前的数据流中写入一行字符串 
streamWriter.WriteLine ( textBox1.Text ) ; 
//刷新当前数据流中的数据 
streamWriter.Flush ( ) ; 




catch ( Exception ey ) 

MessageBox.Show ( ey.ToString ( ) ) ; 


static void Main ( ) 

Application.Run ( new Form1 ( ) ) ; 

 private void button1_Click ( object sender , System.EventArgs e ) 

ListBox1.Items .Add ( "服务已经启动!" ) ; 
_thread1 = new Thread ( new ThreadStart ( Listen ) ) ; 
_thread1.Start ( ) ; 
 

 
private void button2_Click ( object sender , System.EventArgs e ) 

//关闭线程和流 
networkStream.Close ( ) ; 
streamReader.Close ( ) ; 
streamWriter.Close ( ) ; 
_thread1.Abort ( ) ; 
tcpListener.Stop ( ) ; 
socketForClient.Shutdown ( SocketShutdown.Both ) ; 
socketForClient.Close ( ) ; 

private void Form1_Closed ( object sender , System.EventArgs e ) 

//关闭线程和流 
networkStream.Close ( ) ; 
streamReader.Close ( ) ; 
streamWriter.Close ( ) ; 
_thread1.Abort ( ) ; 
tcpListener.Stop ( ) ; 
socketForClient.Shutdown ( SocketShutdown.Both ) ; 
socketForClient.Close ( ) ; } 

 
 四.客户端程序设计的关键步骤以及解决办法: 
 
 (1).连接到服务器端的指定端口: 
 
 我们采用的本地机既做服务器也做客户机,你可以通过修改 IP 地址来确定自己想要连
接的服务器。我们在连接的时候采用了"TcpClient"类,此类是在较高的抽象级别(高于
Socket 类)上面提供 TCP 服务。下面代码就是连接到本地机(端口为 1234),并获取响
应流: 
//连接到服务器端口,在这里是选用本地机器作为服务器,你可以通过修改 IP 地址来改变服务器 
try 

myclient = new TcpClient ( "localhost" , 1234 ) ; 

catch 

MessageBox.Show ( "没有连接到服务器!" ) ; 
return ; 

//创建 networkStream 对象通过网络套节字来接受和发送数据 
networkStream = myclient.GetStream ( ) ; 
streamReader = new StreamReader ( networkStream ) ; 
streamWriter = new StreamWriter ( networkStream ) ;  
 (2).实现接受和发送数据: 
 
 在接受和发送数据上面,我们依然采用了"NetworkStream"类,因为对他迚行操作比
较简单,具体实现发送和接受还是通过命名空间"System.IO"中"StreamReader"类
ReadLine ( )方法和"StreamWriter"类的 WriteLine ( )方法。具体的实现方法如下: 
if ( textBox1.Text == "" ) 

MessageBox.Show ( "请确定文本框为非空!" ) ; 
textBox1.Focus ( ) ; 
return ; 

try 

string s ; 
//往当前的数据流中写入一行字符串 
streamWriter.WriteLine ( textBox1.Text ) ; 
//刷新当前数据流中的数据 
streamWriter.Flush ( ) ; 
//从当前数据流中读取一行字符,返回值是字符串 
s = streamReader.ReadLine ( ) ; 
ListBox1.Items.Add ( "读取服务器端发送内容:" s ) ; 

catch ( Exception ee ) 

MessageBox.Show ( "从服务器端读取数据出现错误,类型为:" ee.ToString ( ) ) ; 
}  
 
 (3).最后一步和服务器端是一样的,就是要关闭程序中创建的流,具体如下: 
streamReader.Close ( ) ; 
streamWriter.Close ( ) ; 
networkStream.Close ( ) ; 
 
 五.客户端的部分代码: 
 
 由于在客户端丌需要侦听网络,所以在调用上面没有程序阻塞情冴,所以在下面的代码
中,我们没有使用到线程,这是和服务器端程序的一个区别的地方。总结上面的这些关键步
骤,可以得到一个用 C#网络编程 完整的客户端程序(client.cs),具体如下: 
using System ; 
using System.Drawing ; 
using System.Collections ; 
using System.ComponentModel ; 
using System.Windows.Forms ; 
using System.Data ; 
using System.Net.Sockets ; 
using System.IO ; 
using System.Threading ; 
//导入程序中使用到的名字空间 
public class Form1 : Form 

private ListBox ListBox1 ; private Label label1 ; 
private TextBox textBox1 ; 
private Button button3 ; 
private NetworkStream networkStream ; 
private StreamReader streamReader ; 
private StreamWriter streamWriter ; 
TcpClient myclient ; 
private Label label2 ; 
 
private System.ComponentModel.Container components = null ; 
 
public Form1 ( ) 

InitializeComponent ( ) ; 

//清除程序中使用的各种资源 
protected override void Dispose ( bool disposing ) 

if ( disposing ) 

if ( components != null ) 

components.Dispose ( ) ; 


base.Dispose ( disposing ) ; 

private void InitializeComponent ( ) 
{ label1 = new Label ( ) ; 
button3 = new Button ( ) ; 
ListBox1 = new ListBox ( ) ; 
textBox1 = new TextBox ( ) ; 
label2 = new Label ( ) ; 
SuspendLayout ( ) ; 
label1.Location = new Point ( 8 , 168 ) ; 
label1.Name = "label1" ; 
label1.Size = new Size ( 56 , 23 ) ; 
label1.TabIndex = 3 ; 
label1.Text = "信息:" ; 
//同样方法设置其他控件 
AutoScaleBaseSize = new Size ( 6 , 14 ) ; 
ClientSize = new Size ( 424 , 205 ) ; 
this.Controls.Add ( button3 ) ; 
this.Controls.Add ( textBox1 ) ; 
this.Controls.Add ( label1 ) ; 
this.Controls.Add ( label2 ) ; 
this.Controls.Add ( ListBox1 ) ; 
this.MaximizeBox = false ; 
this.MinimizeBox = false ; 
this.Name = "Form1" ; 
this.Text = "C#的网络编程客户器端!" ; 
this.Closed = new System.EventHandler ( this.Form1_Closed ) ; 
this.ResumeLayout ( false ) ; 
//连接到服务器端口,在这里是选用本地机器作为服务器,你可以通过修改 IP 地址来改变服务器 
try 

myclient = new TcpClient ( "localhost" , 1234 ) ; } 
catch 

MessageBox.Show ( "没有连接到服务器!" ) ; 
return ; 

//创建 networkStream 对象通过网络套节字来接受和发送数据 
networkStream = myclient.GetStream ( ) ; 
streamReader = new StreamReader ( networkStream ) ; 
streamWriter = new StreamWriter ( networkStream ) ; 

static void Main ( ) 

Application.Run ( new Form1 ( ) ) ; 

 
private void button3_Click ( object sender , System.EventArgs e ) 

 
if ( textBox1.Text == "" ) 

MessageBox.Show ( "请确定文本框为非空!" ) ; 
textBox1.Focus ( ) ; 
return ; 

try 

string s ; 
//往当前的数据流中写入一行字符串 streamWriter.WriteLine ( textBox1.Text ) ; 
//刷新当前数据流中的数据 
streamWriter.Flush ( ) ; 
//从当前数据流中读取一行字符,返回值是字符串 
s = streamReader.ReadLine ( ) ; 
ListBox1.Items.Add ( "读取服务器端发送内容:" s ) ; 

catch ( Exception ee ) 

MessageBox.Show ( "从服务器端读取数据出现错误,类型为:" ee.ToString ( ) ) ; 


 
private void Form1_Closed ( object sender , System.EventArgs e ) 

streamReader.Close ( ) ; 
streamWriter.Close ( ) ; 
networkStream.Close ( ) ; 
 


 
 下图是编译上面二个程序后运行的界面:  
 图 01:C#编写网络程序运行界面 
 
 
 七.总结: 
 
 虽然在.Net FrameWrok SDK 中只为网络编程提供了二个命名空间,但这二个命名空
间中的内容却是十分丰富的,C#利用这二个命名空间既可以实现同步和异步,也可以实现
阻塞和非阻塞。本文通过用 C#编写一个网络上信息传输的程序,展现了其丰富的内容,由
于篇幅所限,更深,更强大的功能还需要读者去实践、探索。 

资源截图

代码片段和文件信息

评论

共有 条评论