It計算機課件 Linu-TCP-IP網(wǎng)絡(luò)編程_第1頁
It計算機課件 Linu-TCP-IP網(wǎng)絡(luò)編程_第2頁
It計算機課件 Linu-TCP-IP網(wǎng)絡(luò)編程_第3頁
It計算機課件 Linu-TCP-IP網(wǎng)絡(luò)編程_第4頁
It計算機課件 Linu-TCP-IP網(wǎng)絡(luò)編程_第5頁
已閱讀5頁,還剩126頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

-rLINUX網(wǎng)輅編程

LINUXTCP/IP網(wǎng)絡(luò)編程

3匚大,堂信息科考與工程學(xué)院付沖

LINUX網(wǎng)輅編程

LinuxTCP/IP網(wǎng)絡(luò)編程

1.1Linux網(wǎng)絡(luò)基礎(chǔ)

TCP/IP協(xié)議集

TELNETFTPSMTPDNSHTTPSNMPPOPApplication

TCPUDPTransport

ICMP

IPInternet

ARPRARP

Host-to-

ARPANETSATNETPacketradioLAN

network

LINUX網(wǎng)輅編程

L本地回路:lo,固定IP地址:

以太網(wǎng)卡:ethO,ethl,.......

2.查看IP地址一ifconfig

各字段說明:

Hwaddr:網(wǎng)卡地址(物理地址,硬件地址)

inetaddr:IP地址

Beast:廣播地址

LINUX網(wǎng)輅編程

Mask:子網(wǎng)掩碼

MTU:MaxTransmissionUnit,最大傳輸單元,

即數(shù)據(jù)幀最大長度

Metric:路由長度

TXpackets:發(fā)送的數(shù)據(jù)包總數(shù)、錯誤數(shù)、遺失數(shù)

及溢出數(shù)

RXpackets:接收的數(shù)據(jù)包總數(shù)、錯誤數(shù)、遺失數(shù)

及溢出數(shù)

LINUX網(wǎng)輅編程

3.設(shè)置IP地址相關(guān)信息

語法:ifconfig接口IP地址[broadcast廣播

地址netmask子網(wǎng)掩碼]

例:ifconfigethO

ifconfigethObroadcast

55netmask

注意:ifconfig命令設(shè)置的IP地址不能永久保存,重

新啟動計算機后將丟失設(shè)置

LINUX網(wǎng)輅編程

4.netconfig命令設(shè)置網(wǎng)絡(luò)參數(shù)

usedynamicIPconfiguration

IPaddress

Netmask

Defaultgateway(IP)

Primarynameserver

注意:netconfig命令設(shè)置的網(wǎng)絡(luò)配置信息可以永久

保存到配置文件中,但不會立即生效,刷新方

法:/etc/init.d/networkrestart

LINUX網(wǎng)輅編程

5.網(wǎng)絡(luò)配置相關(guān)文件

(1)/etc/sysconfig/network

HOSTNAME:主機名設(shè)置

GATEWAY:默認(rèn)網(wǎng)關(guān)的設(shè)置

LINUX網(wǎng)輅編程

(2)/etc/sysconng/network-scnpts/ifcfg-ethO

DEVICE:設(shè)備名稱

BOOTPROTO:IP地址設(shè)置方式(動態(tài)或靜態(tài))

BROADCAST:廣播地址

HWADDR:硬件地址

IPADDR:IP地址設(shè)置

NETMASK:子網(wǎng)掩碼設(shè)置

NETWORK:網(wǎng)絡(luò)地址

ONBOOT:啟動時是否激活

LINUX網(wǎng)輅編程

(3)/etc/resolv.conf

DNS服務(wù)器地址設(shè)置。

(4)/etc/hosts

存放一些常用主機的IP地址與主機名稱的數(shù)據(jù),

即一個簡單的DNS數(shù)據(jù)庫。

基本格式為:

IP主機域名主機別名

(5)/etc/services

記錄主機提供的網(wǎng)絡(luò)服務(wù)項目、端口號(port)及

使用的運輸層協(xié)議。

LINUX網(wǎng)輅編程

1.2套接字編程基本概念

在UNIX系統(tǒng)中,網(wǎng)絡(luò)應(yīng)用編程接口有兩類:

UNIXBSD的套接字(socket)

UNIXSystemV的TLI

由于Sim公司采用了支持TCP/IP的UNIXBSD操

作系統(tǒng),使TCP/IP的應(yīng)用有更大的發(fā)展,其網(wǎng)絡(luò)應(yīng)

用編程接口---套接字(socket)在網(wǎng)絡(luò)軟件中被廣泛應(yīng)

用,至今已引進(jìn)微機操作系統(tǒng)DOS和Windows系統(tǒng)

中,成為開發(fā)網(wǎng)絡(luò)應(yīng)用軟件的強有力工具。

LINUX網(wǎng)輅編程

1.2.1網(wǎng)間進(jìn)程通信

進(jìn)程通信的概念最初來源于單機系統(tǒng)。由于每個進(jìn)

程都在自己的地址范圍內(nèi)運行,為保證兩個相互通信

的進(jìn)程之間既互不干擾又協(xié)調(diào)一致工作,操作系統(tǒng)為進(jìn)

程通信提供了相應(yīng)設(shè)施,如UNIXBSD中的管道

(pipe)、命名管道(namedpipe)和軟中斷信號(signal),

UNIXsystemV的消息(message)、共享存儲區(qū)(shared

memory)和信號量(semaphore)等,但都僅限于用在本

機進(jìn)程之間通信。

LINUX網(wǎng)輅編程

網(wǎng)間進(jìn)程通信要解決的是不同主機進(jìn)程間的相互通信問

題。

為此,首先要解決的是網(wǎng)間進(jìn)程標(biāo)識問題。同一主機

