20170814-20170820C#工作学习周总结

来源:互联网 发布:寻侠龙灵突破数据 编辑:程序博客网 时间:2024/06/06 20:12

20170807-20170813C#工作学习周总结

Log4Net框架概述

记录日志的必要性

程序一旦部署后,就不大可能再利用专门的调试工具了。日志记录往往是软件开发周期中重要的组成部分,可供开发人员尽快找到应用程序中的bug,一旦在程序中加入了Log输出,程序在运行过程中就能生成并输出日志信息而无需人工干预。

结构

Log4Net四种主要的组件,分别是:

Logger(记录器)

Repository(库)

Appender(附着器)

Layout(布局)

日志的级别

级别 允许的方法 Boolean**属性** 优先级别 OFF Highest FATAL void Fatal(…); bool IsFatalEnabled; RROR void Error(…); bool IsErrorEnabled; WARN void Warn(…); bool IsWarnEnabled; INFO void Info(…); bool IsInfoEnabled; DEBUG void Debug(…); bool IsDebugEnabled; ALL Lowest

在log4net框架里,通过设置配置文件,每个日志对象都被分配了一个日志优先级别。如果没有给一个日志对象显式地分配一个级别,那么该对象会试图从他的祖先继承一个级别值。

举例说明,当你创建了一个日志对象,并且把他的级别设置为INFO。于是框架会设置日志的每个Boolean属性。当你调用相应的日志方法时,框架会检查相应的Boolean属性,以决定该方法能不能执行。如下的代码:

Logger.Info("message");Logger.Debug("message");Logger.Warn("message");

对于第一种方法,Info()的级别等与日志的级别(INFO),因此日志请求会被传递,我们可以得到输出结果”message”。

对于第二种方法,Debug()的级别低于日志对象logger的日志级别(INFO),因此,日志请求被拒绝了,我们得不到任何输出。同样的,针对第三行语句,我们可以很容易得出结论。

在上表中有两个特殊的级别:ALL和OFF。ALL表示允许所有的日志请求。OFF是拒绝所有的请求。

Repository

Repository主要用于负责日志对象组织结构的维护。如果你是个log4net框架的使用者,而非扩展者,那么你几乎不会在你的代码里用到Repository的类。相反的,你需要用到LogManager类来自动管理库和日志对象。

Appender

一个好的日志框架应该能够产生多目的地的输出。比如说输出到控制台或保存到一个日志文件log4net 能够很好的满足这些要求。它使用一个叫做Appender的组件来定义输出介质。

Layout 组件用于向用户显示最后经过格式化的输出信息。

在程序中使用Log4Net

定义配置文件

在开始对你的程序进行日志记录前,需要先启动log4net引擎。这意味着你需要先配置前面提到的三种组件。你可以用两种方法来设定配置:在单独的文件中设定配置或在代码中定义配置。

使用配置文件

当我们创建了上面的配置文件后,我们接下来需要把它和我们的应用联系起来。缺省的,每个独立的可执行程序集都会定义它自己的配置。log4net框架使用 log4net.Config.DOMConfiguratorAttribute在程序集的级别上定义配置文件。

简而言之

使用log不过是记录程序当前的运行状态,所以,大概分为以下几个步骤:

1、NuGet包管理器去找log4Net;

2、配置log4Net.config,也可以直接配在App.config里面

3、在需要使用log输出的命名空间下加入:

[assembly: log4net.Config.XmlConfigurator(ConfigFile="Log4Net.config", Watch=true)]

4、cs文件中对log4Net的不同等级的引用


Control.DataBinding数据绑定

引:

数据绑定即控件与数据表、行之间的自动同步。怎么理解自动同步呢?首先聊聊手动同步,比如,Form1上有一个TextBox1的控件,我要给TextBox1赋值,一般的做法即让TextBox1的Text属性等于你所要赋的值。当我想取出TextBox1中的值时,应该声明一个字符串类型的变量来接收Text值,而当我再改变TextBox1的值时,string的值不会跟着变,我必须重新取出TextBox1的值给string才可以。那么有什么办法可以不重复操作而达到这样的效果呢?这个便用到C#中Control.DataBinding类。它能够自动刷新数据。

DataBindings

WinForm中的很多控件,如Label、TextBox等都包含DataBindings属性,其类型为ControlBindingsCollection,是Binding类的集合。Binding类代表某对象属性值和某控件属性值之间的简单绑定。

