您的位置:首页 > 其它

鼎信通讯公司学习信息收集----阿冬专栏

2016-06-22 09:50 302 查看
毕业三年后想回青岛,空气好、房价可以接受、研发岗位待遇还不错、户口好解决、消费中等、离家近等等众多因素。感觉最想回青岛。

但是了解了一下青岛的研发大环境:适合的公司主要就是:海尔、海信、中车、鼎信通讯、青岛百灵等

下面收集了一些最想去的鼎信通讯公司的招聘信息,留着以后提醒自己,有个参考

(最好学习C/C++)-------------这个方向最适合

青岛鼎信通讯股份有限公司

2016年招聘简章

青岛鼎信通讯股份有限公司成立于2008年4月,于2012年7月进行股份制改革,注册资本人民币3.9亿元。公司已于2014年5月5日在证监会作IPO预披露,申请上海主板上市。公司是一家集研发、生产、销售为一体的具有自主知识产权的高新技术企业。

青岛被评为全球宜居城市之一,是中国重要的经济中心城市和沿海开放城市,是国家级历史文化名城和风景旅游、度假胜地, 处于山东蓝色半岛经济区的核心位置,下辖与上海浦东平级的第9个国家级新区-西海岸新区。

公司总部位于青岛市核心区域市南区青岛软件园,园内设有6000余平方米的研发中心,在城阳区拥有20000余平方米的生产基地,同时正在建设位于青岛高新区内的35万平米的鼎信高新科技产业园区。2009年9月通过ISO9001:2008质量管理体系认证,2012年通过ISO14001:2008环境体系认证。2012年被青岛市认定为创新型企业。2014年被认定为国家火炬计划重点高新技术企业,拥有CNAS认证实验室一个。公司现有27项专利、32项软件著作权。

公司以载波及总线通信芯片的研发成果为基础,将其应用于电力、消防等嵌入式智能产品的研发、生产、销售,成为一个有扎实基础理论与系统技术的高新技术企业。初步建成了以IC设计、嵌入式软件、自动化生产及自动化设计为基础的企业集团。青岛鼎信通讯股份有限公司下属青岛鼎信通讯电力工程有限公司、青岛鼎信通讯消防安全有限公司、青岛鼎信通讯智能装备有限公司、青岛鼎信通讯科技有限公司、上海鼎信通讯集成电路设计有限公司、青岛鼎信通讯股份有限公司西安分公司、青岛鼎信通讯电力工程有限公司重庆分公司、四川分公司、河北分公司、湖南分公司等子公司和分公司,并在全国31个省、自治区、直辖市设立了办事处。

青岛鼎信通讯股份有限公司致力于电力线载波通信芯片、总线通信芯片的研发及智能电网、消防安防、智能家居等方面的产品应用推广,在扩频通信、信号处理、自动控制、电能计量、电网终端采集、变配电技术、电能质量控制、新能源、计算机应用及机电一体化等领域具有较强的科研、生产能力。根据国家电网数据信息,青岛鼎信通讯的电力线载波采集系列产品,凭借产品的优异性能,在国网供应商中,市场占有率达40%,处于电力线载波芯片行业的领先地位。目前公司有成体系的自动抄表系统,四合一采集控制终端,家用电智能显示终端等系列产品,并在国内外有较好应用(如以色列国家电网、俄罗斯国家电网、南非国家电网等)。自2008年成立至今,公司取得大量的技术积累和不俗的经营业绩,截止到2015年底,公司总营业额累计达36.85亿元。

青岛鼎信通讯股份有限公司成立以来一直遵循“诚信务实、追求卓越、以人为本”的基本原则,同时努力营造良好的沟通环境,为员工提供一个平等、友爱、合作和不断进步的工作氛围。截止2016年2月,公司已有员工2800余人,其中研发人员500余人。在未来,公司将打造千人规模的精英研发团队。

公司为员工提供舒适的工作环境,完善的就职培训体系,良好的福利待遇,有竞争力的薪酬。公司虚位以待,诚挚欢迎您选择鼎信,和鼎信共同成长,并以鼎信为平台实现人生价值。

招聘对象:                                 
一本线大学的2016届毕业生(博士10人,硕士100人,本科200人)
一本线大学的2014级、2015级在读博士、硕士研究生(研发实习生)
专业排名前30%、参加过专业比赛并获奖者优先
党员、班干部、学生会干部优先
学习成绩优秀,思想品德好
良好的沟通能力、吃苦耐劳,有工作热情和团队合作精神

 

招聘专业:
硬件类专业:电气工程、电力电子、电机与电器、电力系统、通信工程、电子信息工程等相关专业。
软件类专业:计算机软件与理论、计算机应用技术、软件工程、通信、应用数学等相关专业(C、C++、JAVA皆可,有手机端APP、机器视觉等相关经验优先)。
自动化类专业:自动化、电气自动化、自动控制、测控技术与仪器、仪器科学与技术、机械电子工程等。
会计类专业:财务、会计相关专业。

招聘岗位:
硬件类岗位:硬件研发工程师、电气工程师、嵌入式研发工程师、电源设计工程师、电路分析测试工程师等;
软件类岗位:编译器开发工程师、驱动层开发工程师、ARM工程师、高级软件工程师(JAVA/C++)手机APP研发工程师、图像识别研发工程师、UI设计工程师、自动化控制系统研发工程师、二位、三维图形处理工程师;
供应链岗位: 元器件分析工程师采购经理、销售经理、订单经理;
营销服务岗位:技术支持工程师;
财务岗位:会计、财务、审计;
后勤支持岗位:营销助理

薪酬及福利待遇:
有竞争力的薪酬:
本科生6000+/月、硕士生7000+/月。(13薪+年终奖金,年终奖=3~6个月工资),,一般签约的时候给20薪,7000+。
博士30万/年起。
研发实习4000+/月。(限硕士)
职称补贴
五险一金
餐费补贴
入职第一年提供免费住宿
出差住勤补贴
差旅费报销、通讯费报销
健康查体、年节福利
生日礼金、生育礼贺
工会、党建、团队活动

招聘流程:
 

简历投递方式:hr@topscomm.com

公司网址:www.topscomm.com


关于这个公司招聘的比较不错的百度贴吧:http://tieba.baidu.com/p/2196698554?pn=1

接触过的笔试题目:

青岛鼎信通笔试题2016春季

主要考查的知识点:

编程语言

数据库

数组排序

查找

面向对象

网络编程中的多线程与多进程

 

1、   用变量a给出下面的定义:

一个整形数

一个指向整型数的指针

一个指向指针的指针、他指向的指针是指向一个整型数

一个有十个整型数的数组

一个有十个指针的数组,该指针是指向一个整型数的

一个指向有十个整型数数组的指针

一个指向函数的指针,该函数有一个整形参数并返回一个整型数

一个有十个指针的数组,该指针指向一个函数,该函数有一个整形参数并返回一个整型数

a) int a; // Aninteger  
b) int *a; // A pointer to an integer  
c) int **a; // A pointer to a pointer to aninteger  
d) int a[10]; // An array of 10 integers  
e) int *a[10]; // An array of 10 pointers tointegers  
f) int (*a)[10]; // A pointer to an array of10 integers  
g) int (*a)(int); // A pointer to a function athat takes an integer 
                     argument and returns aninteger  
h) int (*a[10])(int); // An array of 10pointers to functions that take 
                      an integer argument andreturn an integer  

2、Socket的含义是什么,要两个电脑中的两个程序之间实现TCP Socket通信,需要设置哪些参数,如何工作?

简单理解Socket

