國嵌Linux視頻課件整理(2)

進程通訊linux

信號通訊算法

信號(signal)機制是Unix系統中最爲古老的進程間通訊機制,不少條件能夠產生一個信號:編程

一、當用戶按某些按鍵時,產生信號。數組

二、硬件異常產生信號:除數爲0、無效的存儲訪問等等。這些狀況一般由硬件檢測到,將其通知內核,而後內核產生適當的信號通知進程,例如,內核對正訪問一個無效存儲區的進程產生一個SIGSEGV信號。服務器

三、進程用kill函數將信號發送給另外一個進程。網絡

四、用戶可用kill命令將信號發送給其餘進程。數據結構

信號類型多線程

1) SIGHUP 2) SIGINT3) SIGQUIT4) SIGILL5) SIGTRAP 6) SIGIOT 7) SIGBUS併發

8) SIGFPE9) SIGKILL10) SIGUSR1 11) SIGSEGV 12) SIGUSR213) SIGPIPEsocket

14) SIGALRM 15)SIGTERM17) SIGCHLD 18) SIGCONT 19) SIGSTOP20) SIGTSTP 21) SIGTTIN 22)SIGTTOU23) SIGURG 24) SIGXCPU 25) SIGXFSZ26) SIGVTALRM 27) SIGPROF 28)SIGWINCH29) SIGIO 30) SIGPWR

下面是幾種常見的信號:

§ SIGHUP: 從終端上發出的結束信號

§ SIGINT: 來自鍵盤的中斷信號(Ctrl-C)

§ SIGKILL:該信號結束接收信號的進程

§ SIGTERM:kill 命令發出的信號

§ SIGCHLD:標識子進程中止或結束的信號

§ SIGSTOP:來自鍵盤(Ctrl-Z)或調試程序的中止執行信號

信號處理

當某信號出現時,將按照下列三種方式中的一種進行處理:

一、忽略此信號

大多數信號都按照這種方式進行處理,但有兩種信號卻決不能被忽略。它們是:

SIGKILL和SIGSTOP。這兩種信號不能被忽略的緣由是:它們向超級用戶提供了一

種終止或中止進程的方法。

二、執行用戶但願的動做

通知內核在某種信號發生時,調用一個用戶函數。在用戶函數中,執行用戶但願的處理。

三、執行系統默認動做

對大多數信號的系統默認動做是終止該進程。

信號發送

發送信號的主要函數有 kill和raise。

區別:

Kill既能夠向自身發送信號,也能夠向其餘進程發送信號。與kill

函數不一樣的是,raise函數是向進程自身發送信號。

#include<sys/types.h>

#include<signal.h>

int kill(pid_t pid,int signo)

int raise(int signo)

kill的pid參數有四種不一樣的狀況:

一、pid>0

將信號發送給進程ID爲pid的進程。

二、pid == 0

將信號發送給同組的進程。

三、pid < 0

將信號發送給其進程組ID等於pid絕對值的進程。

四、pid ==-1

將信號發送給全部進程。

Alarm

使用alarm函數能夠設置一個時間值(鬧鐘時間),當所設置的時間到了時,產生SIGALRM信

號。若是不捕捉此信號,則默認動做是終止該進程。

#include<unistd.h>

unsigned intalarm(unsigned int seconds)

Seconds:通過了指定的seconds秒後會產生信號SIGALRM。

每一個進程只能有一個鬧鐘時間。若是在調用alarm時,之前已爲該進程設置過鬧鐘時間,而

且它尚未超時,之前登記的鬧鐘時間則被新值代換。

若是有之前登記的還沒有超過的鬧鐘時間,而此次seconds值是0,則表示取消之前的鬧鐘。

Pause

pause函數使調用進程掛起直至捕捉到一個信號。

#include<unistd.h>

int pause(void)

只有執行了一個信號處理函數後,掛起才結束。

信號的處理

當系統捕捉到某個信號時,能夠忽略該信號或是使用指定的處理函數來處理該信號,或者使用系統默認的方式。