DataBindings属性是很多控件都有的属性,作用有2方面。一方面是用于与数据库的数据进行绑定,进行数据显示。另一方面用于与控件或类的对象进行数据绑定。

DataBindings的一个常用的方法:

 /************************************************  * 第一个值:要绑定到TextBox的什么地方  * 第二个值:数据源  * 第三个值:数据源的什么属性,即下例中,"Value"是trackBar1的属性  * 第四个值:是否开启数据格式化  * 第五个值:在什么时候启用数据源绑定——可以保证实时更新数据  * *********************************************/  textBox1.DataBindings.Add("Text", trackBar1, "Value", false, DataSourceUpdateMode.OnPropertyChanged);

也可以将自己的类作为数据源,获取类中的属性。

MSDN中的类似一个例子:

//将某个对象的某个属性与指定对象的指定属性进行关联,如下所示。textbox1.DataBindings.Add(new Binding("Text", Label1, "Name"));//将Label1.Name属性与textbox1进行绑定。

中提到的例子,这里需要实现INotifyPropertyChanged接口,通知属性值发生变化,进一步通知DataBindings实时更新TextBox1的显示。

using System;using System.ComponentModel;using System.Windows.Forms;namespace samplesForUpdateBindings{    public partial class Form1 : Form,INotifyPropertyChanged    {        private string _s;        public string S        {            get { return _s; }            set            {                if (_s != value)                {                     _s = value;                    FirePropertyChanged("S"); }            } //<--通知属性更改,相关控件可以更新显示        }        public Form1()        {            InitializeComponent();            this.S = "start";            textBox1.DataBindings.Add("Text", this, "S", false, DataSourceUpdateMode.OnPropertyChanged);        }        #region INotifyPropertyChanged Members        public event PropertyChangedEventHandler PropertyChanged;        void FirePropertyChanged(string propertyName)        {            PropertyChangedEventHandler propertyChanged = this.PropertyChanged;            if (propertyChanged != null) propertyChanged(this, new PropertyChangedEventArgs(propertyName));        }        #endregion        private void button1_Click(object sender, EventArgs e)        {            this.S = "end";        }    }}

如果不实现INotifyPropertyChanged接口,DataBindings便会失效。

另外,用BindingList替换List可以通知到控件:数据已经更改。以下的两个例子:

//From: MSDN Examplesusing System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Runtime.CompilerServices;using System.Text;using System.Threading.Tasks;using System.Windows.Forms;namespace MsSample{    public partial class Form1 : Form    {        // This button causes the value of a list element to be changed.          private Button changeItemBtn = new Button();        // This DataGridView control displays the contents of the list.          private DataGridView customersDataGridView = new DataGridView();        // This BindingSource binds the list to the DataGridView control.          private BindingSource customersBindingSource = new BindingSource();        public Form1()        {            InitializeComponent();            // Set up the "Change Item" button.              this.changeItemBtn.Text = "Change Item";            this.changeItemBtn.Dock = DockStyle.Bottom;            this.changeItemBtn.Click +=                new EventHandler(changeItemBtn_Click);            this.Controls.Add(this.changeItemBtn);            // Set up the DataGridView.              customersDataGridView.Dock = DockStyle.Top;            this.Controls.Add(customersDataGridView);            this.Size = new Size(400, 200);        }        private void Form1_Load(object sender, EventArgs e)        {            // Create and populate the list of DemoCustomer objects              // which will supply data to the DataGridView.              BindingList<DemoCustomer> customerList = new BindingList<DemoCustomer>();            customerList.Add(DemoCustomer.CreateNewCustomer());            customerList.Add(DemoCustomer.CreateNewCustomer());            customerList.Add(DemoCustomer.CreateNewCustomer());            // Bind the list to the BindingSource.              this.customersBindingSource.DataSource = customerList;            // Attach the BindingSource to the DataGridView.              this.customersDataGridView.DataSource =                this.customersBindingSource;        }        // Change the value of the CompanyName property for the first           // item in the list when the "Change Item" button is clicked.          void changeItemBtn_Click(object sender, EventArgs e)        {            // Get a reference to the list from the BindingSource.              BindingList<DemoCustomer> customerList =                this.customersBindingSource.DataSource as BindingList<DemoCustomer>;            // Change the value of the CompanyName property for the               // first item in the list.              customerList[0].CustomerName = "Tailspin Toys";            customerList[0].PhoneNumber = "(708)555-0150";        }    }    // This is a simple customer class that       // implements the IPropertyChange interface.      public class DemoCustomer : INotifyPropertyChanged    {        // These fields hold the values for the public properties.          private Guid idValue = Guid.NewGuid();        private string customerNameValue = String.Empty;        private string phoneNumberValue = String.Empty;        public event PropertyChangedEventHandler PropertyChanged;        // This method is called by the Set accessor of each property.          // The CallerMemberName attribute that is applied to the optional propertyName          // parameter causes the property name of the caller to be substituted as an argument.          private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")        {            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));        }        // The constructor is private to enforce the factory pattern.          private DemoCustomer()        {            customerNameValue = "Customer";            phoneNumberValue = "(312)555-0100";        }        // This is the public factory method.          public static DemoCustomer CreateNewCustomer()        {            return new DemoCustomer();        }        // This property represents an ID, suitable          // for use as a primary key in a database.          public Guid ID        {            get            {                return this.idValue;            }        }        public string CustomerName        {            get            {                return this.customerNameValue;            }            set            {                if (value != this.customerNameValue)                {                    this.customerNameValue = value;                    NotifyPropertyChanged();                }            }        }        public string PhoneNumber        {            get            {                return this.phoneNumberValue;            }            set            {                if (value != this.phoneNumberValue)                {                    this.phoneNumberValue = value;                    NotifyPropertyChanged();                }            }        }    }}

也可以用以下方式直接更新:

 public BindingList<BlogNew> blogNewsRegardUI {get;set; }  //应用于DataGridView界面UI更新  private void mainFrm_Load(object sender, EventArgs e) {       blogNewsRegardUI = new BindingList<BlogNew>();       blogNewsRegardUI.Add(new BlogNew { BlogID = 11, BlogTitle = "僵卧孤村不自哀" });       blogNewsRegardUI.Add(new BlogNew { BlogID = 12, BlogTitle = "尚思为国戍轮台" });       blogNewsRegardUI.Add(new BlogNew { BlogID = 13, BlogTitle = "夜阑卧听风吹雨" });       dataGridView2.DataBindings.Add("DataSource", this, "blogNewsRegardUI", false, DataSourceUpdateMode.OnPropertyChanged);  }  private void button4_Click(object sender, EventArgs e)  {       /*这里主要用来解决DataGridView1界面不更新的问题,其实原因在于使用了List<BlogNew>,这里我们采用BindList<BlogNew>        *通过测试,我们发现,只要数据源改变,界面就可以自动的进行更新了,很是方便,不需要重新绑定         */        var dataRegardUI = dataGridView2.DataSource as BindingList<BlogNew>;        dataRegardUI.Add(new BlogNew { BlogID = 20, BlogTitle = "竹外桃花三两枝,春江水暖鸭先知" });  }

.Net 网络

.NET Framework允许应用程序使用IPV4和 IPV6。

见【.Net网络】一节

.Net 平台下C#socket 通信

什么是socket?

在操作系统中,通常会为应用程序提供一组应用程序接口(API),称为套接字接口(英语:socket API)。应用程序可以通过套接字接口,来使用网络套接字,以进行数据交换。最早的套接字接口来自于4.2 BSD,因此现代常见的套接字接口大多源自Berkeley套接字(Berkeley sockets)标准。在套接字接口中,以IP地址及通信端口组成套接字地址(socket address)。远程的套接字地址,以及本地的套接字地址完成连接后,再加上使用的协议(protocol),这个五元组(five-element tuple),作为套接字对(socket pairs),之后就可以彼此交换数据。参考:维基百科

注:socketsocket,协议是协议,不要把两个概念混淆。

自定义协议

目前业界主要采取的协议定义方式是:包头+包体长度+包体。具体如下:

一般包头使用一个int定义,例如int = 173173173;作用是区分每一个有效的数据包,因此我们的服务器可以通过这个int去切割、合并包,组装出完整的传输协议。有人使用回车字符去分割包体,例如常见的SMTP/POP协议,这种做法在特定的协议是没有问题的,可是如果我们传输的信息内容自带了回车字符串,那么就糟糕了。所以在设计协议的时候要特别小心。

包体长度使用一个int定义,这个长度表示包体所占的比特流长度,用于服务器正确读取并分割出包。

包体就是自定义的一些协议内容,例如是对像序列化的内容(现有的系统已经很常见了,使用对象序列化、反序列化能够极大简化开发流程,等版本稳定后再转入手工压入byte操作)。

一个实际编写的例子:比如我要传输2个整型 int = 1, int = 2,那么实际传输的数据包如下:

包头 包体长度 包体 173173173 8 1 2

这个数据包就是4个整型,总长度 = 4*4 = 16。

具体详见【C#自定义协议】

TCP/IP

TCP/IP:Transmission Control Protocol/Internet Protocol,传输控制协议/因特网互联协议,又名网络通讯协议。简单来说:TCP控制传输数据,负责发现传输的问题,一旦有问题就发出信号,要求重新传输,直到所有数据安全正确地传输到目的地,而IP是负责给因特网中的每一台电脑定义一个地址,以便传输。

TCP/IP由:网络接口层(链路层)、网络层、传输层、应用层。

Tcp/Ip三次握手

1 客户端发送syn报文到服务器端,并置发送序号为x。

2 服务器端接收到客户端发送的请求报文,然后向客户端发送syn报文,并且发送确认序号x+1,并置发送序号为y。

3 客户端受到服务器发送确认报文后,发送确认信号y+1,并置发送序号为z。至此客户端和服务器端建立连接。

31093719-34ccd74b68c646859b6b9ddd8620217f


C#通信——自定义协议

参考

数据协议

简单点说,数据通信协议就是数据通信系统中通信对象之间能准确有效地进行通信所必须遵循的规则和各种约定事项,是为保证数据通信网中通信双方能有效,可靠通信而规定的一系列约定。这些约定包括数据的格式,顺序和速率,数据传输的确认或拒收,差错检测,重传控制和询问等操作。

消息的结构

消息头 + 消息体。每次先发送出去的是消息头,然后是消息体。消息头里描述了这个数据包的类型,长度,序列号等信息。消息头的长度是固定的,消息体的长度是根据每个消息类型会有所的区别。

消息头的定义:

字段 长度(字节) 类型 说明 Length 4 Int 消息的总长度(字节) Command ID 4 Int 命令ID NodeID 4 Int 结点ID TimeID 4 Int 时间戳 SequenceID 4 Int 递增序列号

要成为一个完整的消息,一般还必须包含消息体,客户机与服务器连接上后,它通常会发送一个绑定(Bind) 消息给服务器端。例如:验证确认客户端的合法性。那么此时的Bind消息的格式是:

字段 长度(字节) 类型 说明 HEAD 上面的消息头部 loginName 16 string 用户名(固定16位,不足用空格填充) LoginPassword 16 string 密码(固定16位,不足用空格填充)

1、用于IP地址的.Net类

.NET Framework提供了许多能够帮助寻找IP地址和主机信息的类。

(1)IPAddress类

IPAdkss类代表IP地址。IPAddress类也实现静态的Parse()方法,这个方法的作用与ToString()方法正好相反,把小数点隔开的十进制字符串转化为IP地址。

IPAddress ipAddress = IPAddress.Parse("234.12.1.2");//得到一个byte数组,分别存放234 12 1 2byte[] address = ipAddress.GetAddressBytes();//是和Parse相逆的操作string ipString = ipAddress.ToString();//Loopback地址允许计算机给它自己发送消息,loopback的值为"127.0.0.1"string loopback = IPAddress.Loopback.ToString();//Broadcast地址允许多播到本地网络,用来给本地所有机器发送信息,返回值为""255,255,255,255string broadcast = IPAddress.Broadcast.ToString();

(2)IPHostEntry类

IPHostEntry类用于封装与某台特定的主机相关的信息。通过这个类的HostName属性(这个属性返回一个字符串),可以使用主机名;通过AddressList属性返回一个IPAddress对象数组。

(3)Dns类

该类能与默认的DNS服务器进行通信,以检索IP地址。Dns类有两个重要的静态方法:Resolve()方法和GetHostByAddress()

===================================================


断点续传

断点续传即下载任务暂停后可以继续,而无需重新下载,即下载时需要通知服务器的起始位置。如果允许多线程进行分片下载,必须提供起始-截止位置。说到底就是可以选择下载某个片段,整个文件的字节流,可以截取流的片段,也能实现流的累积,最终完成文件下载。

C#编程总结(十二)断点续传 (基于HTTP,暂时不看)

HTTP协议

HTTP协议是一种基于TCP的简单协议,分为请求回复两种。

请求协议是由 客户机(浏览器)向服务器(WEB SERVER)提交请求时发送报文的协议。

回复协议是由服务器(web server),向客户机(浏览器)回复报文时的协议。

请求和回复协议都由头和体组成。头和体之间以一行空行为分隔。

原理

在 HTTP/1.1里新增的一个头属性:Range,也是现在众多号称多线程下载工具(如 FlashGet、迅雷等)实现多线程下载的核心所在。老版本的HTTP协议不支持,所以一些老的服务器还不支持断点续传。


序列化与反序列化

序列化

将对象的状态信息转换为可以存储或传输的形式的过程。

反序列化

序列化的逆过程。

C#下的实现思路:利用结构体转换——

参考

//C#中结构体与字节流互相转换class Converter     {         //Structure转为Byte数组,实现了序列化         public static Byte[] StructToBytes(Object structure)         {             Int32 size = Marshal.SizeOf(structure);             Console.WriteLine(size);             IntPtr buffer = Marshal.AllocHGlobal(size);             try             {                 Marshal.StructureToPtr(structure, buffer, false);                 Byte[] bytes = new Byte[size];                 Marshal.Copy(buffer, bytes, 0, size);                 return bytes;             }             finally             {                 Marshal.FreeHGlobal(buffer);             }         }         //Byte数组转为Structure,实现了反序列化         public static Object BytesToStruct(Byte[] bytes, Type strcutType)         {             Int32 size = Marshal.SizeOf(strcutType);             IntPtr buffer = Marshal.AllocHGlobal(size);             try             {                 Marshal.Copy(bytes, 0, buffer, size);                 return Marshal.PtrToStructure(buffer, strcutType);             }           //不管程序执行try还是catch,finally 始终会执行.通常在finally语句中是进行资源的清除工作。如关闭打开的文件和通讯句柄,或者数据库链接等。           finally             {                 Marshal.FreeHGlobal(buffer);             }         }     }  

关于IntPtr

首先看一下System.Runtime.InteropServices.Marshal.AllocHGlobal方法:

Calls the System.Runtime.InteropServices.Marshal.AllocHGlobal method to allocate the same number of bytes as the unmanaged string occupies. The method returns an IntPtr object that points to the beginning of the unmanaged block of memory. The Visual Basic example uses this pointer directly; in the C++ and C# examples, it is cast to a pointer to a byte.(From MSDN)

//MSDN中关于内存和指针操作的一个例子class Serielize{    static public void Main()    {        string stringA = "I seem to be turned around!";        int copylen = stringA.Length;        // Allocate HGlobal memory for source and destination strings        //将 ANSI 字符从指定的字符串(在托管堆中)复制到非托管堆中的缓冲区。该方法(StringToHGlobalAnsi)还分配大小正确的目标堆。        IntPtr sptr = Marshal.StringToHGlobalAnsi(stringA);        IntPtr dptr = Marshal.AllocHGlobal(copylen + 1);        // The unsafe section where byte pointers are used.        //在运行unsafe 代码是要在项目属性-生成选项里配置下"允许运行不安全代码"。        unsafe        {            byte* src = (byte*)sptr.ToPointer();            byte* dst = (byte*)dptr.ToPointer();            if (copylen > 0)            {                // set the source pointer to the end of the string                // to do a reverse copy.                src += copylen - 1;                while (copylen-- > 0)                {                    *dst++ = *src--;                }                //结尾为0,用来标志字符串的结束                *dst = 0;            }        }        string stringB = Marshal.PtrToStringAnsi(dptr);        Console.WriteLine("Original:\n{0}\n", stringA);        Console.WriteLine("Reversed:\n{0}", stringB);        // Free HGlobal memory        //释放非托管资源        Marshal.FreeHGlobal(dptr);        Marshal.FreeHGlobal(sptr);    }}

从上例可以看出,

  1. IntPtr对象是C#中的内存对象,可以通过调用ToPointer()函数的方法返回内存开始位置的指针;
  2. 同时也可以看出,C#中字符串结尾的标志位也是0;
  3. Marshal.AllocHGlobal(size)和C语言中的malloc(size)的作用是一样的,分配指定大小的内存空间;
  4. Marshal.StringToHGlobalAnsi(stringA)为获取指定对象的可操作内存;

关于Marshal.PtrToStructure Method系列

其实就是PtrTo系列函数,它们的作用就是将格式化内存数据——Marshals data from an unmanaged block of memory to a managed object.

小结: Marshal 类中定义的 static 方法对于处理非托管代码至关重要。此类中定义的大多数方法通常由需要在托管和非托管编程模型之间提供桥梁的开发人员使用。

原创粉丝点击