SOCKET用于在两个基于TCP/IP协议的应用程序之间相互通信。最早出现在UNIX系统中,是UNIX系统主要的信息传递方式。在WINDOWS系统中,SOCKET称为WINSOCK。两个基本概念:客户方和服务方。当两个应用之间需要采用SOCKET通信时,首先需要在两个应用之间(可能位于同一台机器,也可能位于不同的机器)建立SOCKET连接,发起呼叫连接请求的一方为客户方,接受呼叫连接请求的一方成为服务方。客户方和服务方是相对的,同一个应用可以是客户方,也可以是服务方。在客户方呼叫连接请求之前,它必须知道服务方在哪里。所以需要知道服务方所在机器的IP地址或机器名称,如果客户方和服务方事前有一个约定就好了,这个约定就是PORT(端口号)。也就是说,客户方可以通过服务方所在机器的IP地址或机器名称和端口号唯一的确定方式来呼叫服务方。在客户方呼叫之前,服务方必须处于侦听状态,侦听是否有客户要求建立连接。一旦接到连接请求,服务方可以根据情况建立或拒绝连接。连接方式有两种,同步方式(Blocking)和(noBlocking).客户方发送的消息可以是文本,也可以是二进制信息流。当客户方的消息到达服务方端口时,会自动触发一个事件(event),服务方只要接管该事件,就可以接受来自客户方的消息了。

题外话

前几天和朋友聊天,朋友问我怎么最近不写博客了,一个是因为最近在忙着公司使用的一些控件的开发,浏览器兼容性搞死人;但主要是因为这段时间一直在看html5的东西,看到web socket时觉得很有意思,动手写几个demo,但web
socket需要特定的服务器支持,由于标准制定工作还没完成,所以没有多少主流的服务器支持,自己在网上下载了几个实现,包括PHP的、C#的、甚至Node.js的,但一个是协议变化比较大,很多代码已经过时了,再就是有一些支持最新的标准,但是我想稍微改造一下,看人家源代码的时候云里雾里,看看别人的代码行数也不多,决定自己实现一个。

悲剧由此开始,虽然哥们儿国内非知名工科大学毕业,但好歹也是科班CS出身,但大学得过且过,什么TCP/IP协议,什么socket了都没概念。为了做出一个简单的支持广播的websocket
server,在网上找了很多相关代码,左抄一句,右抄一句,弄了一个星期竟然还是漏洞百出,调试不起来,只好从头来过了,先补一些基本知识,然后再一步步根据原理实现,今天终于实现了绝大部分功能,由此真的感受到了,搞计算机必须得有理论指导实践,否则只能像个没头苍蝇到处乱撞。

TCP/IP

要想理解socket首先得熟悉一下TCP/IP协议族, TCP/IP(Transmission
Control Protocol/Internet Protocol)即传输控制协议/网间协议,定义了主机如何连入因特网及数据如何再它们之间传输的标准,

从字面意思来看TCP/IP是TCP和IP协议的合称,但实际上TCP/IP协议是指因特网整个TCP/IP协议族。不同于ISO模型的七个分层,TCP/IP协议参考模型把所有的TCP/IP系列协议归类到四个抽象层中

应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet等等

传输层:TCP,UDP

网络层:IP,ICMP,OSPF,EIGRP,IGMP

数据链路层:SLIP,CSLIP,PPP,MTU

每一抽象层建立在低一层提供的服务上,并且为高一层提供服务,看起来大概是这样子的

                        

估计有兴趣打开此文的同学都对此有一定了解了,加上我也是一知半解,所以就不详细解释,有兴趣同学可以上网上搜一下资料

在TCP/IP协议中两个因特网主机通过两个路由器和对应的层连接。各主机上的应用通过一些数据通道相互执行读取操作

 

socket

我们知道两个进程如果需要进行通讯最基本的一个前提能能够唯一的标示一个进程,在本地进程通讯中我们可以使用PID来唯一标示一个进程,但PID只在本地唯一,网络中的两个进程PID冲突几率很大,这时候我们需要另辟它径了,我们知道IP层的ip地址可以唯一标示主机,而TCP层协议和端口号可以唯一标示主机的一个进程,这样我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。

能够唯一标示网络中的进程后,它们就可以利用socket进行通信了,什么是socket呢?我们经常把socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。

socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开—读/写—关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。

socket通信流程

socket是"打开—读/写—关闭"模式的实现,以使用TCP协议通讯的socket为例,其交互流程大概是这样子的

服务器根据地址类型(ipv4,ipv6)、socket类型、协议创建socket

服务器为socket绑定ip地址和端口号

服务器socket监听端口号请求,随时准备接收客户端发来的连接,这时候服务器的socket并没有被打开

客户端创建socket

客户端打开socket,根据服务器ip地址和端口号试图连接服务器socket

服务器socket接收到客户端socket请求,被动打开,开始接收客户端请求,直到客户端返回连接信息。这时候socket进入阻塞状态,所谓阻塞即accept()方法一直到客户端返回连接信息后才返回,开始接收下一个客户端谅解请求

客户端连接成功,向服务器发送连接状态信息

服务器accept方法返回,连接成功

客户端向socket写入信息

服务器读取信息

客户端关闭

服务器端关闭

三次握手

在TCP/IP协议中,TCP协议通过三次握手建立一个可靠的连接

第一次握手:客户端尝试连接服务器,向服务器发送syn包(同步序列编号Synchronize Sequence Numbers),syn=j,客户端进入SYN_SEND状态等待服务器确认

第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时向客户端发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态

第三次握手:第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手

定睛一看,服务器socket与客户端socket建立连接的部分其实就是大名鼎鼎的三次握手

 

3、SQL的含义是什么,实例添加、删除、修改、查询语句的写法,用SQL语句建立一个数据库表:职员信息表,包括工号、姓名、性别、出生年月、工资,工号为主键。

4、数据库设计的三范式是什么?

数据库设计三大范式

为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则。在关系型数据库中这种规则就称为范式。范式是符合某一种设计要求的总结。要想设计一个结构合理的关系型数据库,必须满足一定的范式。

                 

在实际开发中最为常见的设计范式有三个:

1.第一范式(确保每列保持原子性)

第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。

第一范式的合理遵循需要根据系统的实际需求来定。比如某些数据库系统中需要用到“地址”这个属性,本来直接将“地址”属性设计成一个数据库表的字段就行。但是如果系统经常会访问“地址”属性中的“城市”部分,那么就非要将“地址”这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。这样设计才算满足了数据库的第一范式,如下表所示。

上表所示的用户信息遵循了第一范式的要求,这样在对用户使用城市进行分类的时候就非常方便,也提高了数据库的性能。

               

2.第二范式(确保表中的每列都和主键相关)

第二范式在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。

比如要设计一个订单信息表,因为订单中可能会有多种商品,所以要将订单编号和商品编号作为数据库表的联合主键,如下表所示。

 订单信息表

这样就产生一个问题:这个表中是以订单编号和商品编号作为联合主键。这样在该表中商品名称、单位、商品价格等信息不与该表的主键相关,而仅仅是与商品编号相关。所以在这里违反了第二范式的设计原则。

而如果把这个订单信息表进行拆分,把商品信息分离到另一个表中,把订单项目表也分离到另一个表中,就非常完美了。如下所示。

这样设计,在很大程度上减小了数据库的冗余。如果要获取订单的商品信息,使用商品编号到商品信息表中查询即可。

                 

3.第三范式(确保每列都和主键列直接相关,而不是间接相关)

第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。

比如在设计一个订单数据表的时候,可以将客户编号作为一个外键和订单表建立相应的关系。而不可以在订单表中添加关于客户其它信息(比如姓名、所属公司等)的字段。如下面这两个表所示的设计就是一个满足第三范式的数据库表。

这样在查询订单信息的时候,就可以使用客户编号来引用客户信息表中的记录,也不必在订单信息表中多次输入客户信息的内容,减小了数据冗余。

 

5、c语言共用体,输出下面程序的运行结果:2

#include<stdio.h>

union

{

   inti;

char x[2];

}

void main()

{

a.x[0] = 3;

a.x[1] =2;

printf(“%d”,a.i);

}

6、static全局变量与普通的全局变量有什么区别,static局部变量与普通的局部变量有什么区别,static函数与普通的函数有什么区别

C语言中讲讲static变量和static函数有什么作用

static关键字有两种意思,你看上下文来判断

1,表示变量是静态存储变量 
表示变量存放在静态存储区. 

2,表示该变量是内部连接 