信號處理的主要方法有兩種,一種是使用簡單的signal函數,另外一種是使用信號集函數組。

signal

#include<signal.h>

void (*signal (intsigno, void (*func)(int)))(int)

typedef void(*sighandler_t)(int)

sighandler_tsignal(int signum, sighandler_t handler))

Func可能的值是:

一、SIG_IGN:忽略此信號

二、SIG_DFL: 按系統默認方式處理

三、信號處理函數名:使用該函數處理

共享內存

共享內存是被多個進程共享的一部分物理內存。共享內存是進程間共享數據的一種最快的方法,一個進程向共享內存區域寫入了數據,共享這個內存區域的全部進程就能夠馬上看到其中的內容。

共享內存實現分爲兩個步驟:

1、建立共享內存,使用shmget函數。

2、映射共享內存,將這段建立的共享內存映射到具體的進程空間去,使用shmat函數。

int shmget( key_t key, int size, int shmflg )

key標識共享內存的鍵值: 0/IPC_PRIVATE。 當key的取值爲IPC_PRIVATE,則函數shmget()將建立一塊新的共享內存;若是key的取值爲0,而參數shmflg中又設置IPC_PRIVATE這個標誌,則一樣會建立一塊新的共享內存。

返回值:若是成功,返回共享內存標識符;若是失敗,返回-1。

int shmat( int shmid, char *shmaddr, int flag)

參數:

shmid:shmget函數返回的共享存儲標識符

flag:決定以什麼方式來肯定映射的地址(一般爲0)

返回值:若是成功,則返回共享內存映射到進程中的地址;若是失敗,則返回- 1。

當一個進程再也不須要共享內存時,須要把它從進程地址空間中脫離。

int shmdt ( char*shmaddr )

消息隊列

unix早期通訊機制之一的信號可以傳送的信息量有限,管道則只能傳送無格式的字節流,這無疑會給應用程序開發帶來不便。消息隊列(也叫作報文隊列)則克服了這些缺點。

消息隊列就是一個消息的鏈表。能夠把消息看做一個記錄,具備特定的格式。進程能夠向中按照必定的規則添加新消息;另外一些進程則能夠從消息隊列中讀走消息。

目前主要有兩種類型的消息隊列:POSIX消息隊列以及系統V消息隊列,系統V消息隊列目前被大量使用。

持續性

系統V消息隊列是隨內核持續的,只有在內核重起或者人工刪除時,該消息隊列纔會被刪除。

鍵值

消息隊列的內核持續性要求每一個消息隊列都在系統範圍內對應惟一的鍵值,因此,要得到一個消息隊列的描述字,必須提供該消息隊列的鍵值。

#include<sys/types.h>

#include<sys/ipc.h>

key_t ftok(char*pathname, char proj)

功能:

返回文件名對應的鍵值。

pathname:文件名

proj:項目名(不爲0便可)

打開/建立

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

int msgget(key_tkey, int msgflg)

返回值:與健值key相對應的消息隊列描述字

key:鍵值,由ftok得到。

msgflg:標誌位。

IPC_CREAT 建立新的消息隊列

IPC_EXCL 與IPC_CREAT一同使用,表示若是要建立的消息隊列已經存在,則返回錯誤。

IPC_NOWAIT 讀寫消息隊列要求沒法獲得知足時,不阻塞。

在如下兩種狀況下,將建立一個新的消息隊列:

若是沒有與健值key相對應的消息隊列,而且msgflg中包含了IPC_CREAT標誌位。

key參數爲IPC_PRIVATE。

發送消息

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

int msgsnd(intmsqid,struct msgbuf*msgp,int msgsz,int msgflg)

功能:向消息隊列中發送一條消息。

msqid 已打開的消息隊列id

msgp 存放消息的結構

msgsz 消息數據長度

msgflg 發送標誌,有意義的msgflg標誌爲IPC_NOWAIT,指明在消息隊列沒有足夠空間容納要發送的消息時,msgsnd是否等待。

消息格式

struct msgbuf

