C#网络编程初探

2016-02-19 16:58 6 1 收藏

下面请跟着图老师小编一起来了解下C#网络编程初探,精心挑选的内容希望大家喜欢,不要忘记点个赞哦!

【 tulaoshi.com - 编程语言 】

  我们知道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(客户端/服务器)结构的软件采用的方式就是异步非阻塞模式的。

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  其实在用C#进行网络编程中,我们并不需要了解什么同步、异步、阻塞和非阻塞的原理和工作机制,因为在.Net FrameWrok SDK中已经已经把这些机制给封装好了。下面我们就用C#开一个具体的网络程序来说明一下问题。

  一.本文中介绍的程序设计及运行环境

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  (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#编写一个网络上信息传输的程序,展现了其丰富的内容,由于篇幅所限,更深,更强大的功能还需要读者去实践、探索。

来源:https://www.tulaoshi.com/n/20160219/1613712.html

延伸阅读
本文给出一个用 C# 编程实现 读写 Binary 的实例代码,对于初学者来说是个不可多得的参考性文章…… 以下是引用片段: //返回blob数据 public MemoryStream getBlob(string SQL) ...{ try ...{ Db_Conn(); cmd = new OleDbCommand(SQL, Conn); cmd.Comma...
程序的活动是通过语句(statement)来表达的。C#支持几种不同的语句,许多语句是以嵌入语句的形式定义的。 块(block)允许在只能使用单个语句的上下文中编写多个语句。块由一个括在大括号{}内的语句列表组成。 声明语句(declaration statement)用于声明局部变量和常量。 表达式语句(expression statement)用于运算表达...
介绍 这是C/C++程序迷们经常谈论的一个话题,同时也是一个复杂的、难以理解的话题-指针!每次谈到C#,大多数我遇到的人都持这样的观点-C#中没有指针的概念。而实际上,它已经被废除了,取而代之的是C#中的非安全编程-如何在程序中使用指针。不同于其字面意思的是,使用指针编程并没有什么不安全的。 它如此受关注的根本原因是...
C#是纯粹的面向对象编程语言,它真正体现了一切皆为对象的精神。在C#中,即使是最基本的数据类型,如int,double,bool类型,都属于System.Object类型。此外,使用C#编程,不会存在与游离于对象之外的属于过程的东西。因此,学习C#,就必须具有面向对象思想,不明白所谓的面向对象思想,就不可能掌握C#的精髓,而对于C#的理解,就只能仅限...
类(class)是C#类型中最基础的类型。类是一个数据结构,将状态(字段)和行为(方法和其他函数成员)组合在一个单元中。类提供了用于动态创建类实例的定义,也就是对象(object)。类支持继承(inheritance)和多态(polymorphism),即派生类能够扩展和特殊化基类的机制。使用类声明可以创建新的类。类声明以一个声明头开始,其组成方式如...

经验教程

610

收藏

85
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部