博主从七月到十月底一共投了142家企业,流程走完的有9家,收到7个offer,目前三方已签,很满意签约的公司,现在把这几个月我遇到常见问题写下来,希望能帮助到大家。

心得:我碰到的八股都是基于我简历上的项目来的,比如我的温室监控系统用的tcp,面试官就会问这个;我用到了数据库,面试官就会问数据库优化以及你的优化方案。建议在自己的项目经历里写上自己拿手的关键词引导面试官去问,同时自己的项目准备几个优化方案,比如组网的方案、数据库优化方案(硬件层面、软件编写方面)、通信协议等等,等到面试官问你为什么这么做的时候就可以把几个方案一罗列,然后为什么选择这个(成本低、上手容易、功耗低啥的),比如我给我的温室监控系统项目写的(面试官就着你的项目冷不丁问个八股,我遇到的都是这样):

针对于整个系统的优化考虑:

1.首先是考虑系统的并发压力:主要是硬件终端与上位机之间的传输

每一个温室共有21个采集结点(8个温度采集节点(四个角的空气和土壤)、8个湿度(同温度一样)、5个光强(四个角和中心))和9个控制节点,为了方便节点管理和日后节点的增加,在底层方面我们采用的是星型结构来组网(问组网方式以及为什么用星型组网?星型:星型结构是以中央节点作为核心,其他节点都连接至中央节点上,这种结构的成本较高、可靠性较低,但是其延迟小、结构简单便于管理。),每个温室有一个中央节点,总中央节点与我们系统进行通信,这样在与硬件终端通信方面,我们只要创建一个线程来接收数据即可(多串口的话,每个串口都需要一个线程来传输),减少上位机与终端直接的并发量。(问为什么用lora,当时考虑物联网应用方面,一是nbIoT的频段要收钱;二是应用场景在室内,组网距离不大,数据量也不算大,lora更适合;三是lora设备的能耗更低)(考虑串并行的问题,我们对数据没有很高的时效性要求,节点每隔10分钟传输一次数据,更多的是考虑成本和稳定性问题,所以我们采用串行方式)(串口通信:RS-485,优点:支持多站,全双工,最大传输速率为10mbps)

在客户端和服务端的连接上,考虑到管理员人数和指令下达的需求,选用的是tcp方式(TCP与UDP区别?为什么用TCP?粘包了解过吗),用一个监听线程来监听客户端的连接请求,宏观上达到一对多的连接方式(下指令要互斥)。

2.对读写数据库的优化(被问的太多了,其实想想也正常,比较企业里数据管理很重要)(并发量、代码优化):

当高并发压力来袭的时候,通常不会是数据库先扛不住了,而是业务系统所在机器抗不住了,底层采用星型结构将底层数据的并发压力做到最低,这时候的关键就是我的系统处理每个业务请求的时候,他是会读写多次数据库的,所以业务系统的一次请求可能会导致数据库有多次请求,也正因为这样,所以此时可能你的数据库并发压力会到几千的样子(增加数据库的并发压力)。

对此我从架构和代码两个方面提出优化方案:

架构层面:

一、根据业务系统拆分多个数据库机器优化方案。第一个根据节点功能不同将数据库进行拆分,将采集信息和控制信息分开存储,减轻数据库机器的压力。

二、读写分离。给每个数据库挂一个从库,让主数据库基于binlog数据更新日志同步复制给从数据库,让主从数据库保持数据一致,然后我们的系统其实可以往主库里写入,在从库里查询,此时就又可以缓解原来的主数据库的压力了。

三、分库。分库分表,就是把主库拆分为多个库,每个库里放一个表的部分数据,然后用多个主库抗高并发写入压力,这样就可以再次分散我们的压力了。

代码方面:

一、在insert语句方面,一条语句插入多个值,拼接values使用StringBuilder(速度快但线程不安全,因为采用的底层结构特点,只需要一个接收线程即可)

二、关闭autocommit,设定一个任务集合,当任务达到一个阈值(比如50)的时候,手动提交。

但是数据写入间隔是五分钟,由于实际要求自动提交就可以了,只需要将一个温室的数据(温室id和其余数据)拼接成一条语句写入即可。

