Linux-網(wǎng)絡(luò)通信編程_第1頁(yè)
Linux-網(wǎng)絡(luò)通信編程_第2頁(yè)
Linux-網(wǎng)絡(luò)通信編程_第3頁(yè)
Linux-網(wǎng)絡(luò)通信編程_第4頁(yè)
Linux-網(wǎng)絡(luò)通信編程_第5頁(yè)
已閱讀5頁(yè),還剩78頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡(jiǎn)介

Linux

操作系統(tǒng)與程序設(shè)計(jì)軟件工程系何海濤內(nèi)容1.概念:協(xié)議套接字通信2.TCPUDPFTP通信3.多線(xiàn)程多進(jìn)程服務(wù)器SchoolofComputerSicence幾個(gè)概念進(jìn)程通信:?jiǎn)螜C(jī):進(jìn)程之間交換信息(通過(guò)pipe,signal等)網(wǎng)絡(luò):不同計(jì)算機(jī)(軟/硬件)之間的信息交換--最終,仍是進(jìn)程通信需要解決的問(wèn)題進(jìn)程標(biāo)識(shí)協(xié)議差錯(cuò)控制、流量控制、報(bào)文順序、連接管理......解決方案TCP/IP協(xié)議+Socket編程機(jī)制SchoolofComputerSicence服務(wù)和端口服務(wù)器客戶(hù)上網(wǎng)電子郵件文件傳輸一個(gè)IP65536個(gè)端口SchoolofComputerSicence編寫(xiě)網(wǎng)絡(luò)通信程序TCP/IP協(xié)議制定了通信雙方通信的細(xì)節(jié):如數(shù)據(jù)包的格式,建立連接的形式等協(xié)議的實(shí)現(xiàn)在每個(gè)系統(tǒng)上是不一樣的,而且協(xié)議很復(fù)雜,具體實(shí)現(xiàn)用到了成百上千個(gè)函數(shù)和無(wú)數(shù)的結(jié)構(gòu)體,即使程序員知道了協(xié)議細(xì)則,要直接調(diào)用協(xié)議中各種函數(shù)完成一個(gè)網(wǎng)絡(luò)通信程序是很困難的如何簡(jiǎn)化:把協(xié)議“封裝”的簡(jiǎn)單一點(diǎn),如同打電話(huà)SchoolofComputerSicenceSocket接口socket:套接字,是一組接口,使得編寫(xiě)網(wǎng)絡(luò)通信程序,如同打電話(huà)般簡(jiǎn)單Socket的英文原義是“孔”或“插座”:一臺(tái)主機(jī)猶如布滿(mǎn)各種插座的房間,每個(gè)插座有一個(gè)編號(hào),有的插座提供220伏交流電,有的提供110伏交流電,有的則提供有線(xiàn)電視節(jié)目??蛻?hù)軟件將插頭插到不同編號(hào)的插座,就可以得到不同的服務(wù)。SchoolofComputerSicencesocket編程和打電話(huà)IP地址和端口三次握手或UDPsendreceive電話(huà)用哪個(gè)運(yùn)營(yíng)商的電話(huà)打給誰(shuí)撥號(hào)和接聽(tīng)交談掛斷socket用什么協(xié)議通信和誰(shuí)通信請(qǐng)求和接受收發(fā)信息關(guān)閉socket步驟對(duì)比SchoolofComputerSicencesocket實(shí)例網(wǎng)絡(luò)通信涉及到2臺(tái)電腦如果是C/S模式:一臺(tái)作為“服務(wù)器”,一臺(tái)為“客戶(hù)機(jī)”也可以對(duì)等P2P:地位平等服務(wù)器:提供服務(wù)接受客戶(hù)端的連接響應(yīng)客戶(hù)端的要求給客戶(hù)端發(fā)消息客戶(hù)端:向服務(wù)器發(fā)送請(qǐng)求從服務(wù)器收取消息SchoolofComputerSicencesocket實(shí)例服務(wù)器程序先運(yùn)行,等待客戶(hù)端請(qǐng)求/消息到來(lái)客戶(hù)端向服務(wù)器發(fā)送一個(gè)字符串hello服務(wù)器在終端輸出客戶(hù)端發(fā)來(lái)的字符串SchoolofComputerSicence最簡(jiǎn)單的UDP服務(wù)器udps.c#include<stdio.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>intmain(intargc,char**argv){intsockfd;structsockaddr_inservaddr;charbuff[1024];intn,sinsize;

sockfd=socket(AF_INET,SOCK_DGRAM,0);memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_addr.s_addr=INADDR_ANY;servaddr.sin_port=htons(9091);bind(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr));sinsize=sizeof(servaddr);n=recvfrom(sockfd,buff,1024,0,NULL,&sinsize);buff[n]='\0';printf("recvmsgfromclient:%s\n",buff);close(sockfd);}SchoolofComputerSicence最簡(jiǎn)單的UDP客戶(hù)端udpc.c#include…...intmain(intargc,char**argv){intsockfd,n;charsendline[1024];structsockaddr_inservaddr;sockfd=socket(AF_INET,SOCK_DGRAM,0);memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_port=htons(9091);servaddr.sin_addr.s_addr=inet_aton("");n=sizeof(structsockaddr);sendto(sockfd,"hello",5,0,(structsockaddr*)&servaddr,n);close(sockfd);exit(0);}SchoolofComputerSicence編譯運(yùn)行同一臺(tái)電腦,打開(kāi)2個(gè)終端分別編譯運(yùn)行服務(wù)器程序和客戶(hù)端程序注意:不能不指定輸出的可執(zhí)行文件名,必須使用-o參數(shù),否則,兩個(gè)程序默認(rèn)的可執(zhí)行文件都是a.out,會(huì)沖突不同的計(jì)算機(jī)之間需要服務(wù)器的IP地址客戶(hù)端程序中修改IP地址使用命令ifconfig查看本機(jī)的IP地址SchoolofComputerSicence基本socketAPIsocket()創(chuàng)建一個(gè)套接字,#include<sys/socket.h>函數(shù)原型:intsocket(intdomain,inttype,intprotocol);參數(shù)說(shuō)明domain:通信協(xié)議族,即地址族,通常是AF_INET(TCP/IP(V4))type:套接字類(lèi)型SOCK_STREAM:TCP協(xié)議SOCK_DGRAM:UDP協(xié)議SOCK_RAW:原始套接字protocol:通信協(xié)議,設(shè)置為0,由內(nèi)核根據(jù)指定的類(lèi)型和協(xié)議族使用默認(rèn)的協(xié)議返回值:成功時(shí),返回一個(gè)大于等于0的文件描述符:可以用文件讀寫(xiě)函數(shù)來(lái)操作socket失敗時(shí),返回一個(gè)小于0的值關(guān)閉:close(socketfd)類(lèi)似于關(guān)閉文件,回收資源intsockfd;sockfd=socket(AF_INET,SOCK_DGRAM,0);SchoolofComputerSicence地址的表示---sockaddr_in結(jié)構(gòu)體//定義結(jié)構(gòu)體變量servadd

