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

转载于:https://www.cnblogs.com/hsapphire/archive/2010/01/08/1641930.html

更多相关:

  • 菜鸟一枚,正在学习C++ Gui Qt4,整理很零碎,欢迎批评指正   1.窗口标题: QWidget *window = new QWidget; window->setWindowTitle("Enter Your Age"); **************************************** 关于标题...

  • 将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例: 输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4 总体思路是: 比较两个链表头节点,较小的插入新链表指针之后,同时较小链表指针向后移动一位 实现如下: ListNode* mergeTwo...

  • 1.直接调用微软socket对象处理 static void Main(string[] args){try{IPAddress ip = new IPAddress(new byte[] { 127, 0, 0, 1 });//在3721端口新建一个TcpListener对象TcpListener listener = new...

  •   现在很多地方都会用到zookeeper, 用到它的地方就是为了实现分布式。用到的场景就是服务注册,比如一个集群服务器,需要知道哪些服务器在线,哪些服务器不在线。   ZK有一个功能,就是创建临时节点,当机器启动应用的时候就会连接到一个ZK节点,然后创建一个临时节点,那么通过获取监听该路径,并且获取该路径下的节点数量就知道有哪些服务...

  • 前台到后台java时data日期类型的转化 在实体类中用@DataTimeFormat,这样设置即使传过来是空的字符串也是可以转的,要和前面传过来的格式一致,如 @XmlElement(name="BeginDate") @DateTimeFormat(pattern="yyyy-MM-dd") private Date begin...

  • Socket基本编程 服务端: using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; Thread mythread ; Socket socket; // 清理所有正在使用的资源。 protected overri...

  • 第一个测试的主要代码: private function closureTest():void{this.addEventListener(Event.ENTER_FRAME, onEnter);}private var num:int = 1000;private function onEnter(e:Event):void{fo...

  • 本文是西门子开放式TCP通信的第2篇,上一篇我们讲了使用西门子1200PLC作为TCP服务器的程序编写,可以点击下方链接阅读:【公众号dotNet工控上位机:thinger_swj】基于Socket访问西门子PLC系列教程(一)在完成上述步骤后,接下来就是编写上位机软件与PLC之间进行通信。上位机UI界面设计如下图所示:从上图可以看出...

  • 我有一个大型数据集,列出了在全国不同地区销售的竞争对手产品。我希望通过使用这些新数据帧名称中的列值的迭代过程,根据区域将该数据帧分成几个其他区域,以便我可以分别处理每个数据帧-例如根据价格对每个地区的信息进行排序,以了解每个地区的市场情况。我给出了以下数据的简化版本:Competitor Region ProductA Product...

  • 作为一名IT从业者,我来回答一下这个问题。首先,对于具有Java编程基础的人来说,学习Python的初期并不会遇到太大的障碍,但是要结合自己的发展规划来制定学习规划,尤其要重视学习方向的选择。Java与Python都是比较典型的全场景编程语言,相比于Java语言来说,当前Python语言在大数据、人工智能领域的应用更为广泛一些,而且大...

  • 这段时间通过学习相关的知识,最大的变化就是看待事物更加喜欢去了解事物后面的本质,碰到问题后解决问题思路也发生了改变。举个具体的例子,我在学习数据分析,将来会考虑从事这方面的工作,需要掌握的相关专业知识这个问题暂且按下不表,那哪些具体的问题是我需要了解的呢,以下简单罗列:1、了解数据分析师这个岗位在各个地区的需求情况?2、数据分析师的薪...

  • 这一节将开始学习python的一个核心数据分析支持库---pandas,它是python数据分析实践与实战的必备高级工具。对于使用 Python 进行数据分析来说,pandas 几乎是无人不知,无人不晓的。今天,我们就来认识认识数据分析界鼎鼎大名的 pandas。目录一. pandas主要数据结构 SeriesDataFrame二...