最大承载量方面没有精确计算,当时十个温室,运行一个月,每个5分钟一条数据,产生了快十万条数据,然后后面就没看了。

计算数据库大小:

单条数据大小:1*10+4*21+8=102个字节

温室id、21个采集节点、9个控制节点、时间

TINYINT (占1个字节)(用于温室id和控制节点(开关只用01(控制量)表示)(数据库没有bool类型))、float型(占4个字节)(21个采集结点)、datetime(占8个字节)(存储时间)

10个温室每隔五分钟采集一次数据,运行了一个月,大概产生快10万条数据,占存储空间10MB(查看表容量是10MB)

以下问题都是我在面试过程中遇到的八股,供大家参考:

一、值引用和地址引用的区别(值类型和引用类型什么区别)(简单记传值和传址)

1.值类型的数据存储在内存的栈中;引用类型的数据存储在内存的堆中,而内存单元中只存放堆中对象的地址。

2.值类型存取速度快,引用类型存取速度慢。

3.值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针或引用

4.值类型继承自System.ValueType,引用类型继承自System.Object

5.栈的内存分配是自动释放;而堆在.NET中会有GC来释放

6.值类型的变量直接存放实际的数据,而引用类型的变量存放的则是数据的地址,即对象的引用。

7.值类型变量直接把变量的值保存在堆栈中,引用类型的变量把实际数据的地址保存在堆栈中,而实际数据则保存在堆中。

注意,堆和堆栈是两个不同的概念,在内存中的存储位置也不相同,堆一般用于存储可变长度的数据,如字符串类型;而堆栈则用于存储固定长度的数据,如整型类型的数据int(每个int变量占用四个字节)。由数据存储的位置可以得知,当把一个值变量赋给另一个值变量时,会在堆栈中保存两个完全相同的值;而把一个引用变量赋给另一个引用变量,则会在堆栈中保存对同一个堆位置的两个引用,即在堆栈中保存的是同一个堆的地址。在进行数据操作时,对于值类型,由于每个变量都有自己的值,因此对一个变量的操作不会影响到其它变量;对于引用类型的变量,对一个变量的数据进行操作就是对这个变量在堆中的数据进行操作,如果两个引用类型的变量引用同一个对象,实际含义就是它们在堆栈中保存的堆的地址相同,因此对一个变量的操作就会影响到引用同一个对象的另一个变量。

二、C#中抽象类(abstract)和接口(interface)的相同点与区别

相同点:

1、都可以被继承

2、都不能被实例化

3、都可以包含方法声明

4、派生类必须实现未实现的方法

区别:

1、抽象基类可以定义字段、属性、方法实现。接口只能定义属性、索引器、事件、和方法声明,不能包含字段。

2、抽象类是一个不完整的类,需要进一步细化,而接口是一个行为规范。微软的自定义接口总是后带able字段,证明其是表述一类“我能做。。。”

3、接口可以被多重实现,抽象类只能被单一继承

4、抽象类更多的是定义在一系列紧密相关的类间,而接口大多数是关系疏松但都实现某一功能的类中

5、抽象类是从一系列相关对象中抽象出来的概念,因此反映的是事物的内部共性;接口是为了满足外部调用而定义的一个功能约定, 因此反映的是事物的外部特性

6、接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法

7、接口可以用于支持回调,而继承并不具备这个特点

8、抽象类实现的具体方法默认为虚的,但实现接口的类中的接口方法却默认为非虚的,当然您也可以声明为虚的

9、如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法

三、Static关键字

static 是静态修饰符,一般修饰成员。被 static 修饰的成员属于类,不属于单个这个类的某个对象。 static修饰的成员被多个对象共享。 static 修饰的成员属于类,但是会影响每一个对象。被 static修饰的成员又叫类成员,不叫对象的成员。

特点

1. 被 static 修饰的属于类,不属于对象

2. 优先于对象存在

3. static 修饰的成员 , 可以作为共享的数据 (只要是根据 static 修饰的成员所在的类创建出来的对象 ,都可以共享这个数据)

四、什么是面向对象以及多态的实现,虚方法实现多态?

面向对象的方法主要是把事物给对象化,包括其属性和行为。面向对象编程更贴近实际生活的思想。总体来说面向对象的底层还是面向过程,面向过程抽象成类,然后封装,方便使用就是面向对象(万物皆对象)。