{

long mtype; /* 消息類型 > 0 */

char mtext[1]; /* 消息數據的首地址 */

};

接收消息

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

int msgrcv(intmsqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg)

功能:從msqid表明的消息隊列中讀取一個msgtyp類型的消息,並把消息存儲在msgp指向

的msgbuf結構中。在成功地讀取了一條消息之後,隊列中的這條消息將被刪除。

信號量

信號量(又名:信號燈)與其餘進程間通訊方式不大相同,主要用途是保護臨界資源。進程能夠根據它斷定是否可以訪問某些共享資源。除了用於訪問控制外,還可用於進程同步。

分類

二值信號燈:信號燈的值只能取0或1,相似於互斥鎖。 但二者有不一樣:信號燈強調共享資源,

只要共享資源可用,其餘進程一樣能夠修改信號燈的值;互斥鎖更強調進程,佔用資源的進程使用完資源後,必須由進程自己來解鎖。

計數信號燈:信號燈的值能夠取任意非負值。

建立/打開

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

int semget(key_tkey, int nsems, int semflg)

key:鍵值,由ftok得到

nsems:指定打開或者新建立的信號燈集中將包含信號燈的數目

semflg:標識,同消息隊列

操做

int semop(int semid,struct sembuf *sops, unsigned nsops)

功能:對信號量進行控制。

semid:信號量集的ID

sops:是一個操做數組,代表要進行什麼操做

nsops:sops所指向的數組的元素個數。

struct sembuf {

unsigned shortsem_num; /* semaphore index in array */

short sem_op; /*semaphore operation */

short sem_flg; /*operation flags */

};

sem_num:要操做的信號量在信號量集中的編號,第一個信號的編號是0。

sem_op:若是其值爲正數,該值會加到現有的信號量值中,一般用於釋放信號量;若是sem_op的值爲負數,而其絕對值又大於信號的現值,操做 將會阻塞,直到信號值大於或等於sem_op的絕對值,一般用於獲取信號量;若是sem_op的值爲0,則操做將暫時阻塞,直到信號的值變爲0。

Sem_flg:信號操做標誌,可能的選擇有兩種:

IPC_NOWAIT:對信號的操做不能知足時,semop()不會阻塞,並當即返回,同時設定錯誤信息。

IPC_UNDO:程序結束時(不論正常或不正常)釋放信號量,這樣作的目的在於避免程序在異常狀況下結束時未將鎖定的資源解鎖,形成該資源永遠鎖定。

 多線程

線程理論基礎

線程(thread)技術早在60年代就被提出,但真正應用多線程到操做系統中去,是在80年代中

期,solaris是這方面的佼佼者。傳統的Unix也支持線程的概念,可是在一個進程(process)中只容許有一個線程,這樣多線程就意味着多進程。如今,多線程技術已經被許多操做系統所支持,包括Windows/NT、Linux。

爲何有了進程,還要引入線程呢?使用多線程到底有哪些好處?

使用多線程的理由之一是:

和進程相比,它是一種很是「節儉」的多任務操做方式。在Linux系統下,啓動一個新的進程

必須分配給它獨立的地址空間,創建衆多的數據表來維護它的代碼段、堆棧段和數據段,這

是一種"昂貴"的多任務工做方式。運行於一個進程中的多個線程,它們之間使用相同的地址空間,並且線程間彼此切換所需的時間也遠遠小於進程間切換所須要的時間。據統計,一個進程的開銷大約是一個線程開銷的30倍左右。

使用多線程的理由之二是:

線程間方便的通訊機制。對不一樣進程來講,它們具備獨立的數據空間,要進行數據的傳遞只能經過進程間通訊的方式進行,這種方式不只費時,並且很不方便。線程則否則,因爲同一進程下的線程之間共享數據空間,因此一個線程的數據能夠直接爲其它線程所用,這不只快捷,並且方便。

除了以上所說的優勢外,多線程程序做爲一種多任務、併發的工做方式,有以下優勢:

使多CPU系統更加有效。操做系統會保證當線程數不大於CPU數目時,不一樣的線程運行於不一樣的CPU上。

改善程序結構。一個既長又複雜的進程能夠考慮分爲多個線程,成爲幾個獨立或半獨立的運行部分,這樣的程序會利於理解和修改。

Linux系統下的多線程遵循POSIX線程接口,稱爲pthread。編寫Linux下的多線程程序,須要使用頭文件pthread.h,鏈接時須要使用庫libpthread.a。

多線程程序設計

建立線程

#include<pthread.h>

intpthread_create(pthread_t * tidp,const pthread_attr_t *attr,

void*(*start_rtn)(void),void *arg)

tidp:線程id

attr:線程屬性(一般爲空)

start_rtn:線程要執行的函數

arg:start_rtn的參數

編譯

由於pthread的庫不是linux系統的庫,因此在進行編譯的時候要加上

-lpthread

# gcc filename-lpthread

終止線程

若是進程中任何一個線程中調用exit或_exit,那麼整個進程都會終止。線程的正常退出方式有:

(1) 線程從啓動例程中返回

(2) 線程能夠被另外一個進程終止

(3) 線程本身調用pthread_exit函數

#include<pthread.h>

voidpthread_exit(void * rval_ptr)

功能:終止調用線程

Rval_ptr:線程退出返回值的指針。

線程等待

#include<pthread.h>

intpthread_join(pthread_t tid,void **rval_ptr)

功能:阻塞調用線程,直到指定的線程終止。

Tid :等待退出的線程id

Rval_ptr:線程退出的返回值的指針

線程標識

#include<pthread.h>

pthread_tpthread_self(void)

功能:

獲取調用線程的 thread identifier

清除

線程終止有兩種狀況:正常終止和非正常終止。線程主動調用pthread_exit或者從線程函數中return都將使線程正常退出,這是可預見的 退出方式;非正常終止是線程在其餘線程的干預下,或者因爲自身運行出錯(好比訪問非法地址)而退出,這種退出方式是不可預見的。

不管是可預見的線程終止仍是異常終止,都會存在資源釋放的問題,如何保證線程終止時能順利的釋放掉本身所佔用的資源,是一個必須考慮解決的問題。

從pthread_cleanup_push的調用點到pthread_cleanup_pop之間的程序段中的終

止動做(包括調用pthread_exit()和異常終止,不包括return)都將執行pthread_cleanup_push()所指定的清理函數。

#include<pthread.h>

voidpthread_cleanup_push(void (*rtn)(void *),void *arg)

功能:

將清除函數壓入清除棧

Rtn:清除函數

Arg:清除函數的參數

#include<pthread.h>

voidpthread_cleanup_pop(int execute)

功能:

將清除函數彈出清除棧

參數:

Execute執行到pthread_cleanup_pop()時是否在彈出清

理函數的同時執行該函數,非0:執行; 0:不執行

線程同步

進行多線程編程,由於沒法知道哪一個線程會在哪一個時候對共享資源進行操做,所以讓如何保護共享資源變得複雜,經過下面這些技術的使用,能夠解決線程之間對資源的競爭:

1 互斥量Mutex

2 信號燈Semaphore

3 條件變量Conditions

互斥量

爲何須要互斥量:

Item * p=queue_list;

Queue_list=queue_list->next;

process_job(p);

free(p);

當線程1處理完Item *p=queue_list後,系統中止線程1的運行,改而運行線程2。線程2照樣取出頭節點,而後進行處理,最後釋放了該節點。過了段時間,線程1從新 獲得運行。而這個時候,p所指向的節點已經被線程2釋放掉,而線程1對此毫無知曉。他會接着運行

process_job(p)。而這將致使沒法預料的後果!

對於這種狀況,系統給咱們提供了互斥量。線程在取出頭節點前必需要等待互斥量,若是此時有其餘線程已經得到該互斥量,那麼該線程將會阻塞在這裏。只有等到其餘線程釋放掉該互斥量後,該線程纔有可能獲得該互斥量。互斥量從本質上說就是一把鎖, 提供對共享資源的保護訪問。

