惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
月光博客
月光博客
The Last Watchdog
The Last Watchdog
T
Tenable Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
Simon Willison's Weblog
Simon Willison's Weblog
V
Vulnerabilities – Threatpost
F
Fortinet All Blogs
Microsoft Security Blog
Microsoft Security Blog
A
Arctic Wolf
云风的 BLOG
云风的 BLOG
Know Your Adversary
Know Your Adversary
P
Palo Alto Networks Blog
GbyAI
GbyAI
阮一峰的网络日志
阮一峰的网络日志
The GitHub Blog
The GitHub Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
U
Unit 42
MyScale Blog
MyScale Blog
B
Blog
Spread Privacy
Spread Privacy
S
Schneier on Security
Project Zero
Project Zero
L
LINUX DO - 热门话题
M
MIT News - Artificial intelligence
F
Full Disclosure
WordPress大学
WordPress大学
Apple Machine Learning Research
Apple Machine Learning Research
Cyberwarzone
Cyberwarzone
AWS News Blog
AWS News Blog
aimingoo的专栏
aimingoo的专栏
博客园 - 三生石上(FineUI控件)
C
Cybersecurity and Infrastructure Security Agency CISA
Hugging Face - Blog
Hugging Face - Blog
Security Latest
Security Latest
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Tailwind CSS Blog
K
Kaspersky official blog
Recent Announcements
Recent Announcements
NISL@THU
NISL@THU
Cisco Talos Blog
Cisco Talos Blog
S
Securelist
P
Privacy & Cybersecurity Law Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
The Exploit Database - CXSecurity.com
V
Visual Studio Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Webroot Blog
Webroot Blog

博客园 - somesongs

好多年没回到这个园子 有趣的多媒体 发布简短计划,备忘 Intelligencia.UrlRewriter, 解决404找不到文件的问题 forms验证如此简单 在一个页面中调用另一个页面定义的函数 .net中,数据提交完毕后,刷新绑定控件,清空输入框的好办法,就是在时间函数的最后加入Response.Redirect(Request.FilePath); [转] Trace跟踪输出进行调试 Flash的全屏播放(转) flash中实现拖拽 Treeview中,递归生成从当前选中节点到根节点的全路径 .net从后台返回js的提示框 编写xmlhelper类【翻译】 [转]C#网络编程概述 只需一行代码实现增删查改,微软已经让我们很简单。谈AccessDataSource的使用。 document.body.clientHeight与document.documentElement.clientHeight 动态将Js代码写入到Head标签中 Asp.net中删除前的提示信息 location和history的详细设置
【转】C#网络编程初探
somesongs · 2008-05-11 · via 博客园 - somesongs

我们知道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#开一个具体的网络程序来说明一下问题。

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

(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 ( ) ;
//从当前数据流中读取一行字符,返回值是字符串
= 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 ( ) ;
//从当前数据流中读取一行字符,返回值是字符串
= 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#编写一个网络上信息传输的程序,展现了其丰富的内容,由于篇幅所限,更深,更强大的功能还需要读者去实践、探索。

-------------
遗憾:没有找到最终出处,仍向作者致敬。