三大特点:封装,继承,多态

多态实现:

举个例子:

Animal eat...

Dog eat...

WolfDog eat...

在上面的例子中类Dog继承自类Animal,对方法Eat()进行了重写,类WolfDog又继承自Dog,再一次对Eat()方法进行了重写,并很好地实现了多态。不管继承了多少层,都可以在子类中对父类中已经重写的方法继续进行重写,即如果父类方法用override修饰,如果子类继承了该方法,也可以用override修饰,多层继承中的多态就是这样实现的。要想终止这种重写,只需重写方法时用sealed关键字(sealed:密封,故名思义,就是由它修饰的类或方法将不能被继承或是重写)进行修饰即可。

理解多态的两大原则:里氏替换原则、开放封闭原则

里氏替换原则(Liskov Substitution Principle):派生类(子类)对象能够替换其基类(超类)对象被使用。通俗一点的理解就是“子类是父类”,举个例子,“男人是人,人不一定是男人”,当需要一个父类类型的对象的时候可以给一个子类类型的对象;当需要一个子类类型对象的时候给一个父类类型对象是不可以的!

开放封闭原则(Open Closed Principle):封装变化、降低耦合,软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。因此,开放封闭原则主要体现在两个方面:对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。

虚方法实现多态:(virtual)

虚方法可以实现多态;即使子类没有重写虚方法时,也可直接调用父类的虚方法。

五、C#中 private、 protected、 public、 internal 修饰符的访问权限

答 . private : 私有成员, 在类的内部才可以访问。 protected : 保护成员,该类内部和继承类中可以访问。 public : 公共成员,完全公开,没有访问限制。 internal: 在同一命名空间内可以访问。

六、用过啥集合吗?说说看

答:ArrayList用过,因为它元素类型没有限制

array和arrayList的区别?

答:Array是数组,声明好之后,其长度就已经固定,通过数组下标来对指定位置的元素进行变更。ArrayList底层是用数组实现的,但是ArrayList的长度是可变的,通过add、remove来变更元素。

七、委托和事务的区别

概念

1、委托:将方法以变量的形式传递,并且以方法的形式执行。他是类,是引用类型。

2、事件:功能被限制的一个委托变量。它的类型是委托类型。

3、委托的三种形式

3.1、delegate: 四步(声明,实例化,注册方法,调用)

3.2、Action:添加的方法不能有返回值

3.3、Func: 添加的方法要有返回值

3.4、lamda表达式:方法只使用一次,没有多次使用的话使用

1.事件的声明只是在委托前面加一个event关键词,虽然你可以定义一个public,但是有了event关键词后编译器始终会把这个委托声明为private,然后添加1组add,remove方法。add对应+=,remove对应-=。这样就导致事件只能用+=,-=来绑定方法或者取消绑定方法。而委托可以用=来赋值,当然委托也是可以用+=,-=来绑定方法的(面试我的那个哥们好像说不行)。

2.委托可以在外部被其他对象调用,而且可以有返回值(返回最后一个注册方法的返回值)。而事件不可以在外部调用,只能在声明事件的类内部被调用。我们可以使用这个特性来实现观察者模式。大概就是这么多。下面是一段测试代码。

八、进程和线程区别(线程是调度的基本单位,而进程则是资源拥有的基本单位),多线程安全怎么考虑?线程中的 sleep() 方法和 wait() 方法有什么区别?

线程与进程的比较如下:

1.进程是资源(包括内存、打开的文件等)分配的单位,线程是CPU调度的单位;

2.进程拥有一个完整的资源平台,而线程只独享必不可少的资源,如寄存器和栈;

3.线程同样具有就绪、阻塞、执行三种基本状态,同样具有状态之间的转换关系;

4.线程能减少并发执行的时间和空间开销;

使用多线程的原因主要有以下几点(优点):

1. 更多的CPU核心 现代计算机处理器性能的提升方式,已经从追求更高的主频向追求更多的核心发展,所以处理器的核心数量会越来越多,充分地利用处理器的核心则会显著地提高程序的性能。而程序使用多线程技术,就可以将计算逻辑分配到多个处理器核心上,显著减少程序的处理时间,从而随着更多处理器核心的加入而变得更有效率。