structsockaddr_inservaddr;//初始化變量

memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_addr.s_addr=INADDR_ANY;

servaddr.sin_port=htons(9091);程序中,需要用一個(gè)數(shù)據(jù)結(jié)構(gòu)保存地址信息,包括:端口號(hào)和IP地址等原本地址表示用的結(jié)構(gòu)體是:sockaddr,但因?yàn)檫@個(gè)結(jié)構(gòu)體使用不方便,人們又重新創(chuàng)建了個(gè)sockaddr_in結(jié)構(gòu)體來(lái)代替sockaddrSchoolofComputerSicencesockaddr_in結(jié)構(gòu)體structsockaddr_in{

shortintsin_family;/*地址族*/

unsignedshortintsin_port;/*端口號(hào)*/

structin_addrsin_addr;/*IP地址*/

unsignedcharsin_zero[8];/*湊數(shù),為了使sockaddr_in和sockaddr長(zhǎng)度相同*/};in_addr結(jié)構(gòu)體:存放IP地址structin_addr{unsignedlongs_addr;//32-bit無(wú)符號(hào)長(zhǎng)整形};即:ip地址本身是一個(gè)數(shù),但平時(shí)使用的是字符串,如"6",把字符串賦值給需要轉(zhuǎn)換,使用inet_aton函數(shù)(internetasctonum)如:inet_aton("")本機(jī)IP地址的賦值操作為:

結(jié)構(gòu)體變量.sin_addr.s_addr=inet_aton("");或

sin_addr=INADDR_ANY;表示填入本機(jī)IP地址SchoolofComputerSicence初始化結(jié)構(gòu)體memset:清零當(dāng)sin_addr=INADDR_ANY時(shí),填入本機(jī)IP地址端口號(hào):除了系統(tǒng)保留的(1~1024),可以自己指定端口(1025~65535),但需要轉(zhuǎn)換字節(jié)順序(用htons函數(shù))//定義結(jié)構(gòu)體變量servadd

structsockaddr_inservaddr;//初始化變量

memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_addr.s_addr=INADDR_ANY;

servaddr.sin_port=htons(9091);SchoolofComputerSicence字節(jié)順序不同的CPU有不同的字節(jié)順序類(lèi)型,這些字節(jié)順序類(lèi)型指的是整數(shù)在內(nèi)存中保存的順序,即主機(jī)字節(jié)順序(HBO,HostByteOrder)常見(jiàn)的有兩種:大端模式(big-endian):地址的高位存儲(chǔ)值的低位,如部分MIPS,POWERPC機(jī)器小端模式(little-endian):地址的低位存儲(chǔ)值的低位,如Intelx86機(jī)器以u(píng)nsigned

int

value

=

0x12345678為例其值的低位和高位分別是?低位高位x86電腦上:addraddr+1addr+2addr+378563412大端電腦上:addraddr+1addr+2addr+312345678網(wǎng)絡(luò)上有各種各樣的機(jī)器,為保證解析正確性和可移植性,要統(tǒng)一順序。host-to-network:hton():把主機(jī)順序轉(zhuǎn)換為網(wǎng)絡(luò)順序(大端順序)network-to-host:ntoh():收到數(shù)據(jù)把時(shí)把網(wǎng)絡(luò)字節(jié)轉(zhuǎn)換為主機(jī)根據(jù)轉(zhuǎn)換的數(shù)類(lèi)型:short,long,共4個(gè)函數(shù):htons,htonl,ntohs,ntohlSchoolofComputerSicence指定服務(wù)器端口號(hào)(1).HTTP協(xié)議代理服務(wù)器常用端口號(hào):80/8080/3128/8081/9080(2).SOCKS代理協(xié)議服務(wù)器常用端口號(hào):1080(3).FTP(文件傳輸)協(xié)議代理服務(wù)器常用端口號(hào):21(4).Telnet(遠(yuǎn)程登錄)協(xié)議代理服務(wù)器常用端口:23(5).SMTP/POP3:25/110小于256的端口作為保留端口通常自己指定的端口號(hào)可以大于1024structsockaddr_in

servaddr;memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_addr.s_addr=inet_aton("");

servaddr.sin_port=htons(9091);SchoolofComputerSicence綁定bindLinux下一切皆文件發(fā)送和接受網(wǎng)絡(luò)數(shù)據(jù),也是通過(guò)讀寫(xiě)文件完成這里的文件,指的是"socket"。為了實(shí)現(xiàn)網(wǎng)絡(luò)通信的目標(biāo),還需要把socket文件和IP地址,端口號(hào)等綁定(關(guān)聯(lián))起來(lái)。WriteSOCKET文件主機(jī)端口端口端口網(wǎng)絡(luò)SchoolofComputerSicencebind()函數(shù)intbind(intsockfd,structsockaddr*my_addr,socklen_taddrlen);參數(shù)說(shuō)明sockfd:調(diào)用socket返回的文件描述符my_addr:保存地址信息(IP地址和端口)addrlen:設(shè)置為sizeof(structsockaddr)返回值成功時(shí),返回0失敗時(shí),返回-1(如端口被占用)intsockfd;

structsockaddr_inservaddr;sockfd=socket(AF_INET,SOCK_DGRAM,0);memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_addr.s_addr=inet_aton("");servaddr.sin_port=htons(9091);

bind(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr));servaddr的類(lèi)型是sockaddr_in,不是sockaddr*類(lèi)型,做一個(gè)強(qiáng)制類(lèi)型轉(zhuǎn)換SchoolofComputerSicence接收網(wǎng)絡(luò)信息一切就緒,等待從網(wǎng)絡(luò)來(lái)的消息(讀socket文件)intrecvfrom(intsockfd,void*buf,intlen,unsignedintflags,structsockaddr*from,int*fromlen);sockfd:將要從其接收數(shù)據(jù)的套接字buf:存放消息接收后的緩沖區(qū)len:buf所指緩沖區(qū)的容量flags:接收數(shù)據(jù)的一些參數(shù),為0則為默認(rèn)from:保存數(shù)據(jù)來(lái)源(ip,端口),如不需保存可以設(shè)為NULLfromlen:from的長(zhǎng)度地址(注意是指針)成功執(zhí)行時(shí),返回接收到的字節(jié)數(shù)。另一端已關(guān)閉則返回0。失敗返回-1。recvfrom默認(rèn)是阻塞型structsockaddr_inservaddr;charbuff[1024];sinsize=sizeof(servaddr);