建立

在Linux中, 互斥量使用類型pthread_mutex_t表示。在使用前, 要對它進行初始化:

對於靜態分配的互斥量, 能夠把它設置爲默認的mutex對象PTHREAD_MUTEX_INITIALIZER

對於動態分配的互斥量, 在申請內存(malloc)以後, 經過pthread_mutex_init進行初始化, 而且在釋放內存(free)前須要調用pthread_mutex_destroy。

#include<pthread.h>

int pthread_mutex_init(pthread_mutex_t*mutex,

constpthread_mutexattr_t *attr)

intpthread_mutex_destroy(pthread_mutex_t *mutex)

加鎖

對共享資源的訪問, 要使用互斥量進行加鎖, 若是互斥量已經上了鎖, 調用線程會阻塞, 直到互斥量被解鎖。

intpthread_mutex_lock(pthread_mutex_t *mutex)

intpthread_mutex_trylock(pthread_mutex_t *mutex)

返回值: 成功則返回0, 出錯則返回錯誤編號。

trylock是非阻塞調用模式, 若是互斥量沒被鎖住, trylock函數將對互斥量加鎖, 並得到對共享資源的訪問權限; 若是互斥量被鎖住了,trylock函數將不會阻塞等待而直接返回EBUSY, 表示共享資源處於忙狀態。

解鎖

在操做完成後,必須給互斥量解鎖,也就是前面所說的釋放。這樣其餘等待該鎖的線程纔有機會得到該鎖,不然其餘線程將會永遠阻塞。

intpthread_mutex_unlock(pthread_mutex_t *mutex)

互斥量PK信號量

Mutex是一把鑰匙,一我的拿了就可進入一個房間,出來的時候把鑰匙交給隊列的第一個。

Semaphore是一件能夠容納N人的房間,若是人不滿就能夠進去,若是人滿了,就要等待有人出來。

對於N=1的狀況,稱爲binary semaphore。

Binary semaphore與Mutex的差別:

1. mutex要由得到鎖的線程來釋放(誰得到,誰釋放)。而semaphore能夠由其它線程釋放

2. 初始狀態可能不同:mutex的初始值是1 ,而semaphore的初始值多是0(或者爲1)。

1.2.2 解決默認壁紙是動態壁紙的問題

終於找到默認壁紙沒法改變的緣由了:

在/vendor/samsung/smdkv210/overlay裏對一些系統參數進行的重載配置,

故在原來的地方改默認壁紙是無效的。

 網絡編程

TCP/IP協議

TCP/IP協議族

TCP/IP 實際上一個協同工做的通訊家族,爲網絡數據通訊提供通路。爲討論方即可TCP/IP 協議組大致上分爲三部分:

• Internet 協議(IP)

• 傳輸控制協議(TCP)和用戶數據報協議(UDP)

• 處於 TCP 和 UDP 之上的一組應用協議。它們包括:TELNET,文件傳送協議(FTP),域名服務(DNS)和簡單的郵件傳送程序(SMTP)等。

網絡層

第一部分稱爲網絡層。主要包括Internet 協議(IP)、網際控制報文協議(ICMP)和地址解析協議(ARP):

• Internet 協議(IP)

該協議被設計成互聯分組交換通訊網,以造成一個網際通訊環境。它負責在源主機和目的地主機之間傳輸來自其較高層軟件的稱爲數據報文的數據塊,它在源和目的地之間提供非鏈接型傳遞服務。

• 網際控制報文協議(ICMP)

它實際上不是IP層部分,但直接同IP層一塊兒工做,報告網絡上的某些出錯狀況。容許網際路由器傳輸差錯信息或測試報文。

• 地址解析協議(ARP)

ARP 實際上不是網絡層部分,它處於IP和數據鏈路層之間,它是在32位IP地址和48位物理地址之間執行翻譯的協議。

傳輸層協議

第二部分是傳輸層協議,包括傳輸控制協議和用戶數據報文協議。