2. 更快的响应时间 我们经常要针对复杂的业务编写出复杂的代码,如果使用多线程技术,就可以将数据一致性不强的操作派发给其他线程处理(也可以是消息队列),如上传图片、发送邮件、生成订单等。这样响应用户请求的线程就能够尽快地完成处理,大大地缩短了响应时间,从而提升了用户体验。

保证线程安全:不安全原因:若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时对一个变量执行读写操作,一般都需要考虑线程同步,否则就可能影响线程安全。方法:通过锁将多个线程串行处理。

线程的 sleep() 方法和 wait() 方法有以下几点区别:

(1)sleep() 方法是 Thread 类中的方法,而 wait() 方法是 Object 类中的方法。

(2)sleep() 方法不会释放 lock,但是 wait() 方法会释放,而且会加入到等待队列中。

(3)sleep() 方法不依赖于同步器 synchronized(),但是 wait() 方法 需要依赖 synchronized 关键字。

(4)线程调用 sleep() 之后不需要被唤醒(休眠时开始阻塞,线程的监控状态依然保持着,当指定的休眠时间到了就会自动恢复运行状态),但是 wait() 方法需要被重新唤醒(不指定时间需要被别人中断)。

(5)、wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围)

(6).sleep必须捕获异常,wait,notify和notifyAll同样需要捕获异常(之前看到网上很多博客都说wait方法不需要抛出异常这个观点是错误的,千万不要被误导了!!!!!!!notify和notifyAll方法确实可以不用抛出异常)

(7).sleep是Thread类的静态方法。sleep的作用是让线程休眠制定的时间,在时间到达时恢复,也就是说sleep将在接到时间到达事件事恢复线程执行。wait是Object的方法,也就是说可以对任意一个对象调用wait方法,调用wait方法将会将调用者的线程挂起,直到其他线程调用同一个对象的notify方法才会重新激活调用者。

九、Runnable和Callable的区别

相同点

1、两者都是接口;(废话)

2、两者都可用来编写多线程程序;

3、两者都需要调用Thread.start()启动线程;

不同点

1、两者最大的不同点是:实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果;

2、Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;

十、设计模式了解吗?就你常用的举两个例子。(单例和工厂最好举例子)

单例模式:一个类只有一个实例存在。单例模式最重要的特点就是构造函数私有,从而避免外界直接使用构造函数直接实例化该类的对象。单例模式的优点: - 在一个对象需要频繁的销毁、创建,而销毁、创建性能又无法优化时,单例模式的优势尤其明显 - 在一个对象的产生需要比较多资源时,如读取配置、产生其他依赖对象时,则可以通过在启用时直接产生一个单例对象,然后用永久驻留内存的方式来解决(适用于需要频繁创建和销毁的类,避免消耗更多的资源.) - 单例模式可以避免对资源的多重占用,因为只有一个实例,避免了对一个共享资源的并发操作 - 单例模式可以在系统设置全局的访问点,优化和共享资源访问 单例模式的缺点: - 单例模式无法创建子类,扩展困难,若要扩展,除了修改代码基本上没有第二种途径可以实现 - 单例模式对测试不利。在并行开发环境中,如果采用单例模式的类没有完成,是不能进行测试的 - 单例模式与单一职责原则有冲突。一个类应该只实现一个逻辑,而不关心它是否是单例的,是不是要用单例模式取决于环境。

案例:1. 网站在线人数统计:就是全局计数器,也就是说所有用户在相同的时刻获取到的在线人数数量都是一致的。要实现这个需求,计数器就要全局唯一,也就正好可以用单例模式来实现。当然这里不包括分布式场景,因为计数是存在内存中的,并且还要保证线程安全。

2. 配置文件访问类:项目中经常需要一些环境相关的配置文件,比如短信通知相关的、邮件相关的。比如 properties 文件,这里就以读取一个properties 文件配置为例,如果你使用的 Spring ,可以用 @PropertySource 注解实现,默认就是单例模式。如果不用单例的话,每次都要 new 对象,每次都要重新读一遍配置文件,很影响性能,如果用单例模式,则只需要读取一遍就好了。