n=recvfrom(sockfd,buff,1024,0,NULL,&sinsize);SchoolofComputerSicenceRecvfrom應(yīng)用舉例執(zhí)行到recvfrom,程序暫停,等待從端口過(guò)來(lái)消息。有信息到來(lái),把消息存放到緩沖區(qū),并解析包,把源地址和端口存放到相應(yīng)變量中,然后繼續(xù)往下執(zhí)行如果一次到來(lái)的消息緩沖區(qū)放不下,則丟棄多余的包c(diǎn)harbuff[4096];structsockaddr_inservaddr;//服務(wù)器地址信息structsockaddr_inclientaddr;//客戶(hù)端地址,用來(lái)保存從哪發(fā)過(guò)來(lái)的size=sizeof(sockaddr);n=recvfrom(sockfd,buff,4096,0,(structsockaddr*)&clientaddr,&size);buff[n]='\0';printf("recvmsgfromclient:%s\n",buff);printf("消息來(lái)自于IP:%s\n",inet_ntoa(clientaddr.sin_addr));

……在64位系統(tǒng)上,要用#include<arpa/inet.h>,inet_ntoa才能正常運(yùn)行SchoolofComputerSicenceIP地址轉(zhuǎn)換將點(diǎn)分十進(jìn)制字符串轉(zhuǎn)換成長(zhǎng)整型數(shù)inet_addr("")inet_aton("")intinet_pton(intaf,constchar*src,void*dst);inet_pton(AF_INET,ip,&servaddr.sin_addr);第一個(gè)參數(shù)af是地址族,轉(zhuǎn)換后存在dst中將長(zhǎng)整型IP地址轉(zhuǎn)換成點(diǎn)分字符串char*inet_ntoa(structin_addraddr)如inet_ntoa(clientaddr.sin_addr)constchar*inet_ntop(intaf,constvoid*src,char*dst,socklen_tlen);把src指向的in_addr數(shù)字地址轉(zhuǎn)換為字符串,len:dst的長(zhǎng)度推薦使用inet_pton和inet_ntop,其他函數(shù)為不可重入函數(shù)SchoolofComputerSicence完善UDP服務(wù)器自己動(dòng)手完善服務(wù)器1.輸出客戶(hù)端信息2.給recvfrom加上循環(huán),使服務(wù)器能一直接受客戶(hù)端連接好習(xí)慣是不要省略函數(shù)返回值的判斷,否則出錯(cuò)時(shí)不容易定位......#include<arpa/inet.h>intmain(intargc,char**argv){intsockfd;structsockaddr_inservaddr,clientAddr;charbuff[1024];intn,sinsize;sockfd=socket(AF_INET,SOCK_DGRAM,0);

//判斷socket是否創(chuàng)建成功memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_addr.s_addr=inet_aton("");servaddr.sin_port=htons(9091);bind(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr));

//判斷bind是否綁定成功sinsize=sizeof(servaddr);

//修改recvfrom,填入客戶(hù)端地址

//n=recvfrom(sockfd,buff,1024,0,NULL,&sinsize);buff[n]='\0';

//輸出客戶(hù)端IP地址,和端口號(hào)(clientAddr.sin_port)SchoolofComputerSicenceUDP客戶(hù)端發(fā)送數(shù)據(jù)的步驟建立socket發(fā)送數(shù)據(jù)sendto:給出服務(wù)器/目標(biāo)端的IP地址和端口號(hào),以及要發(fā)送數(shù)據(jù)的首地址說(shuō)明:不需要綁定bind,在發(fā)送數(shù)據(jù)時(shí)系統(tǒng)自動(dòng)隨機(jī)選擇一個(gè)端口發(fā)送數(shù)據(jù)SchoolofComputerSicencesendto發(fā)送數(shù)據(jù)函數(shù)函數(shù)原型intsendto(intsockfd,constvoid*msg,intlen,unsignedintflags,conststructsockaddr*to,inttolen);參數(shù)解釋sockfd:同recvfrom,獲得的socket文件句柄msg:要發(fā)送數(shù)據(jù)的指針len:數(shù)據(jù)長(zhǎng)度f(wàn)lags:一些參數(shù)to:發(fā)送的目的地tolen:to結(jié)構(gòu)體的長(zhǎng)度返回值成功時(shí),返回實(shí)際發(fā)送的數(shù)據(jù)的字節(jié)數(shù)失敗時(shí),返回-1SchoolofComputerSicence完善UDP客戶(hù)端修改程序:1.從鍵盤(pán)輸入字符串發(fā)送2.給sendto加上循環(huán),可以循環(huán)發(fā)送(*)給main函數(shù)帶參數(shù),可以給不同IP的服務(wù)器發(fā)送#include…...intmain(intargc,char**argv){intsockfd,n;charsendline[1024];structsockaddr_inservaddr;sockfd=socket(AF_INET,SOCK_DGRAM,0);

//判斷socket是否創(chuàng)建成功memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_port=htons(9091);servaddr.sin_addr.s_addr=inet_addr("");n=sizeof(structsockaddr);

//從鍵盤(pán)輸入一個(gè)字符串發(fā)送

//sendto(sockfd,.....,...,0,(structsockaddr*)&servaddr,n);

close(sockfd);exit(0);}SchoolofComputerSicence幾點(diǎn)注意事項(xiàng)字符串定義和輸入charstr[80]輸入使用fgets(str,80,stdin),在linux下用gets會(huì)有警告,因gets不安全main函數(shù)參數(shù)使用argc:參數(shù)個(gè)數(shù),命令本身算一個(gè)參數(shù)

servaddr.sin_addr.s_addr=inet_addr(argv[1]);argv[1]代表第一個(gè)參數(shù),如./a.out