上,不同進(jìn)程可用進(jìn)程號(processID)唯一標(biāo)識。但

在網(wǎng)絡(luò)環(huán)境下,各主機獨立分配的進(jìn)程號不能唯一標(biāo)識

該進(jìn)程。例如,主機A賦于某進(jìn)程號5,在B機中也可

以存在5號進(jìn)程,因此,“5號進(jìn)程”這句話就沒有意義

了。

其次,操作系統(tǒng)支持的網(wǎng)絡(luò)協(xié)議眾多,不同協(xié)議的工

作方式不同,地址格式也不同。

LINUX網(wǎng)輅編程

因此,網(wǎng)間進(jìn)程通信還要解決多重協(xié)議的識別問

題。

為了解決上述問題,TCP/IP協(xié)議引入了下列幾個概

念。

⑴端口

網(wǎng)絡(luò)中可以被命名和尋址的通信端口,是操作系

統(tǒng)可分配的一種資源。

LINUX網(wǎng)輅編程

按照OSI七層協(xié)議的描述,運輸層與網(wǎng)絡(luò)層在功能

上的最大區(qū)別是運輸層提供進(jìn)程通信能力。從這個意

義上講,網(wǎng)絡(luò)通信的最終地址就不僅僅是主機地址

了,還包括可以描述進(jìn)程的某種標(biāo)識符。為此,

TCP/IP協(xié)議提出了協(xié)議端口(protocolport),簡稱端

口)的概念,用于標(biāo)識通信的進(jìn)程。

端口是一種抽象的軟件結(jié)構(gòu)(包括一些數(shù)據(jù)結(jié)構(gòu)和

DO緩沖區(qū))。

LINUX網(wǎng)輅編程

應(yīng)用程序(即進(jìn)程)通過系統(tǒng)調(diào)用與某端口建立連接

(binding)后,運輸層傳給該端口的數(shù)據(jù)都被相應(yīng)進(jìn)程

所接收,相應(yīng)進(jìn)程發(fā)給運輸層的數(shù)據(jù)都通過該端口輸

出。

在TCP/IP協(xié)議的實現(xiàn)中,端口操作類似于一般的

DO操作,進(jìn)程獲取一個端口,相當(dāng)于獲取本地唯一的

DO文件,可以用一般的讀寫原語訪問之。

LINUX網(wǎng)輅編程

類似于文件描述符,每個端口都擁有一個叫端口號

(portnumber)的整數(shù)型標(biāo)識符,用于區(qū)別不同端口。

由于TCP/IP運輸層的兩個協(xié)議TCP和UDP是完全獨立

的兩個軟件模塊,因此各自的端口號也相互獨立,如

TCP有一個255號端口,UDP也可以有一個255號端

口,二者并不沖突。

LINUX網(wǎng)輅編程

TCP/IP號端口號分為兩部分,少量的作為保留端

口,以全局方式分配給服務(wù)進(jìn)程。因此,每一個標(biāo)準(zhǔn)

服務(wù)器都擁有一個全局公認(rèn)的端口(即周知端口,

well-knownport),即使在不同機器上,其端口號也

相同。剩余的為自由端口,以本地方式進(jìn)行分配。

TCP和UDP均規(guī)定,小于256的端口號才能作保留端

口八

LINUX網(wǎng)輅編程

⑵地址

網(wǎng)絡(luò)通信中通信的兩個進(jìn)程分別在不同的機器上。

在互連網(wǎng)絡(luò)中,兩臺機器可能位于不同的網(wǎng)絡(luò),這些

網(wǎng)絡(luò)通過網(wǎng)絡(luò)互連設(shè)備(網(wǎng)關(guān),網(wǎng)橋,路由器等)連

接。因此需要三級尋址:

(a)某一主機可與多個網(wǎng)絡(luò)相連,必須指定一特定網(wǎng)絡(luò)

地址;

LINUX網(wǎng)輅編程

(b)網(wǎng)絡(luò)上母一臺主機應(yīng)有其唯一的地址;

(c)每一主機上的每一進(jìn)程應(yīng)有在該主機上的唯一標(biāo)識

符。

通常主機地址由網(wǎng)絡(luò)ID和主機ID組成,在TCP/IP

協(xié)議中用32位整數(shù)值表示;TCP和UDP均使用16位

端口號標(biāo)識用戶進(jìn)程。

LINUX網(wǎng)輅編程

(3)網(wǎng)絡(luò)字節(jié)順序

不同的計算機存放多字節(jié)值的順序不同,有的機器

在起始地址存放低位字節(jié)(低位先存),有的存高位

字節(jié)(高位先存)。為保證數(shù)據(jù)的正確性,在網(wǎng)絡(luò)協(xié)

議中須指定網(wǎng)絡(luò)字節(jié)順序。TCP/IP協(xié)議使用16位整數(shù)

和32位整數(shù)的高位先存格式,它們均含在協(xié)議頭文件

中。

LINUX網(wǎng)輅編程

(4)半相關(guān)

綜上所述,網(wǎng)絡(luò)中用一個三元組可以在全局唯一標(biāo)

志一個進(jìn)程:

(協(xié)議,本地地址,本地端口號)

這樣一個三元組,叫做一個半相關(guān)(half-

association),它指定連接的每半部分。

LINUX網(wǎng)輅編程

(5)全相關(guān)

一個完整的網(wǎng)間進(jìn)程通信需要由兩個進(jìn)程組成,并

且只能使用同一種高層協(xié)議。也就是說,不可能通信

的一端用TCP協(xié)議,而另一端用UDP協(xié)議。因此一

個完整的網(wǎng)間通信需要一個五元組來標(biāo)識:

(協(xié)議,本地地址,本地端口號,遠(yuǎn)地地址,遠(yuǎn)地端口

號)

LINUX網(wǎng)輅編程