工厂模式:定义一个创建产品对象的工厂接口,将实际创建性工作推迟到子类中(不暴露创建对象的具体逻辑,而是将逻辑封装在一个函数中)。简单工厂的实现思路是,定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。 工厂方法模式是简单工厂的仅一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说每个对象都有一个与之对应的工厂。工厂方法的实现思路是,定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。 抽象工厂模式是工厂方法的仅一步深化,在这个模式中的工厂类不单单可以创建一个对象,而是可以创建一组对象。这是和工厂方法最大的不同点。抽象工厂的实现思路是,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。

十一、几种常见排序(问思想,部分公司要手写)

1.冒泡排序(二者比较,大的下沉,小的上浮)

主要思路:

1.比较相邻的元素。如果第一个比第二个大,就交换它们两个。

2.对每一个相邻元素做同样的工作,从开始第一对到结尾的每一对。在这一 点,最后的元素应该会是最大的数。

3.针对多有的元素重复以上的步骤,除了最后一个。

时间复杂度O(n^2), 稳定

2.选择排序(从中找最小,然后和第一个做交换;并从剩下的中再找最小,以此类推)

主要思路:

1.首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。

2.再从剩余未排序元素中继续寻找最小(大)元素,然后放到未排序序列的起始位置。

3.重复第二步,直到所有元素均排序完毕。

时间复杂度O(n^2), 不稳定

3.插入排序(从后面未排序的数插入到前面已经排好的数组当中去)

主要思路:

插入排序是最简单常用的方法,将数组分为两部分,排好序的数列,以及未排序的数列,将未排序的数列中的元素 与排好序的数列进行比较,然后将该元素插入到已排序列的合适位置中。

1.从第一个元素开始,该元素可以认为已经被排序

2.取下一个元素temp,从已排序的元素序列从后往前扫描

3.如果该元素大于temp,则将该元素移到下一位

4.重复步骤3,直到找到已排序元素中小于等于temp的元素

5.temp插入到该元素的后面,如果已排序所有元素都大于temp,则将temp插入到下标为0的位置

6.重复步骤2~5

时间复杂度O(n^2), 稳定

4.希尔排序

是插入排序的一种进阶排序算法,通过一个不断缩小的增量序列,对无序序列反复的进行拆分并且对拆分后的序列使用插入排序的一种算法,所以也叫作“缩小增量排序”或者“递减增量排序”。时间复杂度为O(n^1.3~n^1.5),要好于直接排序的O(n ^ 2),需要注意的是增量序列的最后一个增量值必须是1.另外由于记录跳跃式的移动,希尔排序并不是一种稳定的排序方法。

基本思想:

  1. 选定一个增长量h,按照增长量h作为数据分组的依据,对数据进行分组;
  2. 对分好组的每一组数据完成插入排序;
  3. 减小增长量,最小减为1,重复第二步操作。

5.归并排序

基本思想:

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用

分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到

完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成

一个有序表,称为二路归并(分两组,每组再细分两组……)。

时间复杂度O(nlogn),稳定

6.快速排序(先选数组最左边的元素,小的放左,大的放右;再从两边再取一个数执行上述行为,知道两边各只有一个为止)

基本思想:

快速排序算法的基本思想为分治思想。先从数列中取出一个数作轴值(基准数)key;

根据基准数将数列进行分区,小于基准数的放左边,大于基准数的放右边;

重复分区操作,知道各区间只有一个数为止。

平均时间复杂度O(nlogn),最差为O(n^2),不稳定

十二、图的遍历(浅显的答的容易,有深度的答不上来,我太菜了)

1.DFS深度优先:

很像二叉树的前序遍历,根节点开始一路向左,把所有的邻都遍历完,然后需要check下还有没有节点没有遍历到啊? 这个时候就需要一个个的回退节点(类似二叉树里的左孩子返回父节点去找右伙伴)去找没有遍历到的邻... 迭代进行这个过程直到所有的节点都过了一遍。(不用递归的话,DFS的实现用栈)

2. BFS广度优先:(遍历节点和其连通节点,依次向下遍历,队列)