其中的“”就是argv[1]SchoolofComputerSicenceUDP實(shí)現(xiàn)一個(gè)“小”文件傳輸文件傳輸和消息發(fā)送原理一樣服務(wù)器端:收到信息,寫(xiě)入文件FILE*fout=fopen("test_rec","wb");n=recvfrom(.......);fwrite(buff,1,n,fout)//把n個(gè)字節(jié)的buff寫(xiě)入到fout中....fclose(fout);//關(guān)閉文件客戶(hù)端:從小文件中讀取數(shù)據(jù)---abc.txt要少于1024字節(jié)FILE*fin=fopen("abc.txt","rb");//打開(kāi)文件abc.txtt=fread(buff,1,1024,fin);//從文件中讀數(shù)據(jù)到buff中sendto(....,buff,t....);//發(fā)送t個(gè)字節(jié)到服務(wù)器fclose(fin)SchoolofComputerSicence傳輸大文件循環(huán)發(fā)送:while((t=fread(buff,1,1024,fin))>0)當(dāng)文件沒(méi)結(jié)束(讀的字節(jié)數(shù)>0)時(shí),循環(huán)發(fā)送循環(huán)接收:當(dāng)客戶(hù)端結(jié)束傳送時(shí),服務(wù)器端應(yīng)結(jié)束接收。以下服務(wù)器端結(jié)束的條件可行嗎?自行驗(yàn)證,若不可行,請(qǐng)?zhí)岢鲛k法while(n>0){n=recvfrom(.......);fwrite(buff,1,n,fout)}........SchoolofComputerSicence思考UDP協(xié)議傳輸文件有何缺點(diǎn)?SchoolofComputerSicence練習(xí):寫(xiě)一個(gè)聊天程序即可以收,又可以發(fā)一個(gè)程序,不分客戶(hù)端和服務(wù)器端,都是一樣的。運(yùn)行在2臺(tái)電腦上,可以聊天,如編譯的程序名為talk:./talk5表示和某個(gè)IP的電腦聊天如果是同一臺(tái)電腦上的兩個(gè)終端,無(wú)法用一個(gè)程序完成(因?yàn)椴荒芏冀壎ㄍ粋€(gè)端口),只需要修改下端口號(hào)重新編譯運(yùn)行即可思路:用多進(jìn)程----一個(gè)進(jìn)程用于發(fā)送,一個(gè)進(jìn)程用于接收多線(xiàn)程也類(lèi)似SchoolofComputerSicence*sendfile#include<sys/sendfile.h>ssize_tsendfile(intout_fd,intin_fd,off_t*offset,size_tcount);將一個(gè)本地文件通過(guò)socket發(fā)送出去通常的做法是:打開(kāi)文件fd和一個(gè)socket,然后循環(huán)地從文件fd中read數(shù)據(jù),并將讀取的數(shù)據(jù)send到socket中。這樣,每次讀寫(xiě)我們都需要兩次系統(tǒng)調(diào)用,并且數(shù)據(jù)會(huì)被從內(nèi)核拷貝到用戶(hù)空間(read),再?gòu)挠脩?hù)空間拷貝到內(nèi)核(send)sendfile就將整個(gè)發(fā)送過(guò)程封裝在一個(gè)系統(tǒng)調(diào)用中,避免了多次系統(tǒng)調(diào)用,避免了數(shù)據(jù)在內(nèi)核空間和用戶(hù)空間之間的大量拷貝(零拷貝機(jī)制)??梢酝ㄟ^(guò)sendfile提高效率SchoolofComputerSicence聊天程序結(jié)構(gòu)初始化同前UDP程序和udps,udpc一樣,但不綁定端口(是為了讓接收和發(fā)送用不同的端口)fork子進(jìn)程:發(fā)送,同前可以有bind,也可以沒(méi)有父進(jìn)程:接收注意:把bind放到父進(jìn)程中來(lái)調(diào)試運(yùn)行udps.c和udpc.c,在可以循環(huán)接收和循環(huán)發(fā)送的基礎(chǔ)上,對(duì)其中某個(gè)程序進(jìn)行修改(添加fork)即可,注意pid=fork()的位置,對(duì)structsockaddr_in變量賦值的代碼父子進(jìn)程可以共用,不用再寫(xiě)一遍intpid;.....pid=fork();if(pid==0){while(1){

input...