(这种情况是指该变量不在任何{}之内,就象全局变量那样,这时候加上static) 

,也就是说在其它的.cpp文件中,该变量是不可见的(你不能用).

当static加在函数前面的时候 
表示该函数是内部连接,之在本文件中有效,别的文件中不能应用该函数. 
不加static的函数默认为是全局的. 
也就是说在其他的.cpp中只要申明一下这个函数,就可以使用它. 

1、static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?

    答:全局变量(外部变量)的说明之前再冠以static就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。

    从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。

    static函数与普通函数作用域不同。static函数仅在本文件中使用。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件

    static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;

    static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;

    static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝

 

7、写一段代码进行100个16位无符号整形数组的增序函数

8、写一段代码,对上题中的输出结果进行指定输入数值的相应位置查找函数

9、简述析构函数与虚函数的用法与作用:

析构函数也是特殊的类成员函数,它没有返回类型,没有参数,不能随意调用,也没有重载。只是在类对象生命期结束的时候,由系统自动调用释放在构造函数中分配的资源。这种在运行时,能依据其类型确认调用那个函数的能力称为多态性,或称迟后联编。另: 析构函数一般在对象撤消前做收尾工作,比如回收内存等工作,

虚拟函数的功能是使子类可以用同名的函数对父类函数进行覆盖,并且在调用时自动调用子类覆盖函数,如果是纯虚函数,则纯粹是为了在子类覆盖时有个统一的命名而已。

注意:子类重新定义父类的虚函数的做法叫覆盖,override,而不是overload(重载),重载的概念不属于面向对象编程,重载指的是存在多个同名函数,这些函数的参数表不同..重载是在编译期间就决定了的,是静态的,因此,重载与多态无关.与面向对象编程无关.

析构函数是用来释放所定义的对象中使用的指针,默认的析构函数不用显示调用,自建的析构函数要在程序末尾调用。虚函数可以让成员函数操作一般化,用基类的指针指向不同的派生类的对象时,基类指针调用其虚成员函数,则会调用其真正指向对象的成员函数,而不是基类中定义的成员函数(只要派生类改写了该成员函数)。若不是虚函数,则不管基类指针指向的哪个派生类对象,调用时都会调用基类中定义的那个函数。

10、

简述面向对象的三个基本特征:

阅读 评论(1) 收藏 举报
面向对象的三个基本特征是:封装、继承、多态。
封装
封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
继承
面向对象编程 (OOP)语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
 
继承概念的实现方式有三类:实现继承、接口继承和可视继承。
Ø         实现继承是指使用基类的属性和方法而无需额外编码的能力;
Ø         接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
Ø         可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。
在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee是一个人,Manager也是一个人,因此这两个类都可以继承 Person类。但是 Leg类却不能继承 Person类,因为腿并不是一个人。
抽象类仅定义将由子类创建的一般属性和方法,创建抽象类时,请使用关键字 Interface 而不是 Class。
OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。
 
多态
多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
实现多态,有二种方式,覆盖,重载。
覆盖,是指子类重新定义父类的虚函数的做法。
重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
其实,重载的概念并不属于“面向对象编程”,重载的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的(记住:是静态)。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!真正和多态相关的是“覆盖”。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚邦定)。结论就是:重载只是一种语言特性,与多态无关,与面向对象也无关!引用一句Bruce
Eckel的话:“不要犯傻,如果它不是晚邦定,它就不是多态。”
那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。

 

11、网络编程中设计并发服务器,使用多进程与多线程,请问有什么区别?

 

答案一

1,         进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。 

2,线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。 

两者都可以提高程序的并发度,提高程序运行效率和响应时间。 

线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。 

答案二: 

根本区别就一点:用多进程每个进程有自己的地址空间(address space),线程则共享地址空间。所有其它区别都是由此而来的: 

1。速度:线程产生的速度快,线程间的通讯快、切换快等,因为他们在同一个地址空间内。 

2。资源利用率:线程的资源利用率比较好也是因为他们在同一个地址空间内。 

3。同步问题:线程使用公共变量/内存时需要使用同步机制还是因为他们在同一个地址空间内。

 

补充 并发服务器:

 

并发服务器编程

标签: 服务器bufferstructserversocket编程
2011-07-0610:50 2495人阅读 评论(0) 收藏 举报
 分类:
 

C语言(13) 

版权声明:本文为博主原创文章,未经博主允许不得转载。

目录(?)[+]
 

并发服务器是socket应用编程中最常见的应用模型。并发服务器模型根据连接方式分为长连接和短连接,长连接为通信双方建立连接后一直保持连接,然后一直用此连接进行读写操作;短连接为通信双方每一次交易过程都建立连接和关闭连接。并发服务器模型根据处理方式可分为同步方式和异步方式,同步是客户端发送请求给服务器等待服务器返回处理结果;异步是指客户端发送请求给服务器,不等待服务器返回处理结果,而直接去完成其他的流程,对于处理结果客户端可以事后查询和让服务器进行主动通知。

1.   并发服务器编程注意事项

进程是一个程序的一次运行过程,它是一个动态实体,是独立的任务,它拥有独立的地址空间、执行堆栈、文件描述符等。每个进程拥有独立的地址空间,在进程不存在父子关系的情况下,互不影响。

进程的终止存在两个可能:父进程先于子进程终止(由init进程领养),子进程先于主进程终止。对于后者,系统内核为子进程保留一定的状态信息(进程ID、终止状态、CPU时间等),并向其父进程发送SIGCHLD信号。当父进程调用wait或waitpid函数时,将获取这些信息,获取后内核将对僵尸进程进行清理。如果父进程设置了忽略SIGCHLD信号或对SIGCHLD信号提供了处理函数,即使不调用wait或waitpid函数内核也会清理僵尸进程。

但父进程调用wait函数处理子进程退出信息时,会存在下面所述的问题。在有多个子进程情况下,wait函数只等待最先到达的子进程的终止信息。下图18-7父进程有3个子进程,由于SIGCHLD信号不排队,在SIGCHLD信号同时到来后,父进程的wait函数只执行一次,这样将留下2个僵尸进程,而使用waitpid函数并设置WNOHANG选项可以解决这个问题。

图18-7 多进程信号图

综上所述,在多进程并发的情况下,防止子进程变成僵尸进程常见有如下两种方法:

①    父进程调用signal(SIGCHLD,SIG_IGN)对子进程退出信号进行忽略,或者把SIG_IGN替换为其他处理函数,设置对SIGCHLD信号的处理。

②    父进程调用waitpid(-1,NULL,WNOHANG)对所有子进程SIGCHLD信号进行处理。

2.   并发服务器文件描述符变化图

图18-8~图18-11画出了并发服务器文件描述符的变化流程图。其中listenfd为服务端的socket监听文件描述符,connfd为accept函数返回的socket连接文件描述符。

服务器调用accept函数后,客户与服务器文件描述符如下图18-8所示。

图18-8 调用accept函数时套接字描述符图

 

服务器调用accept函数后,客户与服务器文件描述符如下图18-9所示。

 

图18-9调用accept函数后套接字描述符图

 

服务器调用fork函数后,客户与服务器文件描述符如下图18-10所示。

 

图18-10调用fork函数后套接字描述符图

 

服务端父进程关闭连接套接字,子进程关闭监听套接字,客户与服务器文件描述符状况如下图18-11所示。

 

图18-11 并发服务器最终连接图

在这里强调的是,并发服务器fork后父进程一定要关闭子进程连接套接字;而子进程要关闭父进程监听套接字,以免误操作。

1.   TCP并发服务器代码实现

(1)并发服务器处理流程

   并发服务器处理流程如下:

①    客户端首先发起连接。

②    服务端进程accept打开一个新的连接套接字与客户端进行连接,accept在一个while(1)循环内等待客户端的连接。

③    服务端fork一个子进程,同时父进程close子进程连接套接字,循环等待下一进程。

④    服务端子进程close父进程监听套接字,并用连接套接字保持与客户端的连接,客户发送数据到服务端,然后阻塞等待服务端返回。