這樣一個五元組,叫做一個相關(guān)(association),即兩

個協(xié)議相同的半相關(guān)才能組合成一個合適的相關(guān),或

完全指定組成一連接。

LINUX網(wǎng)輅編程

1.2.2客戶/服務(wù)器模式

在TCP/IP網(wǎng)絡(luò)應(yīng)用中,通信的兩個進(jìn)程間相互作

用的主要模式是客戶/服務(wù)器模式(Client/Server

model),即客戶向服務(wù)器發(fā)出服務(wù)請求,服務(wù)器接收

到請求后,提供相應(yīng)的服務(wù)。

客戶/服務(wù)器模式的建立基于以下兩點:首先,建立

網(wǎng)絡(luò)的起因是網(wǎng)絡(luò)中軟硬件資源、運算能力和信息不

均等,需要共享,從而造就擁有眾多資源的主機提供

服務(wù),資源較少的客戶請求服務(wù)這一非對等作用。

LINUX網(wǎng)輅編程

其次,網(wǎng)間進(jìn)程通信完全是異步的,相互通信的進(jìn)

程間既不存在父子關(guān)系,又不共享內(nèi)存緩沖區(qū),因此

需要一種機制為希望通信的進(jìn)程間建立聯(lián)系,為二者

的數(shù)據(jù)交換提供同步,這就是基于客戶/服務(wù)器模式的

TCP/IPo

LINUX網(wǎng)輅編程

客戶/服務(wù)器模式在操作過程中采取的是主動請求方

式:

首先服務(wù)器方要先啟動,并根據(jù)請求提供相應(yīng)服

務(wù):

(1)打開一通信通道并告知本地主機,它愿意在某

一公認(rèn)地址上(周知口,如FTP為21)接收客戶請求;

(2)等待客戶請求到達(dá)該端口;

LINUX網(wǎng)輅編程

(3)接收到重復(fù)服務(wù)請求,處理該請求并發(fā)送應(yīng)答信

號。接收到并發(fā)服務(wù)請求,要激活一新進(jìn)程來處理這

個客戶請求(如UNIX系統(tǒng)中用fork,exec)。新進(jìn)程處

理此客戶請求,并不需要對其它請求作出應(yīng)答。服務(wù)

完成后,關(guān)閉此新進(jìn)程與客戶的通信鏈路,并終止。

(4)返回第二步,等待另一客戶請求。

(5)關(guān)閉服務(wù)器。

LINUX網(wǎng)輅編程

客戶方:

1.打開一通信通道,并連接到服務(wù)器所在主機的

特定端口;

2.向服務(wù)器發(fā)服務(wù)請求報文,等待并接收應(yīng)答;

繼續(xù)提出請求……

3.請求結(jié)束后關(guān)閉通信通道并終止。

LINUX網(wǎng)輅編程

從上面所描述過程可知:

1.客戶與服務(wù)器進(jìn)程的作用是非對稱的,因此編碼

不同。

2.服務(wù)進(jìn)程一般是先于客戶請求而啟動的。只要系

統(tǒng)運行,該服務(wù)進(jìn)程一直存在,直到正?;驈娖冉K

止。

LINUX網(wǎng)輅編程

1.2.3套接字類型

TCP/IP的socket提供下列三種類型套接字。

1.流式套接字(SOCK_STREAM)

提供了一個面向連接、可靠的數(shù)據(jù)傳輸服務(wù),數(shù)據(jù)

無差錯、無重復(fù)地發(fā)送,且按發(fā)送順序接收。內(nèi)設(shè)流

量控制,避免數(shù)據(jù)流超限;數(shù)據(jù)被看作是字節(jié)流,無

長度限制。文件傳送協(xié)議(FTP)即使用流式套接字。

LINUX網(wǎng)輅編程

2.數(shù)據(jù)報式套接字(SOCK_DGRAM)

提供了一個無連接服務(wù)。數(shù)據(jù)包以獨立包形式被發(fā)

送,不提供無錯保證,數(shù)據(jù)可能丟失或重復(fù),并且接

收順序混亂。網(wǎng)絡(luò)文件系統(tǒng)(NFS)使用數(shù)據(jù)報式套接

字。

3.原始式套接字(SOCK_RAW)

該接口允許對較低層協(xié)議,如IP、ICMP直接訪問。

常用于檢驗新的協(xié)議實現(xiàn)或訪問現(xiàn)有服務(wù)中配置的新

設(shè)備。

LINUX網(wǎng)輅編程

1.3基本套接字系統(tǒng)調(diào)用

為了更好地說明套接字編程原理,下面給出幾個基

本套接字系統(tǒng)調(diào)用說明。

1.3.1創(chuàng)建套接字——socket()

應(yīng)用程序在使用套接字前,首先必須擁有一個套接

字,系統(tǒng)調(diào)用socket。向應(yīng)用程序提供創(chuàng)建套接字的手

段,其調(diào)用格式如下:

LINUX網(wǎng)輅編程

mtsocket(mtdomain,mttype,intprotocol)

domain:說明我們網(wǎng)絡(luò)程序所在的主機采用的通信

協(xié)議族(AFJJNIX和AFJNET等).UNIX系統(tǒng)支持的地

址族有:AFJJNIX、AFJNET>AF_NS等,而DOS、

WINDOWS中僅支持AF_INET,它是網(wǎng)際網(wǎng)區(qū)域。

AF_UNIX只能夠用于單一的Unix系統(tǒng)進(jìn)程間通信,而

AF_INET是針對Internet的,因而可以允許在遠(yuǎn)程主機

之間通信。

LINUX網(wǎng)輅編程

type:網(wǎng)絡(luò)程序所采用的通信協(xié)議(SOCK_STREAM,

SOCK_DGRAM等)。

SOCK_STREAM表明采用的是TCP協(xié)議,即提供