sendto....

}}else{bind....while(1){

recvfrom.....printf

}}SchoolofComputerSicenceUDPUserDatagramProtocol,用戶(hù)數(shù)據(jù)包協(xié)議,提供面向事務(wù)的簡(jiǎn)單不可靠信息傳送服務(wù)UDP有不提供數(shù)據(jù)包分組、組裝和不能對(duì)數(shù)據(jù)包進(jìn)行排序的缺點(diǎn),也就是說(shuō),當(dāng)報(bào)文發(fā)送之后,是無(wú)法得知其是否安全完整到達(dá)的;同樣,服務(wù)器也無(wú)法知道客戶(hù)端的狀態(tài)在網(wǎng)絡(luò)質(zhì)量較差的環(huán)境下,UDP協(xié)議數(shù)據(jù)包丟失會(huì)比較嚴(yán)重。但是由于UDP的特性:它不屬于連接型協(xié)議,因而具有資源消耗小,處理速度快的優(yōu)點(diǎn),所以通常音頻、視頻和普通數(shù)據(jù)在傳送時(shí)使用UDP較多SchoolofComputerSicenceTCP通信使用UDP協(xié)議發(fā)送數(shù)據(jù),程序中直接用sendto發(fā)送,沒(méi)有確認(rèn)服務(wù)器已經(jīng)就緒TCP:在通信之前需要建立連接(三次握手)服務(wù)器流程:1.socket:使用SOCKET_STREAM,其他同UDPsocket(AF_INET,SOCKET_STREAM,0)2.bind:同UDP3.listen:監(jiān)聽(tīng),等待客戶(hù)端發(fā)送連接請(qǐng)求4.accept:如果條件允許,接受請(qǐng)求,向客戶(hù)端發(fā)送響應(yīng)5.recv:接受數(shù)據(jù)(和recvfrom類(lèi)似)6.處理收到的信息SchoolofComputerSicenceTCP服務(wù)器的listen函數(shù)函數(shù)原型intlisten(intsockfd,intbacklog);參數(shù)說(shuō)明sockfd:調(diào)用socket返回的文件描述符backlog:accept應(yīng)答之前,允許在進(jìn)入隊(duì)列中等待的連接數(shù)目,出錯(cuò)時(shí)返回-1(此數(shù)目=未完成連接客戶(hù)端數(shù)+已完成連接的客戶(hù)端數(shù)),如果兩個(gè)隊(duì)列都是滿(mǎn)的,tcp就忽略客戶(hù)端的同步SYN信號(hào)(但不發(fā)送RST信號(hào),否則會(huì)導(dǎo)致客戶(hù)端connect出錯(cuò)。忽略,客戶(hù)端超時(shí)會(huì)重發(fā))返回值成功時(shí),返回0失敗時(shí),返回-1說(shuō)明服務(wù)器使用listen后,套接字從CLOSED狀態(tài)變?yōu)長(zhǎng)ISTEN狀態(tài),可以接受連接在使用listen()之前,需要調(diào)用bind()綁定到需要的端口三次握手是內(nèi)核負(fù)責(zé)完成的(由客戶(hù)端發(fā)起)SchoolofComputerSicenceTCP的accept函數(shù)建立套接字連接,從建立的連接隊(duì)列中取一個(gè)處理,默認(rèn)是阻塞型的(如果沒(méi)有已完成三次握手的隊(duì)列,則等待)函數(shù)原型intaccept(intsockfd,structvoid*addr,socklen_t*addrlen);參數(shù)說(shuō)明sockfd:正在監(jiān)聽(tīng)端口的套接字文件描述符(調(diào)用socket()函數(shù)生成的)addr:指向本地?cái)?shù)據(jù)結(jié)構(gòu)sockaddr_in的指針,當(dāng)連接成功時(shí),會(huì)填入連入的遠(yuǎn)程主機(jī)(客戶(hù)端)地址信息addrlen:設(shè)置為sizeof(structsockaddr_in)的變量的地址返回值:成功:返回已連接的socket描述字失敗:返回-1SchoolofComputerSicence服務(wù)器的最大連接數(shù)是否是由listen(intsockfd,intbacklog)中的backlog決定?比如backlog為5,是否表示最多5個(gè)客戶(hù)端連接到服務(wù)器?SchoolofComputerSicenceaccept的兩個(gè)socket文件描述符一個(gè)服務(wù)器通常通常僅僅只創(chuàng)建一個(gè)監(jiān)聽(tīng)socket描述字,它在該服務(wù)器的生命周期內(nèi)一直存在。內(nèi)核為每個(gè)由服務(wù)器進(jìn)程接受的客戶(hù)連接創(chuàng)建了一個(gè)已連接socket描述字,當(dāng)服務(wù)器完成了對(duì)某個(gè)客戶(hù)的服務(wù),相應(yīng)的已連接socket描述字就被關(guān)閉。intmain(){intsockfd1,sockfd2;structsockaddr_inraddr;/*客戶(hù)端地址信息*/

sockfd1=socket(AF_INET,SOCK_STREAM,0); ……s=sizeof(structsockaddr_in);

while(1){

sockfd2=accept(sockfd,(structsockaddr*)&raddr,&s); ……recv(sockfd2….)使用accept返回的socket//處理完成后關(guān)閉sockfd2}

SchoolofComputerSicencerecv函數(shù)功能通過(guò)socket接收數(shù)據(jù)函數(shù)原型ssize_trecv(intsockfd,void*buf,size_tlen,intflags);參數(shù)說(shuō)明sockfd:要讀的連接SOCKET描述符(accept函數(shù)的返回值)buf:要讀的信息的緩沖區(qū)len:緩沖的最大長(zhǎng)度f(wàn)lags:一般設(shè)置為0返回值成功時(shí),返回實(shí)際接收到的數(shù)據(jù)的字節(jié)數(shù)失敗時(shí),返回-1。如果正常關(guān)閉了連接,返回為0和recvfrom類(lèi)似,由于是TCP,已經(jīng)建立了連接信息,所以不必再寫(xiě)客戶(hù)端的IP地址和端口號(hào)SchoolofComputerSicence簡(jiǎn)單的TCP服務(wù)器tcps.c#main(){intlistenfd,connfd;structsockaddr_inservaddr,clientaddr;charbuff[4096];intn;intsinsize;

.......close(connfd);close(listenfd);}listenfd=socket(AF_INET,SOCK_STREAM,0);memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_addr.s_addr=htonl(INADDR_ANY);servaddr.sin_port=8888;bind(listenfd,(structsockaddr*)&servaddr,sizeof(servaddr));listen(listenfd,10);sinsize=sizeof(clientaddr);connfd=accept(listenfd,(structsockaddr*)&clientaddr,&sinsize);n=recv(connfd,buff,4096,0);buff[n]='\0';printf("recvmsgfromclient:%s\n",buff);SchoolofComputerSicenceTCP客戶(hù)端普通流程1.建立socket,同服務(wù)器2.connect和服務(wù)器連接3.send發(fā)送消息(或接受消息)4.關(guān)閉SchoolofComputerSicenceTCP客戶(hù)端connect函數(shù)功能建立套接字連接#include<sys/socket.h>函數(shù)原型intconnect(intsockfd,conststructsockaddr*serv_addr,socklen_taddrlen);參數(shù)說(shuō)明sockfd:調(diào)用socket返回的文件描述符serv_addr:遠(yuǎn)程主機(jī)IP地址和端口addrlen:設(shè)置為sizeof(structsockaddr)返回值成功時(shí),返回0。因?yàn)槭亲枞J?,可能超時(shí),具體時(shí)間由內(nèi)核設(shè)置決定。失敗時(shí),返回負(fù)數(shù),具體的錯(cuò)誤代碼存放在errorno中SchoolofComputerSicenceTCP客戶(hù)端send函數(shù)通過(guò)socket發(fā)送數(shù)據(jù)函數(shù)原型ssize_tsend(intsockfd,constvoid*buf,size_tlen,intflags);參數(shù)說(shuō)明sockfd:發(fā)送數(shù)據(jù)的套接字描述符msg:指向發(fā)送數(shù)據(jù)的指針len:數(shù)據(jù)長(zhǎng)度f(wàn)lags:一般設(shè)置為0返回值成功時(shí),返回實(shí)際發(fā)送的數(shù)據(jù)的字節(jié)數(shù)失敗時(shí),返回-1。返回SOCKET_ERROR表示網(wǎng)絡(luò)斷開(kāi)了。SchoolofComputerSicencesend函數(shù)send先比較要發(fā)送數(shù)據(jù)的長(zhǎng)度nbytes和套接字sockfd的發(fā)送緩沖區(qū)的長(zhǎng)度buf,如果nbytes>buf該函數(shù)返回SOCKET_ERROR系統(tǒng)提供的socket緩沖區(qū)大小為8K,可以設(shè)置為大一些,尤其在傳輸實(shí)時(shí)視頻時(shí)注意:并不是send把套接字的發(fā)送緩沖區(qū)中的數(shù)據(jù)傳到連接的另一端的,而是協(xié)議傳送的,send僅僅是把buf中的數(shù)據(jù)copy到套接字sockfd的發(fā)送緩沖區(qū)的剩余空間里send函數(shù)把buff中的數(shù)據(jù)成功copy到sockfd的改善緩沖區(qū)的剩余空間后它就返回了,但是此時(shí)這些數(shù)據(jù)并不一定馬上被傳到連接的另一端。如果協(xié)議在后續(xù)的傳送過(guò)程中出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤的話(huà),那么下一個(gè)socket函數(shù)就會(huì)返回SOCKET_ERROR。每一個(gè)除send的socket函數(shù)在執(zhí)行的最開(kāi)始總要先等待套接字的發(fā)送緩沖區(qū)中的數(shù)據(jù)被協(xié)議傳遞完畢才能繼續(xù),如果在等待時(shí)出現(xiàn)網(wǎng)絡(luò)錯(cuò)誤那么該socket函數(shù)就返回SOCKET_ERRORSchoolofComputerSicence簡(jiǎn)單的tcp客戶(hù)端intmain(intargc,char**argv){intsockfd,n;structsockaddr_inservaddr;sockfd=socket(AF_INET,SOCK_STREAM,0);memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_port=8888;servaddr.sin_addr.s_addr=inet_addr("");connect(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr));send(sockfd,"hello",5,0);close(sockfd);exit(0);}SchoolofComputerSicence練習(xí)完善TCP服務(wù)器端和客戶(hù)端程序服務(wù)器端和客戶(hù)端都改為循環(huán)模式注意循環(huán)位置思考UDP和TCP的效率,用途,區(qū)別和聯(lián)系

socket(...);

bind(...);

listen(...);

while(1){

accept(...);

{

recv(...);

process(...);

}

close(...);

}SchoolofComputerSicence綜合實(shí)例:UDP實(shí)現(xiàn)收發(fā)消息程序功能:客戶(hù)端可以向服務(wù)器發(fā)送命令如發(fā)送:TIME時(shí),服務(wù)器返回服務(wù)器的時(shí)間給客戶(hù)端,客戶(hù)端顯示如發(fā)送:DATA時(shí),服務(wù)器返回字符串“HELLO”給客戶(hù)端顯示如發(fā)送:END時(shí),服務(wù)器結(jié)束服務(wù)器循環(huán)接收命令客戶(hù)端循環(huán)發(fā)送命令使用UDP實(shí)現(xiàn)SchoolofComputerSicence程序框架-服務(wù)器初始化socket,地址,端口等綁定套接字while(字符串buff!=“END”){緩沖區(qū)buff清零recvfrom接收數(shù)據(jù)如果收到數(shù)據(jù)>0如果數(shù)據(jù)是TIME給字符串賦值為當(dāng)前時(shí)間:sprintf(buff,"%s",....)發(fā)送給客戶(hù)端sendto如果數(shù)據(jù)是DATA給字符串賦值為HELLO發(fā)送給客戶(hù)端關(guān)閉套接字,結(jié)束SchoolofComputerSicence程序框架-客戶(hù)端初始化socket,地址,端口等while(字符串buff!=“END”){提示輸入字符串,并讀入發(fā)送給服務(wù)器sendto清空接受緩沖區(qū)(字符串)接收數(shù)據(jù)recvfrom如果接收的數(shù)據(jù)>0打印收到的數(shù)據(jù)關(guān)閉套接字,退出SchoolofComputerSicence改為T(mén)CP協(xié)議實(shí)現(xiàn)-服務(wù)器端如果用TCP實(shí)現(xiàn)收發(fā),過(guò)程和UDP類(lèi)似:初始化socket,地址,端口等,綁定套接字監(jiān)聽(tīng)本地端口listenwhile(字符串buff!=“END”){接收客戶(hù)端的連接accept(....),得到連接套接字socketConrecv接收數(shù)據(jù)如果收到數(shù)據(jù)>0如果數(shù)據(jù)是TIME給字符串賦值為當(dāng)前時(shí)間:sprintf(buff,"%s",....)發(fā)送給客戶(hù)端sendto關(guān)閉連接套接字socketCon

關(guān)閉套接字,結(jié)束注意:accept在循環(huán)體內(nèi)每來(lái)一個(gè)客戶(hù)端,產(chǎn)生一個(gè)新的socket處理完客戶(hù)連接后,關(guān)閉socket再處理下一個(gè)客戶(hù)連接SchoolofComputerSicence改為T(mén)CP協(xié)議實(shí)現(xiàn)-客戶(hù)端初始化socket,地址,端口等while(字符串buff!=“END”){提示輸入字符串,并讀入發(fā)送給服務(wù)器send清空接受緩沖區(qū)(字符串)接收數(shù)據(jù)recv如果接收的數(shù)據(jù)>0打印收到的數(shù)據(jù)關(guān)閉套接字,退出連接服務(wù)器connect(....)??根據(jù)服務(wù)器的處理情況服務(wù)器如果關(guān)閉,則重連SchoolofComputerSicence擴(kuò)展練習(xí):編寫(xiě)“聊天”程序:即可以接收,也可以發(fā)送如:使用UDP,則程序是對(duì)等的,不分客戶(hù)端服務(wù)器端編寫(xiě)一個(gè)ftp程序能夠傳輸文件和傳遞消息一樣,從文件中讀數(shù)據(jù),然后發(fā)送,直到文件結(jié)束用TCP還是UDP?SchoolofComputerSicence一個(gè)簡(jiǎn)單的Web服務(wù)器Web服務(wù)器原理客戶(hù)端是“瀏覽器”,當(dāng)在瀏覽器輸入網(wǎng)址如":8080/index.html",瀏覽器會(huì)向指定IP的服務(wù)器的8080端口發(fā)送請(qǐng)求,具體請(qǐng)求類(lèi)似于GET/index.htmlHTTP/1.1Host::8848User-Agent:Mozilla/5.0(X11;U;Linuxi686;zh-CN;rv:)Gecko/20060313Fedora/-9Firefox/pango-textAccept:text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5Accept-Language:zh-cn,zh;q=0.5Accept-Encoding:gzip,deflateAccept-Charset:gb2312,utf-8;q=0.7,*;q=0.7Keep-Alive:300Connection:keep-aliveSchoolofComputerSicenceWeb服務(wù)器原理服務(wù)器收到請(qǐng)求,分析請(qǐng)求,根據(jù)客戶(hù)端的要求響應(yīng),即向?yàn)g覽器發(fā)送一些信息,包括:狀態(tài)頭,響應(yīng)頭,實(shí)體等,如HTTP/1.1200OKCache-Control:privateContent-Type:text/html;charset=UTF-8Content-Encoding:gzipServer:GWS/2.1Content-Length:1851Date:Sat,14Oct200611:33:39GMT<html><head>.....瀏覽器收到響應(yīng)信息,根據(jù)信息進(jìn)行解析,將html顯示出來(lái)SchoolofComputerSicenceweb服務(wù)器模擬的簡(jiǎn)化1、忽略瀏覽器的具體請(qǐng)求當(dāng)瀏覽器請(qǐng)求時(shí),給瀏覽器返回一個(gè)指定的html文件,如/var/www/index.html2、響應(yīng)頭簡(jiǎn)化可以只要狀態(tài)和類(lèi)型,如sprintf(buf,"HTTP/1.0200OK\r\n");sprintf(buf,"%sContent-type:%s\r\n\r\n",buf,"text/html");3、單進(jìn)程單線(xiàn)程完成SchoolofComputerSicenceweb服務(wù)器框架總體思路基于TCP協(xié)議的服務(wù)器1.初始化服務(wù)器:綁定端口,監(jiān)聽(tīng)2.無(wú)限等待,并響應(yīng)while(1)接受連接接收數(shù)據(jù)讀index.html文件把響應(yīng)頭+index.html文件發(fā)送給客戶(hù)端關(guān)閉連接套接字SchoolofComputerSicence服務(wù)器模型總體可分為C/S和P2PC/S:客戶(hù)端軟件向服務(wù)器發(fā)出請(qǐng)求,服務(wù)器然后對(duì)客戶(hù)端請(qǐng)求做出響應(yīng),在這種情況下,如果客戶(hù)端越多,此時(shí)服務(wù)器的壓力就越大P2P技術(shù)實(shí)現(xiàn)的每臺(tái)計(jì)算機(jī)既是客戶(hù)端,也是服務(wù)器,他們的功能都是對(duì)等的(BT、電驢、迅雷、QQ、MSN和PPlive等都是基于P2P方式實(shí)現(xiàn)的軟件)用戶(hù)之間傳輸多,網(wǎng)絡(luò)負(fù)擔(dān)將加重主機(jī)之間很難發(fā)現(xiàn)(配備發(fā)現(xiàn)服務(wù)器或索引服務(wù)器)SchoolofComputerSicence服務(wù)器基本框架I/O處理單元等待接受客戶(hù)連接,可以是一個(gè)專(zhuān)門(mén)的接入服務(wù)器,實(shí)現(xiàn)負(fù)載均衡邏輯處理單元進(jìn)程或線(xiàn)程,分析處理數(shù)據(jù),然后發(fā)給IO或客戶(hù)端網(wǎng)絡(luò)存儲(chǔ)單元(可選)如果需要,可以是獨(dú)立的數(shù)據(jù)庫(kù),緩存或文件服務(wù)器請(qǐng)求隊(duì)列(*)各個(gè)邏輯單元的抽象,如IO處理,通知某邏輯處理單元;多個(gè)邏輯處理單元訪(fǎng)問(wèn)存儲(chǔ),需要同步SchoolofComputerSicence簡(jiǎn)單的TCP并發(fā)服務(wù)器模型--多進(jìn)程進(jìn)程緩沖池:預(yù)先開(kāi)一些進(jìn)程來(lái)處理可能到來(lái)的連接//main函數(shù)

s=socket(...);

bind(s,...);

listen(s,...);

//處理客戶(hù)端的連接

for(i=0;i<預(yù)指定進(jìn)程數(shù);i++){

pid[i]=fork(); if(pid[i]==0) 處理連接:handle(s)}close(s)//處理連接函數(shù)voidhandle(ints){

while(1){news=accept(s,.....);.....接收recv(news,....)處理....close(news);}客戶(hù)端1accept()recv()處理數(shù)據(jù)客戶(hù)端2accept()recv()處理數(shù)據(jù)服務(wù)器子進(jìn)程服務(wù)器子進(jìn)程accept()recv()處理數(shù)據(jù)服務(wù)器子進(jìn)程SchoolofComputerSicence另一種并發(fā)模型統(tǒng)一accept當(dāng)客戶(hù)端連接請(qǐng)求到來(lái)時(shí),臨時(shí)fork子進(jìn)程處理將連接請(qǐng)求和業(yè)務(wù)處理分離//main函數(shù)

s=socket(...);

bind(s,...);

listen(s,...);

//處理客戶(hù)端的連接

while(1){

s_c=accept(s,.....); if(fork()==0) 處理連接:handle(s_c)elseclose(s_c);}close(s)//處理函數(shù)voidhandle(ints_c){.....接收recv(s_c,....)處理....close(s);}這種模型很容易改為“多線(xiàn)程”模型創(chuàng)建線(xiàn)程pthread_create(..handle...)

線(xiàn)程函數(shù)SchoolofComputerSicence多線(xiàn)程并發(fā)服務(wù)器-線(xiàn)程池模型和多進(jìn)程一樣,但因?yàn)榫€(xiàn)程是共享資源(socket文件句柄),為防止多個(gè)線(xiàn)程競(jìng)爭(zhēng),必須互斥使用互斥區(qū)只需要保護(hù)accept//main函數(shù)

s=socket(...);

bind(s,...);

listen(s,...);

//處理客戶(hù)端的連接

for(i=0;i<預(yù)指定線(xiàn)程數(shù);i++)

創(chuàng)建線(xiàn)程//線(xiàn)程函數(shù)void*handle(void*arg){

while(1){pthread_mutex_lock(&MU);s_c=accept(s,.....);

pthread_mutex_unlock(&MU);.....接收recv(s_c,....)處理....close(s_c);}SchoolofComputerSicenceTCP并發(fā)服務(wù)器--多線(xiàn)程示例線(xiàn)程比進(jìn)程更節(jié)省資源功能描述客戶(hù)端多線(xiàn)程:向服務(wù)器發(fā)送從標(biāo)準(zhǔn)輸入得到的字符在另一個(gè)線(xiàn)程中將從服務(wù)器端返回的字符顯示到標(biāo)準(zhǔn)輸出服務(wù)器端多線(xiàn)程將客戶(hù)端發(fā)來(lái)的數(shù)據(jù)原樣返回給客戶(hù)端,每一個(gè)客戶(hù)在服務(wù)器上對(duì)應(yīng)一個(gè)線(xiàn)程SchoolofComputerSicenceTCP多線(xiàn)程-客戶(hù)端static intsockfd;//線(xiàn)程:負(fù)責(zé)鍵盤(pán)輸入和發(fā)送void*cRecv(void*arg){intrs=0;charstr[80];while(1){scanf("%s",str);send(sockfd,str,80,0);}}intmain(intargc,char**argv){intn,rs;charstr[80],buf[4096];pthread_ttid;sockfd=socket(AF_INET,SOCK_STREAM,0);memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_port=htons(8888);servaddr.sin_addr.s_addr=inet_addr("");connect(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr));pthread_create(&tid,NULL,cRecv,NULL);while(1){rs=recv(sockfd,buff,4096,0);if(rs>0){buff[rs]='\0';printf("recvmsgfromserver:%s\n",buff);}}}tcpcMulthread.cSchoolofComputerSicenceTCP多線(xiàn)程-服務(wù)器端線(xiàn)程:循環(huán)接收來(lái)自客戶(hù)端的信息并原樣發(fā)回去void*handle(void*arg){intsockClient=*((int*)arg);intn=0;charbuff[4096];while(1){n=recv(sockClient,buff,4096,0);if(n>0){buff[n]='\0';printf("recvmsgfromclient:%s\n",buff);send(sockClient,buff,4096,0);}}}tcpsMulthread.cSchoolofComputerSicenceTCP多線(xiàn)程-服務(wù)器端統(tǒng)一的accept函數(shù),來(lái)了新的客戶(hù)端則開(kāi)啟新線(xiàn)程intmain(){intn,sinsize,listenfd,connfd;structsockaddr_inservaddr,clientaddr;charbuff[4096];pthread_tpid;listenfd=socket(AF_INET,SOCK_STREAM,0);memset(&servaddr,0,sizeof(servaddr));servaddr.......bind(listenfd,(structsockaddr*)&servaddr,sizeof(servaddr));listen(listenfd,50);while(1){connfd=accept(listenfd,(structsockaddr*)&clientaddr,&sinsize);if(connfd>0){pthread_create(&pid,NULL,handle,(void*)&connfd);}}}SchoolofComputerSicence綜合練習(xí)寫(xiě)一個(gè)簡(jiǎn)單的網(wǎng)絡(luò)猜數(shù)游戲客戶(hù)端:運(yùn)行程序,輸入服務(wù)器IP地址,連接服務(wù)器,接收并顯示服務(wù)器的信息:服務(wù)器會(huì)提示讓用戶(hù)輸入一個(gè)數(shù),并根據(jù)用戶(hù)輸入數(shù)的大小提示猜的數(shù)字大了還是小了,直到猜對(duì)為止服務(wù)器:當(dāng)客戶(hù)端連接時(shí),產(chǎn)生一個(gè)隨機(jī)數(shù),并根據(jù)用戶(hù)的輸入發(fā)送適當(dāng)?shù)男畔c(diǎn)問(wèn)題:UDP和TCP你選哪種協(xié)議來(lái)完成?隨機(jī)數(shù)的問(wèn)題:多個(gè)用戶(hù)連接,是用同一個(gè)隨機(jī)數(shù),還是不同的?若不同的,當(dāng)新的用戶(hù)連接時(shí),不要把前面產(chǎn)生的隨機(jī)數(shù)給覆蓋了為增加游戲的趣味性,服務(wù)器應(yīng)保存客戶(hù)的哪些信息?如何保存?SchoolofComputerSicence小結(jié)前面TCP/UDP中使用send/recv系列函數(shù)(I/O函數(shù))來(lái)發(fā)、收信息,因?yàn)閟ocket是文件,因此,可以改為使用write/read函數(shù)像文件一樣進(jìn)行操作,如read(fd,buff,1024)connect(),accept(),send(),recv()等阻塞情況讀/recv:緩沖區(qū)沒(méi)有數(shù)據(jù),線(xiàn)程就一直睡眠直到數(shù)據(jù)來(lái)(數(shù)據(jù)到來(lái)由內(nèi)核通知:數(shù)據(jù)先到內(nèi)核,然后用讀copy到應(yīng)用層來(lái))寫(xiě)/send:socket緩沖區(qū)沒(méi)足夠的空間accept:沒(méi)有連接請(qǐng)求connect:服務(wù)器沒(méi)應(yīng)答之前(至少等待到服務(wù)器的一次往返時(shí)間)阻塞模式套接字簡(jiǎn)單,易于實(shí)現(xiàn),適用于并發(fā)量?。蛻?hù)端數(shù)目少),連續(xù)傳輸大數(shù)據(jù)量的情況下,如FTP;缺點(diǎn)但是當(dāng)同時(shí)處理大量套接字時(shí),采用多線(xiàn)程,系統(tǒng)開(kāi)銷(xiāo)大SchoolofComputerSicence阻塞模式的工作方式請(qǐng)求服務(wù)器客戶(hù)1新線(xiàn)程線(xiàn)程:和客戶(hù)1聯(lián)系recv...send..請(qǐng)求客戶(hù)2因?yàn)樽枞?,線(xiàn)程不能馬上完成;線(xiàn)程在處理完客戶(hù)端請(qǐng)求后結(jié)束新線(xiàn)程......當(dāng)很多個(gè)線(xiàn)程時(shí),系統(tǒng)負(fù)擔(dān)很大(Linux中1個(gè)線(xiàn)程棧默認(rèn)是8M)SchoolofComputerSicence非阻塞模式的工作方式請(qǐng)求服務(wù)器客戶(hù)1處理請(qǐng)求recv...send..請(qǐng)求客戶(hù)2因?yàn)榉亲枞?,沒(méi)收到數(shù)據(jù)不等待,繼續(xù)往下執(zhí)行......客戶(hù)n輪詢(xún)polling:如果把每個(gè)請(qǐng)求處理看成一個(gè)線(xiàn)程的話(huà),這個(gè)線(xiàn)程很快就結(jié)束了SchoolofComputerSicence多路復(fù)用模式如何實(shí)現(xiàn)非阻塞IO模式sockfd=socket(AF_INET,SOCK_STREAM,0);fcntl(sockfd,

F_SETFL,

O_NONBLOCK);

將socket設(shè)置為非阻塞模式真正的輪詢(xún)是不實(shí)際的(why?)輪詢(xún):當(dāng)有事件發(fā)生時(shí)通知內(nèi)核依次檢測(cè)所有連接的socket(包括讀,寫(xiě)),如果某個(gè)socket可以操作了(可以讀了,或可以寫(xiě)了),通知用戶(hù)linux提供select函數(shù)int

select(int

nfds,fd_set

*readfds,fd_set

*writefds,

fd_set

*except

fds,struct

timeval

*timeout)

函數(shù)參數(shù):要讀的socket集合,要寫(xiě)的socket集合,....,超時(shí)時(shí)間調(diào)用select函數(shù)時(shí),進(jìn)程會(huì)一直阻塞直到以下的一種情況發(fā)生.

1

溫馨提示

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

評(píng)論

0/150

提交評(píng)論