首先起始节点压入队列,然后当这个节点被推出队列,它的所有邻就得马上给我进到队列里来!然后一直这样重复下去,直到队列空了。这期间,我们可以把自己的实际问题“放入”出队进队这一活动中,实现我们的算法目的。

十三、String、StringBuffer、StringBuilder之间区别

1.三者在执行速度方面的比较: StringBuilder >StringBuffer > String

String一旦赋值或实例化后就不可更改(String被final修饰,所以不可变),如果赋予新值将会重新开辟内存地址进行存储。而StringBuffer类使用append和insert等方法改变字符串值时只是在原有对象存储的内存地址上进行连续操作,减少了资源的开销。因此:当需要进行频繁修改字符串的操作时先建立StringBuffer类对象进行操作,将最后结果转化成String类对象返回,这样效率会高很多。

StringBuffer(StringBuilder)其实可以看做“基本数据类型”String的包装类(Wrapper),就像int与之对应的Integer等关系。StringBuffer有缓存的,如果你声明一个字符串只是接收传过来的参数,然后进行业务逻辑处理,那么假如你用很多个StringBuffer类型的对象,就比较浪费内存。这样用String就更好。

2. 在字符串拼接时,String 对象的速度并不会比 StringBuffer对象慢。

String 对象的字符串拼接其实是被 JVM 解释成了StringBuffer 对象的拼接,所以这些时候String 对象的速度并不会比StringBuffer 对象慢,而特别是以下的字符串对象生成中, String 效率是远要比 StringBuffer 快。

String S1 = “This is only a” + “ simple” + “ test”;

StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“test”);

你会很惊讶的发现,生成String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个

String S1 = “This is only a” + “ simple” + “test”;

其实就是:

String S1 = “This is only a simple test”;

所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:

String S2 = “This is only a”;

String S3 = “ simple”;

String S4 = “ test”;

String S1 = S2 +S3 + S4;

这时候 JVM 会规规矩矩的按照原来的方式去做

3. 在线程安全方面不同

StringBuffer 字符串变量(线程安全)

StringBuilder 字符串变量(非线程安全)

三者使用总结:在操作少量变动的数据时,使用String;在单线程中操作大量数据的字符串时,使用StringBuilder;在多线程中操作大量数据的字符串时,使用StringBuffer。

十四、TCP三次握手,TCP传输中有个粘包现象了解过吗?你怎么解决的?

建立连接最关键的一步就是进行3次握手,这一步主要关注三个“硬性指标”,分别是

ACK,tcp报头的控制位之一,用于表示确认号是否有效,若有效则置1(确认号:用于告诉数据发送端发送过来的序列号之前的数据都已经接收到了)

SYN,同步序列号,如果TCP建立连接成功则置1

FIN,发送端完成发送任务位,当TCP完成了数据传输的任务,想要断开连接之后,该位置1

详细过程:

1.(同步通信方式)假定服务端向客户端发送信息

服务端向客户端发送一个含有SYN的数据段给客户端,并请求连接

客户端收到服务端收到请求后,用含有ACK和SYN的数据段响应服务端

服务端收到客户端发来的数据段后,再发送一个ACK,确认收到数据段,至此建立连接成功。

注:握手的步骤在协议内部协议实现的,不体现在具体的上位机代码中。理解握手过程即可

2.传输数据

建立连接之后,就可以通过通信信道进行传输数据的任务了,但是需要注意的是TCP协议中的数据是以字节流的形式存在的,发送端需要将的数据转换为字节流,然后才可以发送。通常是字符集转换为字节序列,这其中就涉及到编码和解码的问题了。

3.断开连接

断开连接主要是关闭读写流。至于是服务端先断开连接还是客户端先断开连接,有时需要进行异常处理。

粘包出现原因:在流传输中出现,UDP不会出现粘包,因为它有消息边界(参考Windows 网络编程)

1 发送端需要等缓冲区满才发送出去,造成粘包

2 接收方不及时接收缓冲区的包,造成多个包接收

解决办法:

为了避免粘包现象,可采取以下几种措施。一是对于发送方引起的粘包现象,用户可通过编程设置来避免,TCP提供了强制数据立即传送的操作指令push(TCP协议字段中有6个标志位,像ACK,FIN,SYN都是喜闻乐见的标志位,push标志位不常用),TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满;二是对于接收方引起的粘包,则可通过优化程序设计、精简接收进程工作量、提高接收进程优先级等措施,使其及时接收数据,从而尽量避免出现粘包现象(Thread.CurrentThread.Priority = ThreadPriority.Highest;);三是由接收方控制,将一包数据按结构字段,人为控制分多次接收,然后合并,通过这种手段来避免粘包。

以上提到的三种措施,都有其不足之处。第一种编程设置方法虽然可以避免发送方引起的粘包,但它关闭了优化算法,降低了网络发送效率,影响应用程序的性能,一般不建议使用。第二种方法只能减少出现粘包的可能性,但并不能完全避免粘包,当发送频率较高时,或由于网络突发可能使某个时间段数据包到达接收方较快,接收方还是有可能来不及接收,从而导致粘包。第三种方法虽然避免了粘包,但应用程序的效率较低,对实时应用的场合不适合。

十五、ADO.NET五大对象及用法

1.Connection对象

Connection 对象就像是打仗时候的通信兵,他们在打仗之前需要先接通司令部与各个作战单位之间的通信线路。之后作战命令的获取以及发布都要靠通信线路来完成。这里的司令部就类似于服务器,各作战单位就类似于各应用程序。

2.Command对象

执行一些简单操作命令,如:增删改删,即执行T-SQL语句。Command对象有几个比较重要的方法,如ExecuteNonQuery()方法,执行增删改命令,返回的是受影响的行数。查询方法有2种:一个是ExecuteReader()方法,返回一个DataReader对象。还有ExecuteScale()方法,返回首行首列值。

3.DataAdapter对象

数据适配器,从数据库中检索数据,再填充到本地数据集中。同时,我们可以利用DataAdapter,再将数据反向从DataSet中更新回数据库。DataAdapter使用中主要有4个命令对象比较重要。它们分别是:SelectCommand、InsertCommand、UpdateCommand、DeleteCommand。SelectCommand,主要是从数据库中检索数据InsertCommand、UpdateCommand、DeleteCommand这3个命令对象主要负责把本地数据集DataSet中的数据上传回服务器。我们主要使用的是前者。DataAdapter的Fill方法,用于使用DataAdapter的SelectCommand的执行结果集来填充DataSet。

4.DataReader对象

当我们只需要循序的读取数据而不需要其它操作时,可以使用DataReader 对象。DataReader对象只是一次一笔向下循序地读取数据源中的数据,不作其它的操作。因为DataReader 在读取数据的时候限制了每次只读取一笔,而且只能只读,所以使用起来不但节省资源而且效率很好。此外,因为不用把数据全部传回,故可以降低网络的负载。但是,当我们从数据源中一条一条的读取数据的时候,一定记得要时刻打开数据库的连接。

5.DataSet对象

DataSet 这个对象可以视为本地内存中的一个数据库,可以把从数据库中所查询到的数据保留到本地。DataSet 的能力不只是可以储存多个Table,还可以透过DataAdapter 对象取得一些例如主键等数据表结构,并可以记录数据表间的关联。当我们使用DataSet读取数据的时候,数据库的连接是否关闭已经无关紧要了。DataSet 对象可以说是ADO.NET 中重量级的对象,通过DataAdapter 对象这个桥梁,实现了与数据源沟通的能力 。

另外因为公司业务也会问一些专业性的小问题,就不一一例举了。