按順序的,可靠,雙向,面向連接的比特流。

SOCK_DGRAM表明采用的是UDP協(xié)議,這樣只

會提供定長的,不可靠,無連接的通信。

LINUX網(wǎng)輅編程

protocol:由于我們指定了type,所以這個地方我們一

般只要用0來代替就可以了socket為網(wǎng)絡(luò)通信做基本

的準(zhǔn)備.

成功時返回文件描述符,失敗時返回查看errno可

知道出錯的詳細(xì)情況.

根據(jù)這三個參數(shù)建立一個套接字,并將相應(yīng)的資源

分配給它,同時返回一個整型套接字號。

因此,socket。系統(tǒng)調(diào)用實際上指定了相關(guān)五元組中

的“協(xié)議”這一元。

LINUX網(wǎng)輅編程

1.3.2指定本地地址——bind()

當(dāng)一個套接字用socket。創(chuàng)建后,存在一個名字空間

(地址族),但它沒有被命名。bind。將套接字地址(包括

本地主機地址和本地端口地址)與所創(chuàng)建的套接字號

聯(lián)系起來,即將名字賦予套接字,以指定本地半相

關(guān)。其調(diào)用格式如下:

LINUX網(wǎng)輅編程

mtbind(mtsockfd,structsockaddr*my_addr,mt

addrlen)

sockfd:是由socket調(diào)用返回的文件描述符.

addrlen:是sockaddr結(jié)構(gòu)的長度.

my_addr:是一個指向sockaddr的指針.

sockaddr的定義:

LINUX網(wǎng)輅編程

structsockaddr{

unisgnedshortas_family;

charsa_data[14];

);

不過由于系統(tǒng)的兼容性,我們一般使用另外一個結(jié)構(gòu)

(structsockaddr_in)來代替.

sockaddr_in的定義:

LINUX網(wǎng)輅編程