•傳輸控制協議(TCP):

該協議對創建網絡上用戶進程之間的對話負責,它確保進程之間的可靠通訊,所提供的功能以下:

1. 監聽輸入對話創建請求

2. 請求另外一網絡站點對話

3. 可靠的發送和接收數據

4.適度的關閉對話

用戶數據報文協議(UDP):

UDP 提供不可靠的非鏈接型傳輸層服務,它容許在源和目的地之間傳送數據,而沒必要在傳送數據以前創建對話。它主要用於那些非鏈接型的應用程序,如:視頻點播。

應用協議

這部分主要包括Telnet,文件傳送協議(FTP 和TFTP),簡單文件傳送協議(SMTP)和域名服務(DNS)等協議。

IP協議

IP主要有如下四個主要功能:

• 數據傳送

• 尋址

• 路由選擇

• 數據報文的分段

IP的主要目的是爲數據輸入/輸出網絡提供基本算法,爲高層協議提供無鏈接的傳送服務。這意味着在IP將數據遞交給接收站點之前不在傳輸站點和接收站點之間創建對話。它只是封裝和傳遞數據,但不向發送者或接收者報告包的狀態,不處理所遇到的故障。

IP包由IP協議頭與協議數據兩部分構成。

TCP協議

TCP是重要的傳輸層協議,目的是容許數據同網絡上的其餘節點進行可靠的交換。它能提供端口編號的譯碼,以識別主機的應用程序,並且完成數據的可靠傳輸。

•TCP 協議具備嚴格的內裝差錯檢驗算法確保數據的完整性。

•TCP 是面向字節的順序協議,這意味着包內的每一個字節被分配一個順序編號,並分配給每包一個順序編號。

UDP協議

UDP也是傳輸層協議,它是無鏈接的,不可靠的傳輸服務。當接收數據時它不向發送方提供確認信息,它不提供輸入包的順序,若是出現丟失包或重份包的狀況,也不會向發送方發出差錯報文。因爲它執行功能時具備較低的開銷,於是執行速度比TCP快。

linux socket編程

Socket

Linux中的網絡編程經過Socket(套接字)接口實現,Socket是一種文件描述符。

套接字socket有三種類型:

• 流式套接字(SOCK_STREAM)能夠提供可靠的、面向鏈接的通信流。它使用了TCP協議。TCP保證了數據傳輸的正確性和順序性。

• 數據報套接字(SOCK_DGRAM)字定義了一種無鏈接的服務,數據經過相互獨立的報文進行傳輸,是無序的,而且不保證可靠,無差錯,它使用數據報協議UDP。

• 原始套接字容許對低層協議如IP或ICMP直接訪問,主要用於新的網絡協議的測試等。

地址結構

struct sockaddr

{

u_short sa_family;

char sa_data[14];

}

Sa_family:地址族,採用「AF_xxx」的形式,如:AF_INET。

Sa_data:14字節的特定協議地址。

struct sockaddr_in

{

short intsin_family; /* Internet地址族 */

unsigned short intsin_port; /* 端口號 */

struct in_addrsin_addr; /* IP地址 */

unsigned charsin_zero[8]; /* 填0 */

}

編程中通常並不直接針對sockaddr數據結構操做,而是使用與sockaddr等價的sockaddr_in數據結構

struct in_addr

{

unsigned longs_addr;

}

S_addr: 32位的地址。

地址轉換

IP地址一般由數字加點(192.168.0.1)的形式表示,而在struct in_addr中使用的是IP地址是由32位的整數表示的,爲了轉換咱們可使用下面兩個函數:

int inet_aton(constchar *cp,struct in_addr *inp)

char*inet_ntoa(struct in_addr in)

函數裏面 a 表明 ascii n 表明network.第一個函數表示將a.b.c.d形式的IP轉換爲32位的IP,存儲在 inp指針裏面。第二個是將32位IP轉換爲a.b.c.d的格式。

字節序轉換

爲何要進行字節序轉換?

例:

INTEL的CPU使用的小端字節序MOTOROLA 68k系列CPU使用的是大端字節序 MOTOROLA發一個16位數據0X1234給INTEL, 傳到INTEL時 ,就被INTEL解釋爲0X3412 。

不一樣類型的 CPU 對變量的字節存儲順序可能不一樣:有的系統是高位在前,低位在後,而有的系統是低位在前,高位在後,而網絡傳輸的數據順序是必定要統一的。因此當內部字節存儲順序和網絡字節順序不一樣時,就必定要進行轉換。

網絡字節順序是TCP/IP中規定好的一種數據表示格式,它與具體的CPU類型、操做系統

等無關,從而能夠保證數據在不一樣主機之間傳輸時可以被正確解釋。網絡字節順序採用

big endian排序方式。

htons把unsigned short類型從主機序轉換到網絡序

htonl把unsigned long類型從主機序轉換到網絡序

ntohs把unsigned short類型從網絡序轉換到主機序

ntohl把unsigned long類型從網絡序轉換到主機序

IP與主機名

在網絡上標識一臺機器能夠用IP,也可使用主機名。

struct hostent*gethostbyname(const char *hostname)

struct hostent

{

char *h_name; /* 主機的正式名稱 */

char *h_aliases; /* 主機的別名 */

int h_addrtype; /* 主機的地址類型 AF_INET*/

int h_length; /* 主機的地址長度 */

char **h_addr_list;/* 主機的IP地址列表 */

}

#define h_addrh_addr_list[0] /* 主機的第一個IP地址*/

函數

進行Socket編程的經常使用函數有:

• socket 建立一個socket。

• bind 用於綁定IP地址和端口號到socket。

• connect 該函數用於綁定以後的client端,與服務器創建鏈接。

• listen 設置能處理的最大鏈接要求,Listen()並未開始接收連線,只是設置socket爲listen模式。

• accept 用來接受socket鏈接。

• send 發送數據

• recv 接收數據

基於TCP-服務器

1. 建立一個socket,用函數socket()

2. 綁定IP地址、端口等信息到socket上,用函數bind()

3. 設置容許的最大鏈接數,用函數listen()

4. 接收客戶端上來的鏈接,用函數accept()

5. 收發數據,用函數send()和recv(),或者read()和write()

6. 關閉網絡鏈接

基於TCP-客戶端

1. 建立一個socket,用函數socket()

2. 設置要鏈接的對方的IP地址和端口等屬性

3. 鏈接服務器,用函數connect()

4. 收發數據,用函數send()和recv(),或者

read()和write()

5. 關閉網絡鏈接

基於UDP-服務器

1. 建立一個socket,用函數socket()

2. 綁定IP地址、端口等信息到socket上,用函數bind()

3. 循環接收數據,用函數recvfrom()

4. 關閉網絡鏈接

基於UDP-客戶端

1. 建立一個socket,用函數socket()

2. 綁定IP地址、端口等信息到socket上,用函數bind()

3. 設置對方的IP地址和端口等屬性

4. 發送數據,用函數sendto()

5. 關閉網絡鏈接

服務器模型

在網絡程序裏面,通常來講都是許多客戶對應一個服務器,爲了處理客戶的請求, 對服務端的程序就提出了特殊的要求。目前最經常使用的服務器模型有:

•循環服務器:服務器在同一個時刻只能夠響應一個客戶端的請求

•併發服務器:服務器在同一個時刻能夠響應多個客戶端的請求

UDP循環服務器

UDP循環服務器的實現方法:UDP服務器每次從套接字上讀取一個客戶端的請求->處理->而後將結果返回給客戶機。

socket(...);

bind(...);

while(1)

{

recvfrom(...);

process(...);

sendto(...);

}

由於UDP是非面向鏈接的,沒有一個客戶端能夠總是佔住服務端, 服務器對於每個客戶機的請求老是可以知足。

TCP循環服務器

TCP服務器接受一個客戶端的鏈接,而後處理,完成了這個

客戶的全部請求後,斷開鏈接。算法以下:

socket(...);

bind(...);

listen(...);

while(1)

{

accept(...);

process(...);

close(...);

}

TCP循環服務器一次只能處理一個客戶端的請求。只有在這個客戶的全部請求都知足後, 服務器才能夠繼續後面的請求。這樣若是有一個客戶端佔住服務器不放時,其它的客戶機都不能工做了,所以,TCP服務器通常不多用循環服務器模型的。

TCP併發服務器

併發服務器的思想是每個客戶機的請求並不禁服務器直接處

理,而是由服務器建立一個 子進程來處理。算法以下:

socket(...);

bind(...);

listen(...);

while(1) {

accept(...);

if(fork(..)==0) {

process(...);

close(...);

exit(...);

}

close(...);

}

TCP併發服務器能夠解決TCP循環服務器客戶機獨佔服務器的狀況。但同時也帶來了問題:爲了響應客戶的請求,服務器要建立子進程來處理,而建立子進程是一種很是消耗資源的操做。

多路複用I/O

阻塞函數在完成其指定的任務之前不容許程序繼續向下執行。例如:當服務器運行到accept語句時,而沒有客戶請求鏈接,服務器就會中止在 accept語句上等待鏈接請求的到來。這種狀況稱爲阻塞(blocking),而非阻塞操做則能夠當即完成。例如,若是你但願服務器僅僅檢查是否有客戶 在等待鏈接,有就接受鏈接,不然就繼續作其餘事情,則能夠經過使用select系統調用來實現。除此以外,select還能夠同時監視多個套接字 。

int select(intmaxfd, fd_set *readfds, fd_set *writefds, fe_set

*exceptfds, conststruct timeval *timeout)

Maxfd: 文件描述符的範圍,比待檢的最大文件描述符大1

Readfds:被讀監控的文件描述符集

Writefds:被寫監控的文件描述符集

Exceptfds:被異常監控的文件描述符集

Timeout:定時器

Timeout取不一樣的值,該調用有不一樣的表現:

Timeout值爲0,無論是否有文件知足要求,都馬上返回,無文件知足要求返回0,有文件知足要求返回一個正值。

Timeout爲NULL,select將阻塞進程,直到某個文件知足要求

Timeout值爲正整數,就是等待的最長時間,即select在timeout時間內阻塞進程。

Select調用返回時,返回值有以下狀況:

1. 正常狀況下返回知足要求的文件描述符個數;

2. 通過了timeout等待後仍無文件知足要求,返回值爲0;

3. 若是select被某個信號中斷,它將返回-1並設置errno爲EINTR。

4. 若是出錯,返回-1並設置相應的errno。

Select使用步驟

1. 設置要監控的文件

2. 調用Select開始監控

3. 判斷文件是否發生變化

統提供了4個宏對描述符集進行操做:

#include<sys/select.h>

void FD_SET(int fd,fd_set *fdset) 宏FD_SET將文件描述符fd添加到文件描述符集fdset中;

void FD_CLR(int fd,fd_set *fdset) 宏FD_CLR從文件描述符集fdset中清除文件描述符fd;

void FD_ZERO(fd_set*fdset) 宏FD_ZERO清空文件描述符集fdset;

void FD_ISSET(intfd, fd_set *fdset) 在調用select後使用FD_ISSET來檢測文件描述符集fdset中的文件fd發生了變化。

FD_ZERO(&fds);//清空集合

sock1 =socket(......);

sock2 =socket(......);

bind(sock1,...);

bind(sock2,...);

listen(sock1,...);

listen(sock1,...);

FD_SET(sock1,&fds);//設置描述符

FD_SET(sock2,&fds);//設置描述符

maxfdp=(sock1>sock2?sock1:sock2)+ 1;

switch(select(maxfdp,&fds,NULL,NULL,&timeout))

case -1:exit(-1);break; //select錯誤,退出程序

case 0:break;

default:

if(FD_ISSET(sock1,&fds))//測試sock1是否可讀

accpet(sock1,...)

相關文章
相關標籤/搜索