鼎信通訊公司學習信息收集----阿冬專欄



畢業三年後想回青島,空氣好、房價可以接受、研發崗位待遇還不錯、戶口好解決、消費中等、離家近等等衆多因素。感覺最想回青島。


但是瞭解了一下青島的研發大環境:適合的公司主要就是:海爾、海信、中車、鼎信通訊、青島百靈等


下面收集了一些最想去的鼎信通訊公司的招聘信息,留着以後提醒自己,有個參考


(最好學習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+/月。(限碩士)
  • 職稱補貼
  • 五險一金
  • 餐費補貼
  • 入職第一年提供免費住宿
  • 出差住勤補貼
  • 差旅費報銷、通訊費報銷
  • 健康查體、年節福利
  • 生日禮金、生育禮賀
  • 工會、黨建、團隊活動

招聘流程:

  •  

簡歷投遞方式:[email protected]

公司網址: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/IPTransmission Control Protocol/Internet Protocol)即傳輸控制協議/網間協議,定義了主機如何連入因特網及數據如何再它們之間傳輸的標準,

從字面意思來看TCP/IPTCPIP協議的合稱,但實際上TCP/IP協議是指因特網整個TCP/IP協議族。不同於ISO模型的七個分層,TCP/IP協議參考模型把所有的TCP/IP系列協議歸類到四個抽象層中

應用層:TFTPHTTPSNMPFTPSMTPDNSTelnet等等

傳輸層:TCPUDP

網絡層:IPICMPOSPFEIGRPIGMP

數據鏈路層:SLIPCSLIPPPPMTU

每一抽象層建立在低一層提供的服務上,並且爲高一層提供服務,看起來大概是這樣子的

                        

估計有興趣打開此文的同學都對此有一定了解了,加上我也是一知半解,所以就不詳細解釋,有興趣同學可以上網上搜一下資料



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中只要申明一下這個函數,就可以使用它

1static全局變量與普通的全局變量有什麼區別?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+/月。(限碩士)
  • 職稱補貼
  • 五險一金
  • 餐費補貼
  • 入職第一年提供免費住宿
  • 出差住勤補貼
  • 差旅費報銷、通訊費報銷
  • 健康查體、年節福利
  • 生日禮金、生育禮賀
  • 工會、黨建、團隊活動

招聘流程:

  •  

簡歷投遞方式:[email protected]

公司網址: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/IPTransmission Control Protocol/Internet Protocol)即傳輸控制協議/網間協議,定義了主機如何連入因特網及數據如何再它們之間傳輸的標準,

從字面意思來看TCP/IPTCPIP協議的合稱,但實際上TCP/IP協議是指因特網整個TCP/IP協議族。不同於ISO模型的七個分層,TCP/IP協議參考模型把所有的TCP/IP系列協議歸類到四個抽象層中

應用層:TFTPHTTPSNMPFTPSMTPDNSTelnet等等

傳輸層:TCPUDP

網絡層:IPICMPOSPFEIGRPIGMP

數據鏈路層:SLIPCSLIPPPPMTU

每一抽象層建立在低一層提供的服務上,並且爲高一層提供服務,看起來大概是這樣子的

                        

估計有興趣打開此文的同學都對此有一定了解了,加上我也是一知半解,所以就不詳細解釋,有興趣同學可以上網上搜一下資料



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中只要申明一下這個函數,就可以使用它

1static全局變量與普通的全局變量有什麼區別?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的變量,序列化時這些變量引用的輸入流無法被打開。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章