⑤    子进程进行接收数据,进行业务处理,然后再发送数据给客户端。

⑥    子进程关闭连接,然后退出。

 

(2)程序报文协议说明

该程序报文协议模式为常见行业应用软件协议模式,其具体说明如下:

发送和接收报文协议:8位报文长度(不包含本身)+6位交易码+报文内容,交易码标识该交易的类型。

实际应用中服务端进程根据6位交易码调度不同的应用服务。

 

(3)并发服务器服务端代码

tcpsrv.c源代码如下:

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<string.h>

#include<netdb.h>

#include<sys/types.h>

#include<sys/socket.h>

#include<netinet/in.h>

#include<netinet/tcp.h>

#include<errno.h>

#include<sys/wait.h>

#defineMY_PORT 10000

externint readn(int fd,void *buffer,int length) ;

externint writen(int fd,void *buffer,int length) ;

intmain(int argc ,char **argv)

{

   int listen_fd,accept_fd;

   struct sockaddr_in server_addr;

   struct sockaddr_in cli_addr;

   int n;

   int cliaddr_len ;

   char buffer[1024];

   char data[1024] ;

   long length ;

   int nbytes ;

   if((listen_fd=socket(AF_INET,SOCK_STREAM,0))<0)

   {

       printf("Socket Error:%s\n",strerror(errno));

       return -1;

   }

   memset(&server_addr,0x00, sizeof(struct sockaddr_in));

   memset(&cli_addr,0x00, sizeof(struct sockaddr_in));

   server_addr.sin_family=AF_INET;

   server_addr.sin_port=htons(MY_PORT);

   server_addr.sin_addr.s_addr=htonl(INADDR_ANY);

   n=1;

   /* 如果服务器终止后,服务器可以第二次快速启动而不用等待一段时间*/

   setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&n,sizeof(int));

   if(bind(listen_fd,(struct sockaddr *)&server_addr,sizeof(server_addr))<0)

   {

       printf("Bind Error:%s\n",strerror(errno));

       return -1;

   }

   listen(listen_fd,5);

   while(1)

   {

       cliaddr_len= sizeof( cli_addr ) ;

       accept_fd=accept(listen_fd, (struct sockaddr *)&cli_addr, &cliaddr_len);

       if((accept_fd<0)&&(errno==EINTR))

           continue;

       else if(accept_fd<0)

       {

           printf("Accept Error:%s\n",strerror(errno));

           continue;

       }

       if((n=fork())==0)

       {

           /* 子进程处理客户端的连接*/

           fprintf(stdout,"listen_fd:%d accept_fd:%d\n",listen_fd, accept_fd);

           close(listen_fd);

           memset(buffer, 0x00, sizeof(buffer)) ;

           memset(data, 0x00, sizeof(data));

           if((nbytes=readn(accept_fd, data, 8 ))==-1)

           {

               fprintf(stderr,"Read Error:%s\n",strerror(errno));

               fprintf(stderr,"data:%s\n",data );

               close(accept_fd);

               return -1;

           }

           fprintf(stdout,"data:%s,nbytes=%d\n",data, nbytes );

           data[nbytes]='\0' ;

           length=atol(data) ;

           fprintf(stdout,"data:%s,nbytes=%d\n",data, nbytes );

           if((nbytes=readn(accept_fd, data, length ))==-1)

           {

               fprintf(stderr,"Read Error:%s\n",strerror(errno));

               close(accept_fd);

               return -1;

           }

           data[nbytes]='\0' ;

           fprintf(stdout,"data:%s,nbytes=%d\n",data, nbytes );

           if( strncmp(data, "000000", 6 )==0 )

           {

               strcpy(buffer, "I am sorry! who am  I? I don't know also.") ;

               length=strlen(buffer) ;

               sprintf(data,"%08ld%6.6s%s", (length+6),"000000", buffer );

               if((nbytes=writen(accept_fd,data, (length+6+8)))==-1)

               {

                   fprintf(stderr,"Read Error:%s\n",strerror(errno));

                   close(accept_fd);

                   return -1;

               }

               fprintf(stdout,"data:%s\n",data );

           }else{

               /*非000000交易请求为非法,沉默是最好的回答*/

           }

           close(accept_fd);

           return 0;

       }

       else if(n<0)

           printf("Fork Error:%s\n\a",strerror(errno));

       close(accept_fd);

       while(waitpid(-1,NULL,WNOHANG) > 0); /* clean up child processes */ 

   }

}

 

(4)客户端代码

tcpcli.c源代码如下:

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<string.h>

#include<netdb.h>

#include<sys/types.h>

#include<sys/socket.h>

#include<netinet/in.h>

#include<netinet/tcp.h>

#include<errno.h>

externint readn(int fd,void *buffer,int length) ;

externint writen(int fd,void *buffer,int length) ;

intmain(int argc, char *argv[])

{

   int sockfd;

   char buffer[1024];

   char data[1024];

   long length ;

   struct sockaddr_in server_addr;

   struct hostent *host;

   int portnumber,nbytes;

   if(argc!=3)

   {

       fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);

       return -1;

   }

   if((host=gethostbyname(argv[1]))==NULL)

   {

       fprintf(stderr,"Gethostname error\n");

       return -1;

   }

   if((portnumber=atoi(argv[2]))<0)

   {

       fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);

       return -1;

   }

   /* 客户程序开始建立sockfd描述符*/

   if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)

   {

       fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));

       return -1;

   }

   /* 客户程序填充服务端的地址端口信息*/

   bzero(&server_addr,sizeof(server_addr));

   server_addr.sin_family=AF_INET;

   server_addr.sin_port=htons(portnumber);

  server_addr.sin_addr= *((struct in_addr *)host->h_addr);

   /* 客户程序发起连接请求*/

   if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr)

             )==-1)

   {

       fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));

       return -1;

   }

   memset(buffer, 0x00, sizeof(buffer)) ;

   memset(data, 0x00, sizeof(data));

   strcpy(buffer, "Hello! who are  you? could you tell me?") ;

   length=strlen(buffer) ; 

   /***000000为假设的交易码***/

   sprintf(data,"%08ld%6.6s%s", (length+6),"000000", buffer );

   length=length+8+6 ;

   if((nbytes=writen(sockfd,data, length ))==-1)

   {

       fprintf(stderr,"Read Error:%s\n",strerror(errno));

       close(sockfd);

       return -1;

   }

   printf("I have send:%s\n", data+8);

   if((nbytes=readn(sockfd, data, 8 ))==-1)

   {

       fprintf(stderr,"Read Error:%s\n",strerror(errno));

       close(sockfd);

       return -1;

   }

   data[nbytes]='\0' ;

   length=atol(data) ;

   if((nbytes=readn(sockfd, data, length ))==-1)

   {

       fprintf(stderr,"ReadError:%s\n",strerror(errno));

       close(sockfd);

       return -1;

   }

   data[nbytes]='\0' ;

   printf("I have received:%s\n", data);

   close(sockfd);

   return 0;

}

(5)编译与执行

编译 gcc tcpsrv.c  tcpio.c -otcpsrv。

编译 gcc tcpcli.c  tcpio.c -otcpcli。

在一界面下启动服务端进程 ./tcpsrv。

在另一界面下执行./tcpcli 127.0.0.1 10000,执行结果如下:

I havesend:000000Hello! who are  you? could you tell me?

I have received:000000Iam sorry! who am  I? I don't know also.

 

 

 

补充:

关键字:

volatile

用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。volatile很容易被误用,用来进行原子性操作。

 

transient的用途

Q:transient关键字能实现什么?

A:当对象被序列化时(写入字节序列到目标文件)时,transient阻止实例中那些用此关键字声明的变量持久化;当对象被反序列化时(从源文件读取字节序列进行重构),这样的实例变量值不会被持久化和恢复。例如,当反序列化对象——数据流(例如,文件)可能不存在时,原因是你的对象中存在类型为Java.io.InputStream的变量,序列化时这些变量引用的输入流无法被打开。