structsockaddr_in{

shortsin_family;

u_shortsin_port;/*16位端口號,網(wǎng)絡(luò)字節(jié)順序*/

structin_addrsin_addr;/*32位IP地址,網(wǎng)絡(luò)字節(jié)順序刃

charsin_zero[8];/*保留刃

)

我們主要使用Internet所以sin_family一般為AF_INET,

sinaddr設(shè)置為INADDRANY表示可以和任何的主機

LINUX網(wǎng)輅編程

通信,sin_port是我們要監(jiān)聽的端口號.sin_zero⑻是用

來填充的.bind1尋本地的端口同socket返回的文件描述

符捆綁在一起.成功是返回0,失敗的情況和socket一

樣。

LINUX網(wǎng)輅編程

1.3.3建立套接字連接一connect()與accept()

這兩個系統(tǒng)調(diào)用用于完成一個完整相關(guān)的建立,其中

connect。用于建立連接。無連接的套接字進(jìn)程也可以調(diào)

用connect(),但這時在進(jìn)程之間沒有實際的報文交換,

調(diào)用將從本地操作系統(tǒng)直接返回。而accept。用于使服

務(wù)器等待來自某客戶進(jìn)程的實際連接。

connect。的調(diào)用格式如下:

intconnect(intsockfd,structsockaddr^serv_addr9int

addrlen)

LINUX網(wǎng)輅編程

參數(shù)sockfd是欲建立連接的本地套接字描述符。參

數(shù)serv_addr指出說明對方套接字地址結(jié)構(gòu)的指針。

對方套接字地址長度由addrlen說明。

connect函數(shù)是客戶端用來同服務(wù)端連接的。成功時

返回0,sockfd是同服務(wù)端通信的文件描述符,失敗時

返回

LINUX網(wǎng)輅編程

accept。的調(diào)用格式如下:

intaccept(intsockfd,structsockaddr*addr,int

^addrlen)

accept()調(diào)用前應(yīng)該先調(diào)用過listen()o

sockfd:是listen后的文件描述符。

addr,addrlen是用來給客戶端的程序填寫的,服務(wù)

器端只要傳遞指針就可以了。

LINUX網(wǎng)輅編程

調(diào)用前,參數(shù)addr指向一個初始值為空的地址結(jié)

構(gòu),而addrlen的初始值為0;調(diào)用accept()后,服務(wù)

器等待從編號為sockfd的套接字上接受客戶連接請

求,而連接請求是由客戶方的connect()調(diào)用發(fā)出的。

當(dāng)有連接請求到達(dá)時,accept()調(diào)用將請求連接隊列上

的第一個客戶方套接字地址及長度放入addr和addrlen.

LINUX網(wǎng)輅編程

bind,listen和accept是服務(wù)器端用的函數(shù),accept調(diào)

用時,服務(wù)器端的程序會一直阻塞到有一個客戶程序發(fā)

出了連接。accept成功時返回最后的服務(wù)器端的文件

描述符,這個時候服務(wù)器端可以向該描述符寫信息

了,失敗時返回4。

四個套接字系統(tǒng)調(diào)用,socket。、bind。、connect。、

accept(),可以完成一個完全五元相關(guān)的建立。socket()

指定五元組中的協(xié)議元,它的用法與是否為客戶或服

務(wù)器、是否面向連接無關(guān)。

LINUX網(wǎng)輅編程

bind()指定五元組中的本地二元,即本地主機地址和

端口號,其用法與是否面向連接有關(guān):在服務(wù)器方,

無論是否面向連接,均要調(diào)用bind。;在客戶方,若采

用面向連接,則可以不調(diào)用bind(),而通過connect。自

動完成。若采用無連接,客戶方必須使用bind。以獲得

一個唯一的地址。

LINUX網(wǎng)輅編程

1.3.4監(jiān)聽連接——listen()

此調(diào)用用于面向連接服務(wù)器,表明它愿意接收連

接。listen。需在accept。之前調(diào)用,其調(diào)用格式如下:

intlisten(intsockfd,intbacklog)

參數(shù)sockfd標(biāo)識一個本地已建立、尚未連接的套接

字號,服務(wù)器愿意從它上面接收請求。

LINUX網(wǎng)輅編程

backlog表示請求連接隊列的最大長度,用于限制排

隊請求的個數(shù),目前允許的最大值為5。如果沒有錯誤

發(fā)生,listen。返回0。否則它返回SOCKET_ERROR。

調(diào)用listen。是服務(wù)器接收一個連接請求的四個步驟

中的第三步。它在調(diào)用socket。分配一個流套接字,且

調(diào)用bind。給sockfd賦于一個名字之后調(diào)用,而且一

定要在accept。之前調(diào)用。

LINUX網(wǎng)輅編程

在客戶/服務(wù)器模式中,有兩種類型的服務(wù):重復(fù)服

務(wù)和并發(fā)服務(wù)。accept。調(diào)用為實現(xiàn)并發(fā)服務(wù)提供了極

大方便,因為它要返回一個新的套接字號,其典型結(jié)

構(gòu)為:

intinitsockid,newsockid;

if((initsockid=socket(....))<0)

error(ucan,tcreatesocket");

if(bind(initsockidv...)<0)

error(ubinderror");

LINUX網(wǎng)輅編程

if(listen(initsockid,5)v0)

error(ulistenerror");

for(;;){

newsockid=accept(initsockid,)/*阻塞*/

if(newsockid<0)

error(uaccepterror");

if(fork()==0){/*子進(jìn)程*/

closesocket(initsockid);

do(newsockid);/*處理請求*/

exit(0);

LINUX網(wǎng)輅編程

closesocket(newsockid);/*父進(jìn)程*/

)

這段程序執(zhí)行的結(jié)果是newsockid與客戶的套接字建

立相關(guān),子進(jìn)程啟動后,關(guān)閉繼承下來的主服務(wù)器的

initsockid,并利用新的newsockid與客戶通信。主服務(wù)

器的initsockid可繼續(xù)等待新的客戶連接請求。由于在

Unix等搶先多任務(wù)系統(tǒng)中,在系統(tǒng)調(diào)度下,多個進(jìn)程

可以同時進(jìn)行。因此,使用并發(fā)服務(wù)器可以使服務(wù)器

LINUX網(wǎng)輅編程

進(jìn)程在同一時間可以有多個子進(jìn)程和不同的客戶程序

連接、通信。在客戶程序看來,服務(wù)器可以同時并發(fā)

地處理多個客戶的請求,這就是并發(fā)服務(wù)器名稱的來

由。

LINUX網(wǎng)輅編程

面向連接服務(wù)器也可以是重復(fù)服務(wù)器,其結(jié)構(gòu)如下:

intinitsockid,newsockid;

if((initsockid=socket(....))<0)

error(ucan,tcreatesocket");

if(bind(initsockidv...)<0)

error(ubinderror");

if(listen(initsockid,5)<0)

error(ulistenerror");

LINUX網(wǎng)輅編程

for(;;){

newsockid=accept(initsockid,...)/*阻塞?/

if(newsockid<0)

error(uaccepterror");

do(newsockid);/*處理請求*/

closesocket(newsockid);

)

LINUX網(wǎng)輅編程

重復(fù)服務(wù)器在一個時間只能和一個客戶程序建立連

接,它對多個客戶程序的處理是采用循環(huán)的方式重復(fù)

進(jìn)行,因此叫重復(fù)服務(wù)器。

并發(fā)服務(wù)器和重復(fù)服務(wù)器各有利弊:并發(fā)服務(wù)器可

以改善客戶程序的響應(yīng)速度,但它增加了系統(tǒng)調(diào)度的

開銷;重復(fù)服務(wù)器正好與其相反,因此用戶在決定是

使用并發(fā)服務(wù)器還是重復(fù)服務(wù)器時,要根據(jù)應(yīng)用的實

際情況來定。

服務(wù)器方

LINUX網(wǎng)輅編程

客戶機方

建立連接

服務(wù)請求/應(yīng)答

LINUX網(wǎng)輅編程

1.4基本套接字通信程序?qū)嵗?/p>

服務(wù)器端(server-basic.c):

#include<stdio.h>

#include<errno.h>

#include<netdb.h>

#include<sys/socket.h>

intmain(intargc,char*argv[]){

intsockfd,new_fd;

structsockaddr_inserver_addr;

structsockaddr_inclient_addr;

LINUX網(wǎng)輅編程

intsin_size,portnumber;

charhello[]=HHello!AreYouFine?\nM;

if(argc!=2){

H1f

fprintf(stderr9Usage:%sportnumber\a\n,argv[0]);

exit(l);

)

if((portnumber=atoi(argv[l]))<0){

n1

fprintf(stderr9Usage:%sportnumber\a\n,argv[0]);

exit(l);

LINUX網(wǎng)輅編程

/*服務(wù)器端開始建立socket描述符*/

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

(

fprintf(stderr,nSocketerror:%s\n\a,\strerror(errno));

exit(l);

)

/*服務(wù)器端填充sockaddr結(jié)構(gòu)*/

bzero(&server_addr,sizeof(structsockaddr_in));

server_addr.sin_family=AF_INET;

server_addr.sin_addr.s_addr=htonl(INADDR_ANY);

server_addr.sin_port=htons(portnumber);

LINUX網(wǎng)輅編程

/*捆綁sockfd描述符*/

if(bind(sockfd,(structsockaddr*)(&server_addr),

sizeof(structsockaddr))==-1){

fprintf(stderr,nBinderror:%s\n\a*\strerror(errno));

exit(l);

)

/*監(jiān)聽sockfd描述符*/

if(listen(sockfd,5)==-1)

Hn

fprintf(stderr,Listenerror:%s\n\a9strerror(errno));

LINUX網(wǎng)輅編程

exit(l);

)

\¥11陽1){

/*服務(wù)器阻塞,直到客戶程序建立連接*/

sin_size=sizeof(structsockaddr_in);

if((new_fd=accept(sockfd,(structsockaddr*)(&client_addr),

&sin_size))==-1)

Hn

fprintf(stderr,Accepterror:%s\n\a9strerror(errno));

exit(l);

LINUX網(wǎng)輅編程

fprintf(stderr,HServergetconnectionfrom%s\nH,

inet_ntoa(client_addr.sin_addr));

if(write(new_fd,hello,strlen(hello))==-l){

Mn

fprintf(stderr,WriteError:%s\n9strerror(errno));

exit(l);

)

/*這個通信已經(jīng)結(jié)束*/

close(new_fd);

/*循環(huán)下一^個*/

LINUX網(wǎng)輅編程

close(sockfd);

exit(O);

)

客戶端(client-basic?c):

#include<stdio.h>

#include<netdb.h>

#include<sys/socket.h>

#include<sys/errno.h>

intmain(intargc,char*argv口)

LINUX網(wǎng)輅編程

mtsockfd;

charbuffer[1024];

structsockaddr_inserver_addr;

structhostent*host;

intportnumber,nbytes;

if(argc!=3){

n1

fprintf(stderr9Usage:%shostnameportnumber\a\n*,

argv[0]);

exit(l);

LINUX網(wǎng)輅編程

if((host=gethostbyname(argv[l]))==NULL){

fprintf(stderr,nGethostnameerror\nn);

exit(l);

)

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

fprintf(stderr,HUsage:%shostnameportnumber\a\n1*,

argv[0]);

exit(l);

)

LINUX網(wǎng)輅編程

/*客戶程序開始建立sockfd描述符*/

if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-l){

M

fprintf(stderr9^SocketError:%s\a\n,strerror(errno));

exit(l);

)

/*客戶程序填充服務(wù)端的資料*/

bzero(&server_addr,sizeof(server_addr));

server_addr.sin_family=AF_INET;

server_addr.sin_port=htons(portnumber);

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

LINUX網(wǎng)輅編程

/*客戶程序發(fā)起連接請求*/

if(connect(sockfd,(structsockaddr*)(&server_addr),

sizeof(structsockaddr))==-1){

fprintf(stderr,nConnectError:%s\a\nf\strerror(errno));

exit(l);

)

/*連接成功*/

if((nbytes=read(sockfd,buffer,1024))==-1){

fprintf(stderr,nReadError:%s\nf\strerror(errno));

exit(l);

LINUX網(wǎng)輅編程

buffer[nbytes]=H\On;

printf(MIhavereceived:%s\nf\buffer);

/*結(jié)束通信*/

close(sockfd);

exit(O);

LINUX網(wǎng)輅編程

1.5服務(wù)器和客戶機的信息函數(shù)

1.5.1字節(jié)轉(zhuǎn)換函數(shù)

在網(wǎng)絡(luò)上面有著許多類型的機器,這些機器在表示數(shù)

據(jù)的字節(jié)順序是不同的,比如i386芯片是低字節(jié)在內(nèi)存

地址的低端,高字節(jié)在高端,而alpha芯片卻相反.為了

統(tǒng)一起來在Linux下面,有專門的字節(jié)轉(zhuǎn)換函數(shù).

LINUX網(wǎng)輅編程

unsignedlongmthtonl(unsignedlongmthostlong)

unsignedshortinthtons(unisgnedshortinthostshort)

unsignedlongintntohl(unsignedlongintnetlong)

unsignedshortintntohs(unsignedshortintnetshort)

在這四個轉(zhuǎn)換函數(shù)中,h代表host,n代表network,s

代表short,1代表long,第一個函數(shù)的意義是將本機器

上的long數(shù)據(jù)轉(zhuǎn)化為網(wǎng)絡(luò)上的long.其他幾個函數(shù)的意

義同上.

LINUX網(wǎng)輅編程

1.5.2IP和域名的轉(zhuǎn)換

在網(wǎng)絡(luò)上標(biāo)志一臺機器可以用IP或者是用域名,那

么我們怎么去進(jìn)行轉(zhuǎn)換呢?

structhostent*gethostbyname(constchar*hostname)

structhostent*gethostbyaddr(constchar*addr,intleu,inttype)

structhostent的定義:

LINUX網(wǎng)輅編程

structhostent{

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

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

inth_addrtype;主機的地址類型AF_INET*/

inthJength;/*主機的地址長度對于IP4是4字節(jié)32位*/

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

#defineh_addrh_addr_list[O]/*主機的第一個IP地址*/

)

gethostbyname可以將機器名(如)#

換為一個結(jié)構(gòu)指針.在這個結(jié)構(gòu)里面儲存了域名的信息.

LINUX網(wǎng)輅編程

gethostbyaddr可以將一個32位的IP地址(C0A80001)

轉(zhuǎn)換為結(jié)構(gòu)指針.

這兩個函數(shù)失敗時返回NULL且設(shè)置h_errno錯誤

變量,調(diào)用h_strerror()可以得到詳細(xì)的出錯信息.

1.5.3字符串的IP和32位IP的轉(zhuǎn)換

在網(wǎng)絡(luò)上面我們用的IP都是數(shù)字加點()

構(gòu)成的,而在structin_addr結(jié)構(gòu)中用的是32位的IP,

我們上面那個32位IP(C0A80001)是的,為

了轉(zhuǎn)換我們可以使用下面兩個函數(shù)

LINUX網(wǎng)輅編程

mtmet_aton(constchar*cp,structm_addr*mp)

char^inet_ntoa(structin_addrin)

函數(shù)里面a代表asciin代表network.第一個函數(shù)表

示將a.b.c.d的IP轉(zhuǎn)換為32位的IP,存儲在inp指針里

面.第二個是修32位IP轉(zhuǎn)換為a.b.c.d的格式.

LINUX網(wǎng)輅編程

1.6完整的讀寫函數(shù)

在Linux中把我們前面建立的通道看成是文件描述

符,這樣服務(wù)器端和客戶端進(jìn)行通信時候,只要往文件描

述符里面讀寫東西了.就象我們往文件讀寫一樣.

1.6.1寫函數(shù)write

intwrite(intfd,constvoid*buf,size_tnbytes)

write函數(shù)將buf中的nbytes字節(jié)內(nèi)容寫入文件描述

符fd.成功時返回寫的字節(jié)數(shù).失敗時返回并設(shè)置

errno變量.

LINUX網(wǎng)輅編程

在網(wǎng)絡(luò)程序中,當(dāng)我們向套接字文件描述符寫時有兩

種可能.

(1)write的返回值大于0,表示寫了部分或者是全部的

數(shù)據(jù).

(2)返回的值小于0,此時出現(xiàn)了錯誤.我們要根據(jù)錯誤

類型來處理.

如果錯誤為EINTR表示在寫的時候出現(xiàn)了中斷錯誤.

LINUX網(wǎng)輅編程

如果為EPIPE表示網(wǎng)絡(luò)連接出現(xiàn)了問題(對方已經(jīng)

關(guān)閉了連接).

1.6.2讀函數(shù)read

intread(intfd,void*buf,size_tnbyte)

read函數(shù)是負(fù)責(zé)從fd中讀取內(nèi)容.當(dāng)讀成功時,read

返回實際所讀的字節(jié)數(shù),如果返回的值是0表示已經(jīng)讀

到文件的結(jié)束了,小于0表示出現(xiàn)了錯誤.如果錯誤為

EINTR說明讀是由中斷引起的,如果是ECONNREST

表示網(wǎng)絡(luò)連接出了問題.

LINUX網(wǎng)輅編程

1.6.3send()與recv()

send和recv函數(shù)提供了和read和write差不多的功

能.

send()調(diào)用用于在參數(shù)s指定的已連接的數(shù)據(jù)報或流

套接字上發(fā)送輸出數(shù)據(jù),格式如下:

intsend(intsockfd,void*buf,intlen,intflags)

參數(shù)sockfd為已連接的本地套接字描述符。buf指

向存有發(fā)送數(shù)據(jù)的緩沖區(qū)的指針,其長度由len指定。

flags指定傳輸控制方式,如是否發(fā)送帶外數(shù)據(jù)等。

LINUX網(wǎng)輅編程

如果沒有錯誤發(fā)生,send。返回總共發(fā)送的字節(jié)數(shù)。否

則它返回SOCKET_ERROR。

recv()調(diào)用用于在參數(shù)sockfd指定的已連接的數(shù)據(jù)

報或流套接字上接收輸入數(shù)據(jù),格式如下:

intrecv(intsockfd,void*buf,intleu,intflags)

參數(shù)sockfd為已連接的套接字描述符。Buf指向接收

輸入數(shù)據(jù)緩沖區(qū)的指針,其長度由len指定。flags指

定傳輸控制方式,如是否接收帶外數(shù)據(jù)等。

LINUX網(wǎng)輅編程

如果沒有錯誤發(fā)生,recv()返回總共接收的字節(jié)數(shù)。如

果連接被關(guān)閉,返回0。否則它返回

SOCKETERRORo

LINUX網(wǎng)輅編程

1.6.4關(guān)閉套接字——close()

closesocket()關(guān)閉套接字sockfd,并釋放分配給該套

接字的資源;如果sockfd涉及一個打開的TCP連接,

則該連接被釋放。dosesocket。的調(diào)用格式如下:

intclose(intsockfd)

參數(shù)sockfd為待關(guān)閉的套接字描述符。如果沒有錯

誤發(fā)生,closesocket()返回0。否則返回值

SOCKETERRORo

LINUX網(wǎng)輅編程

1.7完整的數(shù)據(jù)收發(fā)與套接字關(guān)閉程序

服務(wù)器端(server-standard.c):

#include<stdio.h>

#include<errno.h>

#include<netdb.h>

#include<sys/socket.h>

intmain(intargc,char*argv[])

intsockfd,new_fd;

structsockaddr_inserver_addr;

structsockaddr_inclient_addr;

LINUX網(wǎng)輅編程

intsin_size,portnumber;

charhello[]=HHello!AreYouFine?\nM;

if(argc!=2)

(

H1

fprintf(stderr9Usage:%sportnumber\a\n*,argv[0]);

exit(l);

)

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

(

fprintf(stderr,MUsage:%sportnumber\a\n1,argv[0]);

exit(l);

)

LINUX網(wǎng)輅編程

/*服務(wù)器端開始建立socket描述符*/

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

(

fprintf(stderr,nSocketerror:%s\n\a,\strerror(errno));

exit(l);

)

/*服務(wù)器端填充sockaddr結(jié)構(gòu)*/

bzero(&server_addr,sizeof(structsockaddr_in));

server_addr.sin_family=AF_INET;

server_addr.sin_addr.s_addr=htonl(INADDR_ANY);

server_addr.sin_port=htons(portnumber);

LINUX網(wǎng)輅編程

/*捆綁sockfd描述符*/

if(bind(sockfd,(structsockaddr*)(&server_addr)9sizeof(struct

sockaddr))==-1)

(

Kn

fprintf(stderr,Bmderror:%s\n\a9strerror(errno));

exit(l);

)

/*監(jiān)聽sockfd描述符*/

if(listen(sockfd,5)==-1){

fprintf(stderr,^Listenerror:%s\n\a,\strerror(errno));

LINUX網(wǎng)輅編程

exit(l);

)

\¥11陽1){

/*服務(wù)器阻塞,直到客戶程序建立連接*/

sin_size=sizeof(structsockaddr_in);

if((new_fd=accept(sockfd,(structsockaddr*)(&client_addr),

&sin_size))==-1)

Hn

fprintf(stderr,Accepterror:%s\n\a9strerror(errno));

exit(l);

LINUX網(wǎng)輅編程

fprintf(stderr,HServergetconnectionfrom

inet_ntoa(client_addr.sin_addr));

if(write(new_fd,hello,strlen(hello))==-l){

1n

fprintf(stderr,,WriteError:%s\n9strerror(errno));

exit(l);

)

while(l)

intflag;

LINUX網(wǎng)輅編程

if((flag=read(new_fd,buf,1024))<0)

(

printf(nReadingdataerror!\nn);

break;

)

if(flag==0)

(

printf(MEndingcurrentconnection!\nH);

break;

)

else

LINUX網(wǎng)輅編程

prmtf(—>%s\n\buf);

if(strstr(buf,nexitn)){

printf(HEndingcurrentconnection!\nM);

break;

)

//if(strstr(buf,nbyen))

//{

//close(new_fd);

//close(sockfd);

//exit(O);

//)

LINUX網(wǎng)輅編程

}//else

}//while

close(new_fd);

}//while

close(sockfd);

exit(O);

LINUX網(wǎng)輅編程

客戶端(client.standard.c):

#include<stdio.h>

#include<netdb.h>

#include<sys/socket.h>

#include<sys/errno.h>

intmain(intargc,char*argv[]){

LINUX網(wǎng)輅編程

mtsockfd;

charbuffer[1024];

structsockaddr_inserver_addr;

structhostent*host;

intportnumber,nbytes;

if(argc!=3)

(

fprintf(stderr,HUsage:%shostnameportnumber\a\n1,

argv[0]);

exit(l);

LINUX網(wǎng)輅編程

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

(

fprintf(stderr,"Gethostnameerror\nM);

exit(l);

)

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

(

n1

fprintf(stderr9Usage:%shostnameportnumber\a\n*,

argv[0]);

exit(l);

LINUX網(wǎng)輅編程

/*客戶程序開始建立sockfd描述符*/

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

(

nf

fprintf(stderr9SocketError:%s\a\n\strerror(errno));

exit(l);

)

/*客戶程序填充服務(wù)端的資料*/

bzero(&server_addr,sizeof(server_addr));

server_addr.sin_family=AF_INET;

server_addr.sin_port=htons(portnumber);

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

LINUX網(wǎng)輅編程

/*客戶程序發(fā)起連接請求*/

if(connect(sockfd,(structsockaddr*)(&server_addr),

sizeof(structsockaddr))==-1)

(

nn

fprintf(stderr,ConnectError:%s\a\n9strerror(errno));

exit(l);

)

/*連接成功了*/

if((nbytes=read(sockfd,buffer,1024))==-1)

LINUX網(wǎng)輅編程

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

exit(l);

)

buffer[nbytes]=**\0n;

Mn

printf(Ihavereceived:%s\n9buffer);

while(l)

gets(buffer);

if(wri陽sockfd,buffer,sizeof(buffer))<0)

printf(Msendingdataerror!\nn);

LINUX網(wǎng)輅編程

if(strstr(buffer,exitn))

break;

)

/*結(jié)束通信*/

close(sockfd);

exit(O);

LINUX網(wǎng)輅編程

1.8完整的多進(jìn)程套接字通信程序

月艮務(wù)器端(server-advance.c):

#include<stdio.h>

#include<errno.h>

#include<netdb.h>

#include<sys/socket.h>

//maybeusebygetpid()andgetppid()

#include<unistd.h>

intmain(intargc,char*argv[])

LINUX網(wǎng)輅編程

intsockfd,new_fd;

structsockaddr_inserver_addr;

structsockaddr_inclient_addr;

intsin_size,portnumber;

charhello[]=nHello!AreYouFine?'';

charbuf[1024];

intflag;

if(argc!=2)

M11

fprintf(stderr9Usage:%sportnumber\a\n,argv[0]);

exit(l);

LINUX網(wǎng)輅編程

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

(

fprintf(stderr,nUsage:%sportnumber\a\n1*,argv[O]);

exit(l);

)

/*服務(wù)器端開始建立socket描述符*/

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

nM

fprintf(stderr,Socketerror:%s\n\a9strerror(errno));

exit(l);

LINUX網(wǎng)輅編程

/*服務(wù)器立而填充sockaddr結(jié)構(gòu)*/

bzero(&server_addr,sizeof(structsockaddr_in));

server_addr.sin_family=AF_INET;

server_addr.sin_addr.s_addr=htonl(INADDR_ANY);

server_addr.sin_port=htons(portnumber);

/*捆綁sockfd描述符*/

if(bind(sockfd,(structsockaddr*)(&server_addr),sizeof(struct

sockaddr))=="l)

(

fprintf(stderr,HBmderror:%s\n\af\strerror(errno));

exit(l);

)

LINUX網(wǎng)輅編程

/*監(jiān)聽sockfd描述符*/

if(listen(sockfd95)==-1)

(

fprintf(stderr,HListenerror:%s\n\af\strerror(errno));

exit(l);

)

//printfmyPIDnum

printf(nStartingServeratport:%dSuccess.MyPIDis:%d\n

AcceptingRequest…\n'',portnumber,getpid());

while(l)

/*服務(wù)器阻塞,直到客戶程序建立連接*/

LINUX網(wǎng)輅編程

sin_size=sizeof(structsockaddr_in);

if((new_fd=accept(sockfd,(structsockaddr*)(&client_addr),

&sin_size))=="l)

(

fprintf(stderr,nAc

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論