C#软件工程师、 .NET、 上位机软件开发工程师秋招面经八股汇总 及心得相关推荐

  1. python做工控机_「上位机软件」工控机上位机软件的开发历程(一) - seo实验室...

    上位机软件 本人就职于一家环境监测公司,上位机软件的主要功能是采集各仪器的数据,然后存储起来,并传送到环保局平台. 刚到公司的时候,公司使用的是组态软件(用以显示流程图),然后再开发了报表软件.数据上 ...

  2. 上位机软件开发流程是怎样的?上位机开发软件分享

    随着科技的发展, 上位机软件开发已经成为当今社会的一个重要组成部分.上位机软件开发是一种技术,它可以帮助用户更好地控制和管理计算机系统.它可以帮助用户更有效地完成任务,提高工作效率,提高生产力. 上位 ...

  3. 如何调试上位机软件与串口进行通信

    为了在没有下位机连接的情况下调试上位机软件,看上位机软件是否能通过串口和下位机通信,以及通信的具体内容,下面给出解决方法: 1.下载"vspd虚拟串口" vspd虚拟串口软件是用来 ...

  4. 梅特勒.托利多-称重上位机软件

    智能仪表上位机软件,PC直连toledo仪表. 支持CTPZ和SICS协议,适配所有toledo系列,以太网和串口通用. 适用于工业手动配料,配方自定义,对接工厂信息化系统,MES系统,报表导出,支持 ...

  5. 什么是核心竞争力——源自半导体行业,一个上位机软件开发工程师

    到底什么是核心竞争力 ----来自半导体行业,一个上位机软件开发工程师 我的工作侧重于软件二次开发,因行业问题,软件需要对电机,泵等硬件进行通信,按照一定的顺序对其进行控制,对Wafer进行加工,以达 ...

  6. 上位机软件工程师_硬件工程师吐槽起自己来能有多狠?看看,你就知道了

    俗话说,干一行爱一行 现实是,最爱的行业往往伤人最深 无论外人看起来多么高大上的硬件行业 工程师一句话就能让其原形毕露 吐槽起来,那更是一个稳准狠 上次发布的<硬件工程师崩溃图鉴> 就收到 ...

  7. 上位机软件开发项目案例(一)_C#开发

    了解或咨询 上位机软件开发/LabVIEW软件开发/C#软件开发/QT软件开发,请访问AgainDo再度科技官网:www.againdo.com 概述 该软件为线束连接器自动化生产设备上位机软件,系统 ...

  8. 使用C#进行串口通信开发上位机软件

    因为工作需要 曾使用C#开发一款上位机软件,当时工期很赶,开发联调测试交付后就转向其他工作.一直念叨着要记录一下,这次五一终于如愿. 软件开发环境:操作系统windows64位,开发工具:VisioS ...

  9. 基于C#的工控上位机软件开发从入门到学废,需要几步?

    基于C#的工控上位机软件开发从入门到学废,需要几步? 随着工控产品的应用,开始大幅度渗透进民用领域.涉及的工控产品范围广泛,如PLC.DCS.变频器.仪表.电机.SCADA.低压及配电元器件等. 准备 ...

最新文章

  1. 虚拟纹理与几何图像技术
  2. 从粗放到精细,如何用AI技术实现信息流广告投放的降本增效
  3. 关于机房有八台计算出现E盘无法更改盘符的问题
  4. GUI应用程序架构的十年变迁:MVC,MVP,MVVM,Unidirectional,Clean
  5. 区块链系统之《基于区块链的数字身份认证》
  6. Django中提供了6种缓存方式,你会几种?
  7. 容器的综合应用:文本查询程序
  8. 如何给linux目录加密码,怎么只给一个文件夹的内容加密?
  9. jquery中$.each循环的跳出
  10. 网络攻防技术——shellcode编写
  11. 集成电路可测性设计(DFT,Design For Testability)
  12. ubuntu安装anjuta
  13. 基于SmartQQ协议的QQ自动回复机器人-1
  14. [BTS] Unable to create the transform
  15. Android 实现Home按键功能的两种方式
  16. python有什么颜色_Python中常见颜色记录
  17. VS2013中F#的新特性
  18. 【项目整理】安卓应用商店评论监控平台
  19. android微信列表滑动删除,Android仿微信对话列表滑动删除效果
  20. 使用malloc函数分配空间

热门文章

  1. pythonopencv检测行人_行人检测 基于 OpenCV 的人体检测
  2. LabVIEW编程技巧:如何制作安装包程序
  3. 2022年R1快开门式压力容器操作上岗证题库及模拟考试
  4. SOC的第一个Hello_World实验
  5. 打印1000-2000年间的闰年
  6. TL431在电源方面的应用
  7. 计算机转岗测试,软件测试人员转岗哪些岗位
  8. java 超时重试机制_Java之Retry重试机制详解
  9. vue格式化input输入框的数字,4位一分隔
  10. 新发布的 TypeScript 3.5 RC 作出的改进和优化