毕业三年后想回青岛,空气好、房价可以接受、研发岗位待遇还不错、户口好解决、消费中等、离家近等等众多因素。感觉最想回青岛。

但是了解了一下青岛的研发大环境:适合的公司主要就是:海尔、海信、中车、鼎信通讯、青岛百灵等

下面收集了一些最想去的鼎信通讯公司的招聘信息,留着以后提醒自己,有个参考

(最好学习C/C++)-------------这个方向最适合

青岛鼎信通讯股份有限公司

2016年招聘简章

青岛鼎信通讯股份有限公司成立于2008年4月,于2012年7月进行股份制改革,注册资本人民币3.9亿元。公司已于2014年5月5日在证监会作IPO预披露,申请上海主板上市。公司是一家集研发、生产、销售为一体的具有自主知识产权的高新技术企业。

青岛被评为全球宜居城市之一,是中国重要的经济中心城市和沿海开放城市,是国家级历史文化名城和风景旅游、度假胜地, 处于山东蓝色半岛经济区的核心位置,下辖与上海浦东平级的第9个国家级新区-西海岸新区。

公司总部位于青岛市核心区域市南区青岛软件园,园内设有6000余平方米的研发中心,在城阳区拥有20000余平方米的生产基地,同时正在建设位于青岛高新区内的35万平米的鼎信高新科技产业园区。2009年9月通过ISO9001:2008质量管理体系认证,2012年通过ISO14001:2008环境体系认证。2012年被青岛市认定为创新型企业。2014年被认定为国家火炬计划重点高新技术企业,拥有CNAS认证实验室一个。公司现有27项专利、32项软件著作权。

公司以载波及总线通信芯片的研发成果为基础,将其应用于电力、消防等嵌入式智能产品的研发、生产、销售,成为一个有扎实基础理论与系统技术的高新技术企业。初步建成了以IC设计、嵌入式软件、自动化生产及自动化设计为基础的企业集团。青岛鼎信通讯股份有限公司下属青岛鼎信通讯电力工程有限公司、青岛鼎信通讯消防安全有限公司、青岛鼎信通讯智能装备有限公司、青岛鼎信通讯科技有限公司、上海鼎信通讯集成电路设计有限公司、青岛鼎信通讯股份有限公司西安分公司、青岛鼎信通讯电力工程有限公司重庆分公司、四川分公司、河北分公司、湖南分公司等子公司和分公司,并在全国31个省、自治区、直辖市设立了办事处。

青岛鼎信通讯股份有限公司致力于电力线载波通信芯片、总线通信芯片的研发及智能电网、消防安防、智能家居等方面的产品应用推广,在扩频通信、信号处理、自动控制、电能计量、电网终端采集、变配电技术、电能质量控制、新能源、计算机应用及机电一体化等领域具有较强的科研、生产能力。根据国家电网数据信息,青岛鼎信通讯的电力线载波采集系列产品,凭借产品的优异性能,在国网供应商中,市场占有率达40%,处于电力线载波芯片行业的领先地位。目前公司有成体系的自动抄表系统,四合一采集控制终端,家用电智能显示终端等系列产品,并在国内外有较好应用(如以色列国家电网、俄罗斯国家电网、南非国家电网等)。自2008年成立至今,公司取得大量的技术积累和不俗的经营业绩,截止到2015年底,公司总营业额累计达36.85亿元。

青岛鼎信通讯股份有限公司成立以来一直遵循“诚信务实、追求卓越、以人为本”的基本原则,同时努力营造良好的沟通环境,为员工提供一个平等、友爱、合作和不断进步的工作氛围。截止2016年2月,公司已有员工2800余人,其中研发人员500余人。在未来,公司将打造千人规模的精英研发团队。

公司为员工提供舒适的工作环境,完善的就职培训体系,良好的福利待遇,有竞争力的薪酬。公司虚位以待,诚挚欢迎您选择鼎信,和鼎信共同成长,并以鼎信为平台实现人生价值。

招聘对象:                                 
一本线大学的2016届毕业生(博士10人,硕士100人,本科200人)
一本线大学的2014级、2015级在读博士、硕士研究生(研发实习生)
专业排名前30%、参加过专业比赛并获奖者优先
党员、班干部、学生会干部优先
学习成绩优秀,思想品德好
良好的沟通能力、吃苦耐劳,有工作热情和团队合作精神

 

招聘专业:
硬件类专业:电气工程、电力电子、电机与电器、电力系统、通信工程、电子信息工程等相关专业。
软件类专业:计算机软件与理论、计算机应用技术、软件工程、通信、应用数学等相关专业(C、C++、JAVA皆可,有手机端APP、机器视觉等相关经验优先)。
自动化类专业:自动化、电气自动化、自动控制、测控技术与仪器、仪器科学与技术、机械电子工程等。
会计类专业:财务、会计相关专业。

招聘岗位:
硬件类岗位:硬件研发工程师、电气工程师、嵌入式研发工程师、电源设计工程师、电路分析测试工程师等;
软件类岗位:编译器开发工程师、驱动层开发工程师、ARM工程师、高级软件工程师(JAVA/C++)手机APP研发工程师、图像识别研发工程师、UI设计工程师、自动化控制系统研发工程师、二位、三维图形处理工程师;
供应链岗位: 元器件分析工程师采购经理、销售经理、订单经理;
营销服务岗位:技术支持工程师;
财务岗位:会计、财务、审计;
后勤支持岗位:营销助理

薪酬及福利待遇:
有竞争力的薪酬:
本科生6000+/月、硕士生7000+/月。(13薪+年终奖金,年终奖=3~6个月工资),,一般签约的时候给20薪,7000+。
博士30万/年起。
研发实习4000+/月。(限硕士)
职称补贴
五险一金
餐费补贴
入职第一年提供免费住宿
出差住勤补贴
差旅费报销、通讯费报销
健康查体、年节福利
生日礼金、生育礼贺
工会、党建、团队活动

招聘流程:
 

简历投递方式:hr@topscomm.com

公司网址:www.topscomm.com


关于这个公司招聘的比较不错的百度贴吧:http://tieba.baidu.com/p/2196698554?pn=1

接触过的笔试题目:

青岛鼎信通笔试题2016春季

主要考查的知识点:

编程语言

数据库

数组排序

查找

面向对象

网络编程中的多线程与多进程

 

1、   用变量a给出下面的定义:

一个整形数

一个指向整型数的指针

一个指向指针的指针、他指向的指针是指向一个整型数

一个有十个整型数的数组

一个有十个指针的数组,该指针是指向一个整型数的

一个指向有十个整型数数组的指针

一个指向函数的指针,该函数有一个整形参数并返回一个整型数

一个有十个指针的数组,该指针指向一个函数,该函数有一个整形参数并返回一个整型数

a) int a; // Aninteger  
b) int *a; // A pointer to an integer  
c) int **a; // A pointer to a pointer to aninteger  
d) int a[10]; // An array of 10 integers  
e) int *a[10]; // An array of 10 pointers tointegers  
f) int (*a)[10]; // A pointer to an array of10 integers  
g) int (*a)(int); // A pointer to a function athat takes an integer 
                     argument and returns aninteger  
h) int (*a[10])(int); // An array of 10pointers to functions that take 
                      an integer argument andreturn an integer  

2、Socket的含义是什么,要两个电脑中的两个程序之间实现TCP Socket通信,需要设置哪些参数,如何工作?

简单理解Socket

SOCKET用于在两个基于TCP/IP协议的应用程序之间相互通信。最早出现在UNIX系统中,是UNIX系统主要的信息传递方式。在WINDOWS系统中,SOCKET称为WINSOCK。两个基本概念:客户方和服务方。当两个应用之间需要采用SOCKET通信时,首先需要在两个应用之间(可能位于同一台机器,也可能位于不同的机器)建立SOCKET连接,发起呼叫连接请求的一方为客户方,接受呼叫连接请求的一方成为服务方。客户方和服务方是相对的,同一个应用可以是客户方,也可以是服务方。在客户方呼叫连接请求之前,它必须知道服务方在哪里。所以需要知道服务方所在机器的IP地址或机器名称,如果客户方和服务方事前有一个约定就好了,这个约定就是PORT(端口号)。也就是说,客户方可以通过服务方所在机器的IP地址或机器名称和端口号唯一的确定方式来呼叫服务方。在客户方呼叫之前,服务方必须处于侦听状态,侦听是否有客户要求建立连接。一旦接到连接请求,服务方可以根据情况建立或拒绝连接。连接方式有两种,同步方式(Blocking)和(noBlocking).客户方发送的消息可以是文本,也可以是二进制信息流。当客户方的消息到达服务方端口时,会自动触发一个事件(event),服务方只要接管该事件,就可以接受来自客户方的消息了。

题外话

前几天和朋友聊天,朋友问我怎么最近不写博客了,一个是因为最近在忙着公司使用的一些控件的开发,浏览器兼容性搞死人;但主要是因为这段时间一直在看html5的东西,看到web socket时觉得很有意思,动手写几个demo,但web
socket需要特定的服务器支持,由于标准制定工作还没完成,所以没有多少主流的服务器支持,自己在网上下载了几个实现,包括PHP的、C#的、甚至Node.js的,但一个是协议变化比较大,很多代码已经过时了,再就是有一些支持最新的标准,但是我想稍微改造一下,看人家源代码的时候云里雾里,看看别人的代码行数也不多,决定自己实现一个。

悲剧由此开始,虽然哥们儿国内非知名工科大学毕业,但好歹也是科班CS出身,但大学得过且过,什么TCP/IP协议,什么socket了都没概念。为了做出一个简单的支持广播的websocket
server,在网上找了很多相关代码,左抄一句,右抄一句,弄了一个星期竟然还是漏洞百出,调试不起来,只好从头来过了,先补一些基本知识,然后再一步步根据原理实现,今天终于实现了绝大部分功能,由此真的感受到了,搞计算机必须得有理论指导实践,否则只能像个没头苍蝇到处乱撞。

TCP/IP

要想理解socket首先得熟悉一下TCP/IP协议族, TCP/IP(Transmission
Control Protocol/Internet Protocol)即传输控制协议/网间协议,定义了主机如何连入因特网及数据如何再它们之间传输的标准,

从字面意思来看TCP/IP是TCP和IP协议的合称,但实际上TCP/IP协议是指因特网整个TCP/IP协议族。不同于ISO模型的七个分层,TCP/IP协议参考模型把所有的TCP/IP系列协议归类到四个抽象层中

应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet等等

传输层:TCP,UDP

网络层:IP,ICMP,OSPF,EIGRP,IGMP

数据链路层:SLIP,CSLIP,PPP,MTU

每一抽象层建立在低一层提供的服务上,并且为高一层提供服务,看起来大概是这样子的

                        

估计有兴趣打开此文的同学都对此有一定了解了,加上我也是一知半解,所以就不详细解释,有兴趣同学可以上网上搜一下资料

在TCP/IP协议中两个因特网主机通过两个路由器和对应的层连接。各主机上的应用通过一些数据通道相互执行读取操作

 

socket

我们知道两个进程如果需要进行通讯最基本的一个前提能能够唯一的标示一个进程,在本地进程通讯中我们可以使用PID来唯一标示一个进程,但PID只在本地唯一,网络中的两个进程PID冲突几率很大,这时候我们需要另辟它径了,我们知道IP层的ip地址可以唯一标示主机,而TCP层协议和端口号可以唯一标示主机的一个进程,这样我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。

能够唯一标示网络中的进程后,它们就可以利用socket进行通信了,什么是socket呢?我们经常把socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。

socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开—读/写—关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。

socket通信流程

socket是"打开—读/写—关闭"模式的实现,以使用TCP协议通讯的socket为例,其交互流程大概是这样子的

服务器根据地址类型(ipv4,ipv6)、socket类型、协议创建socket

服务器为socket绑定ip地址和端口号

服务器socket监听端口号请求,随时准备接收客户端发来的连接,这时候服务器的socket并没有被打开

客户端创建socket

客户端打开socket,根据服务器ip地址和端口号试图连接服务器socket

服务器socket接收到客户端socket请求,被动打开,开始接收客户端请求,直到客户端返回连接信息。这时候socket进入阻塞状态,所谓阻塞即accept()方法一直到客户端返回连接信息后才返回,开始接收下一个客户端谅解请求

客户端连接成功,向服务器发送连接状态信息

服务器accept方法返回,连接成功

客户端向socket写入信息

服务器读取信息

客户端关闭

服务器端关闭

三次握手

在TCP/IP协议中,TCP协议通过三次握手建立一个可靠的连接

第一次握手:客户端尝试连接服务器,向服务器发送syn包(同步序列编号Synchronize Sequence Numbers),syn=j,客户端进入SYN_SEND状态等待服务器确认

第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时向客户端发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态

第三次握手:第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手

定睛一看,服务器socket与客户端socket建立连接的部分其实就是大名鼎鼎的三次握手

 

3、SQL的含义是什么,实例添加、删除、修改、查询语句的写法,用SQL语句建立一个数据库表:职员信息表,包括工号、姓名、性别、出生年月、工资,工号为主键。

4、数据库设计的三范式是什么?

数据库设计三大范式

为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则。在关系型数据库中这种规则就称为范式。范式是符合某一种设计要求的总结。要想设计一个结构合理的关系型数据库,必须满足一定的范式。

                 

在实际开发中最为常见的设计范式有三个:

1.第一范式(确保每列保持原子性)

第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。

第一范式的合理遵循需要根据系统的实际需求来定。比如某些数据库系统中需要用到“地址”这个属性,本来直接将“地址”属性设计成一个数据库表的字段就行。但是如果系统经常会访问“地址”属性中的“城市”部分,那么就非要将“地址”这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。这样设计才算满足了数据库的第一范式,如下表所示。

上表所示的用户信息遵循了第一范式的要求,这样在对用户使用城市进行分类的时候就非常方便,也提高了数据库的性能。

               

2.第二范式(确保表中的每列都和主键相关)

第二范式在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。

比如要设计一个订单信息表,因为订单中可能会有多种商品,所以要将订单编号和商品编号作为数据库表的联合主键,如下表所示。

 订单信息表

这样就产生一个问题:这个表中是以订单编号和商品编号作为联合主键。这样在该表中商品名称、单位、商品价格等信息不与该表的主键相关,而仅仅是与商品编号相关。所以在这里违反了第二范式的设计原则。

而如果把这个订单信息表进行拆分,把商品信息分离到另一个表中,把订单项目表也分离到另一个表中,就非常完美了。如下所示。

这样设计,在很大程度上减小了数据库的冗余。如果要获取订单的商品信息,使用商品编号到商品信息表中查询即可。

                 

3.第三范式(确保每列都和主键列直接相关,而不是间接相关)

第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。

比如在设计一个订单数据表的时候,可以将客户编号作为一个外键和订单表建立相应的关系。而不可以在订单表中添加关于客户其它信息(比如姓名、所属公司等)的字段。如下面这两个表所示的设计就是一个满足第三范式的数据库表。

这样在查询订单信息的时候,就可以使用客户编号来引用客户信息表中的记录,也不必在订单信息表中多次输入客户信息的内容,减小了数据冗余。

 

5、c语言共用体,输出下面程序的运行结果:2

#include<stdio.h>

union

{

   inti;

char x[2];

}

void main()

{

a.x[0] = 3;

a.x[1] =2;

printf(“%d”,a.i);

}

6、static全局变量与普通的全局变量有什么区别,static局部变量与普通的局部变量有什么区别,static函数与普通的函数有什么区别

C语言中讲讲static变量和static函数有什么作用

static关键字有两种意思,你看上下文来判断

1,表示变量是静态存储变量 
表示变量存放在静态存储区. 

2,表示该变量是内部连接 

(这种情况是指该变量不在任何{}之内,就象全局变量那样,这时候加上static) 

,也就是说在其它的.cpp文件中,该变量是不可见的(你不能用).

当static加在函数前面的时候 
表示该函数是内部连接,之在本文件中有效,别的文件中不能应用该函数. 
不加static的函数默认为是全局的. 
也就是说在其他的.cpp中只要申明一下这个函数,就可以使用它. 

1、static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?

    答:全局变量(外部变量)的说明之前再冠以static就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。

    从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。

    static函数与普通函数作用域不同。static函数仅在本文件中使用。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件

    static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;

    static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;

    static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝

 

7、写一段代码进行100个16位无符号整形数组的增序函数

8、写一段代码,对上题中的输出结果进行指定输入数值的相应位置查找函数

9、简述析构函数与虚函数的用法与作用:

析构函数也是特殊的类成员函数,它没有返回类型,没有参数,不能随意调用,也没有重载。只是在类对象生命期结束的时候,由系统自动调用释放在构造函数中分配的资源。这种在运行时,能依据其类型确认调用那个函数的能力称为多态性,或称迟后联编。另: 析构函数一般在对象撤消前做收尾工作,比如回收内存等工作,

虚拟函数的功能是使子类可以用同名的函数对父类函数进行覆盖,并且在调用时自动调用子类覆盖函数,如果是纯虚函数,则纯粹是为了在子类覆盖时有个统一的命名而已。

注意:子类重新定义父类的虚函数的做法叫覆盖,override,而不是overload(重载),重载的概念不属于面向对象编程,重载指的是存在多个同名函数,这些函数的参数表不同..重载是在编译期间就决定了的,是静态的,因此,重载与多态无关.与面向对象编程无关.

析构函数是用来释放所定义的对象中使用的指针,默认的析构函数不用显示调用,自建的析构函数要在程序末尾调用。虚函数可以让成员函数操作一般化,用基类的指针指向不同的派生类的对象时,基类指针调用其虚成员函数,则会调用其真正指向对象的成员函数,而不是基类中定义的成员函数(只要派生类改写了该成员函数)。若不是虚函数,则不管基类指针指向的哪个派生类对象,调用时都会调用基类中定义的那个函数。

10、

简述面向对象的三个基本特征:

阅读 评论(1) 收藏 举报
面向对象的三个基本特征是:封装、继承、多态。
封装
封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
继承
面向对象编程 (OOP)语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
在某些 OOP语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。
 
继承概念的实现方式有三类:实现继承、接口继承和可视继承。
Ø         实现继承是指使用基类的属性和方法而无需额外编码的能力;
Ø         接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
Ø         可视继承是指子窗体(类)使用基窗体(类)的外观和实现代码的能力。
在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee是一个人,Manager也是一个人,因此这两个类都可以继承 Person类。但是 Leg类却不能继承 Person类,因为腿并不是一个人。
抽象类仅定义将由子类创建的一般属性和方法,创建抽象类时,请使用关键字 Interface 而不是 Class。
OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。
 
多态
多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
实现多态,有二种方式,覆盖,重载。
覆盖,是指子类重新定义父类的虚函数的做法。
重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。
其实,重载的概念并不属于“面向对象编程”,重载的实现是:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的(记住:是静态)。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!真正和多态相关的是“覆盖”。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态(记住:是动态!)的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚邦定)。结论就是:重载只是一种语言特性,与多态无关,与面向对象也无关!引用一句Bruce
Eckel的话:“不要犯傻,如果它不是晚邦定,它就不是多态。”
那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。

 

11、网络编程中设计并发服务器,使用多进程与多线程,请问有什么区别?

 

答案一

1,         进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。 

2,线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。 

两者都可以提高程序的并发度,提高程序运行效率和响应时间。 

线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。 

答案二: 

根本区别就一点:用多进程每个进程有自己的地址空间(address space),线程则共享地址空间。所有其它区别都是由此而来的: 

1。速度:线程产生的速度快,线程间的通讯快、切换快等,因为他们在同一个地址空间内。 

2。资源利用率:线程的资源利用率比较好也是因为他们在同一个地址空间内。 

3。同步问题:线程使用公共变量/内存时需要使用同步机制还是因为他们在同一个地址空间内。

 

补充 并发服务器:

 

并发服务器编程

标签: 服务器bufferstructserversocket编程
2011-07-0610:50 2495人阅读 评论(0) 收藏 举报
 分类:
 

C语言(13) 

版权声明:本文为博主原创文章,未经博主允许不得转载。

目录(?)[+]
 

并发服务器是socket应用编程中最常见的应用模型。并发服务器模型根据连接方式分为长连接和短连接,长连接为通信双方建立连接后一直保持连接,然后一直用此连接进行读写操作;短连接为通信双方每一次交易过程都建立连接和关闭连接。并发服务器模型根据处理方式可分为同步方式和异步方式,同步是客户端发送请求给服务器等待服务器返回处理结果;异步是指客户端发送请求给服务器,不等待服务器返回处理结果,而直接去完成其他的流程,对于处理结果客户端可以事后查询和让服务器进行主动通知。

1.   并发服务器编程注意事项

进程是一个程序的一次运行过程,它是一个动态实体,是独立的任务,它拥有独立的地址空间、执行堆栈、文件描述符等。每个进程拥有独立的地址空间,在进程不存在父子关系的情况下,互不影响。

进程的终止存在两个可能:父进程先于子进程终止(由init进程领养),子进程先于主进程终止。对于后者,系统内核为子进程保留一定的状态信息(进程ID、终止状态、CPU时间等),并向其父进程发送SIGCHLD信号。当父进程调用wait或waitpid函数时,将获取这些信息,获取后内核将对僵尸进程进行清理。如果父进程设置了忽略SIGCHLD信号或对SIGCHLD信号提供了处理函数,即使不调用wait或waitpid函数内核也会清理僵尸进程。

但父进程调用wait函数处理子进程退出信息时,会存在下面所述的问题。在有多个子进程情况下,wait函数只等待最先到达的子进程的终止信息。下图18-7父进程有3个子进程,由于SIGCHLD信号不排队,在SIGCHLD信号同时到来后,父进程的wait函数只执行一次,这样将留下2个僵尸进程,而使用waitpid函数并设置WNOHANG选项可以解决这个问题。

图18-7 多进程信号图

综上所述,在多进程并发的情况下,防止子进程变成僵尸进程常见有如下两种方法:

①    父进程调用signal(SIGCHLD,SIG_IGN)对子进程退出信号进行忽略,或者把SIG_IGN替换为其他处理函数,设置对SIGCHLD信号的处理。

②    父进程调用waitpid(-1,NULL,WNOHANG)对所有子进程SIGCHLD信号进行处理。

2.   并发服务器文件描述符变化图

图18-8~图18-11画出了并发服务器文件描述符的变化流程图。其中listenfd为服务端的socket监听文件描述符,connfd为accept函数返回的socket连接文件描述符。

服务器调用accept函数后,客户与服务器文件描述符如下图18-8所示。

图18-8 调用accept函数时套接字描述符图

 

服务器调用accept函数后,客户与服务器文件描述符如下图18-9所示。

 

图18-9调用accept函数后套接字描述符图

 

服务器调用fork函数后,客户与服务器文件描述符如下图18-10所示。

 

图18-10调用fork函数后套接字描述符图

 

服务端父进程关闭连接套接字,子进程关闭监听套接字,客户与服务器文件描述符状况如下图18-11所示。

 

图18-11 并发服务器最终连接图

在这里强调的是,并发服务器fork后父进程一定要关闭子进程连接套接字;而子进程要关闭父进程监听套接字,以免误操作。

1.   TCP并发服务器代码实现

(1)并发服务器处理流程

   并发服务器处理流程如下:

①    客户端首先发起连接。

②    服务端进程accept打开一个新的连接套接字与客户端进行连接,accept在一个while(1)循环内等待客户端的连接。

③    服务端fork一个子进程,同时父进程close子进程连接套接字,循环等待下一进程。

④    服务端子进程close父进程监听套接字,并用连接套接字保持与客户端的连接,客户发送数据到服务端,然后阻塞等待服务端返回。

⑤    子进程进行接收数据,进行业务处理,然后再发送数据给客户端。

⑥    子进程关闭连接,然后退出。

 

(2)程序报文协议说明

该程序报文协议模式为常见行业应用软件协议模式,其具体说明如下:

发送和接收报文协议:8位报文长度(不包含本身)+6位交易码+报文内容,交易码标识该交易的类型。

实际应用中服务端进程根据6位交易码调度不同的应用服务。

 

(3)并发服务器服务端代码

tcpsrv.c源代码如下:

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<string.h>

#include<netdb.h>

#include<sys/types.h>

#include<sys/socket.h>

#include<netinet/in.h>

#include<netinet/tcp.h>

#include<errno.h>

#include<sys/wait.h>

#defineMY_PORT 10000

externint readn(int fd,void *buffer,int length) ;

externint writen(int fd,void *buffer,int length) ;

intmain(int argc ,char **argv)

{

   int listen_fd,accept_fd;

   struct sockaddr_in server_addr;

   struct sockaddr_in cli_addr;

   int n;

   int cliaddr_len ;

   char buffer[1024];

   char data[1024] ;

   long length ;

   int nbytes ;

   if((listen_fd=socket(AF_INET,SOCK_STREAM,0))<0)

   {

       printf("Socket Error:%s\n",strerror(errno));

       return -1;

   }

   memset(&server_addr,0x00, sizeof(struct sockaddr_in));

   memset(&cli_addr,0x00, sizeof(struct sockaddr_in));

   server_addr.sin_family=AF_INET;

   server_addr.sin_port=htons(MY_PORT);

   server_addr.sin_addr.s_addr=htonl(INADDR_ANY);

   n=1;

   /* 如果服务器终止后,服务器可以第二次快速启动而不用等待一段时间*/

   setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&n,sizeof(int));

   if(bind(listen_fd,(struct sockaddr *)&server_addr,sizeof(server_addr))<0)

   {

       printf("Bind Error:%s\n",strerror(errno));

       return -1;

   }

   listen(listen_fd,5);

   while(1)

   {

       cliaddr_len= sizeof( cli_addr ) ;

       accept_fd=accept(listen_fd, (struct sockaddr *)&cli_addr, &cliaddr_len);

       if((accept_fd<0)&&(errno==EINTR))

           continue;

       else if(accept_fd<0)

       {

           printf("Accept Error:%s\n",strerror(errno));

           continue;

       }

       if((n=fork())==0)

       {

           /* 子进程处理客户端的连接*/

           fprintf(stdout,"listen_fd:%d accept_fd:%d\n",listen_fd, accept_fd);

           close(listen_fd);

           memset(buffer, 0x00, sizeof(buffer)) ;

           memset(data, 0x00, sizeof(data));

           if((nbytes=readn(accept_fd, data, 8 ))==-1)

           {

               fprintf(stderr,"Read Error:%s\n",strerror(errno));

               fprintf(stderr,"data:%s\n",data );

               close(accept_fd);

               return -1;

           }

           fprintf(stdout,"data:%s,nbytes=%d\n",data, nbytes );

           data[nbytes]='\0' ;

           length=atol(data) ;

           fprintf(stdout,"data:%s,nbytes=%d\n",data, nbytes );

           if((nbytes=readn(accept_fd, data, length ))==-1)

           {

               fprintf(stderr,"Read Error:%s\n",strerror(errno));

               close(accept_fd);

               return -1;

           }

           data[nbytes]='\0' ;

           fprintf(stdout,"data:%s,nbytes=%d\n",data, nbytes );

           if( strncmp(data, "000000", 6 )==0 )

           {

               strcpy(buffer, "I am sorry! who am  I? I don't know also.") ;

               length=strlen(buffer) ;

               sprintf(data,"%08ld%6.6s%s", (length+6),"000000", buffer );

               if((nbytes=writen(accept_fd,data, (length+6+8)))==-1)

               {

                   fprintf(stderr,"Read Error:%s\n",strerror(errno));

                   close(accept_fd);

                   return -1;

               }

               fprintf(stdout,"data:%s\n",data );

           }else{

               /*非000000交易请求为非法,沉默是最好的回答*/

           }

           close(accept_fd);

           return 0;

       }

       else if(n<0)

           printf("Fork Error:%s\n\a",strerror(errno));

       close(accept_fd);

       while(waitpid(-1,NULL,WNOHANG) > 0); /* clean up child processes */ 

   }

}

 

(4)客户端代码

tcpcli.c源代码如下:

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<string.h>

#include<netdb.h>

#include<sys/types.h>

#include<sys/socket.h>

#include<netinet/in.h>

#include<netinet/tcp.h>

#include<errno.h>

externint readn(int fd,void *buffer,int length) ;

externint writen(int fd,void *buffer,int length) ;

intmain(int argc, char *argv[])

{

   int sockfd;

   char buffer[1024];

   char data[1024];

   long length ;

   struct sockaddr_in server_addr;

   struct hostent *host;

   int portnumber,nbytes;

   if(argc!=3)

   {

       fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);

       return -1;

   }

   if((host=gethostbyname(argv[1]))==NULL)

   {

       fprintf(stderr,"Gethostname error\n");

       return -1;

   }

   if((portnumber=atoi(argv[2]))<0)

   {

       fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);

       return -1;

   }

   /* 客户程序开始建立sockfd描述符*/

   if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)

   {

       fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));

       return -1;

   }

   /* 客户程序填充服务端的地址端口信息*/

   bzero(&server_addr,sizeof(server_addr));

   server_addr.sin_family=AF_INET;

   server_addr.sin_port=htons(portnumber);

  server_addr.sin_addr= *((struct in_addr *)host->h_addr);

   /* 客户程序发起连接请求*/

   if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr)

             )==-1)

   {

       fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));

       return -1;

   }

   memset(buffer, 0x00, sizeof(buffer)) ;

   memset(data, 0x00, sizeof(data));

   strcpy(buffer, "Hello! who are  you? could you tell me?") ;

   length=strlen(buffer) ; 

   /***000000为假设的交易码***/

   sprintf(data,"%08ld%6.6s%s", (length+6),"000000", buffer );

   length=length+8+6 ;

   if((nbytes=writen(sockfd,data, length ))==-1)

   {

       fprintf(stderr,"Read Error:%s\n",strerror(errno));

       close(sockfd);

       return -1;

   }

   printf("I have send:%s\n", data+8);

   if((nbytes=readn(sockfd, data, 8 ))==-1)

   {

       fprintf(stderr,"Read Error:%s\n",strerror(errno));

       close(sockfd);

       return -1;

   }

   data[nbytes]='\0' ;

   length=atol(data) ;

   if((nbytes=readn(sockfd, data, length ))==-1)

   {

       fprintf(stderr,"ReadError:%s\n",strerror(errno));

       close(sockfd);

       return -1;

   }

   data[nbytes]='\0' ;

   printf("I have received:%s\n", data);

   close(sockfd);

   return 0;

}

(5)编译与执行

编译 gcc tcpsrv.c  tcpio.c -otcpsrv。

编译 gcc tcpcli.c  tcpio.c -otcpcli。

在一界面下启动服务端进程 ./tcpsrv。

在另一界面下执行./tcpcli 127.0.0.1 10000,执行结果如下:

I havesend:000000Hello! who are  you? could you tell me?

I have received:000000Iam sorry! who am  I? I don't know also.

 

 

 

补充:

关键字:

volatile

用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。volatile很容易被误用,用来进行原子性操作。

 

transient的用途

Q:transient关键字能实现什么?

A:当对象被序列化时(写入字节序列到目标文件)时,transient阻止实例中那些用此关键字声明的变量持久化;当对象被反序列化时(从源文件读取字节序列进行重构),这样的实例变量值不会被持久化和恢复。例如,当反序列化对象——数据流(例如,文件)可能不存在时,原因是你的对象中存在类型为Java.io.InputStream的变量,序列化时这些变量引用的输入流无法被打开。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息