linux高階程式設計day11 筆記 (轉)
回顧:
多程序的問題:資料共享。
多程序的問題: 程序的上下文環境(context)
檔案描述符號是整數以及對應上下文環境
多程序的問題:上下文環境共享
一.SELECT TCP伺服器程式設計模式
1.select函式
int select(
int fds,//建議是監控的檔案描述符號的最大值+1
fd_set *readfds,//讀檔案描述符號集合
//該引數既是輸入,也是輸出
//輸入:被監控的描述符號
//輸出:有資料的描述符號
fd_set *writefds,
fd_set *errfds,
struct timeval*timeout);//指定阻塞時間限制
//為NULL,永久
返回:
>0:發生改變的檔案描述符號個數
=0:時間限制過期
=-1:異常
//select用法#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <sys/select.h>
main()
{
fd_set fds;
int r;
char buf[100];
while(1)
{
FD_ZERO(&fds); //清空 FD_SET(0,&fds); //將描述符0加入 r=select(1,&fds,0,0,0); //
r=read(0,buf,99);
}
} 2.IO能否發出訊號?
非同步IO就是通過訊號工作.
3.應用使用select
4.使用select實現TCP的多客戶連線與處理
看個小例子:
//chatServer.c
//聊天伺服器端#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
main()
{
int
//1.建立socket sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd==-1) printf("1:%m\n"),exit(-1);
printf("socket ok!\n");
//2.繫結地址與埠 dr.sin_family=AF_INET;
dr.sin_port=htons(8866);
inet_aton("192.168.180.92",&dr.sin_addr);
r=bind(sfd,(struct sockaddr*)&dr,sizeof(dr));
if(r==-1) printf("2:%m\n"),close(sfd),exit(-1);
printf("bind ok!\n");
//3.監聽 r=listen(sfd,10);
if(r==-1) printf("3:%m\n"),close(sfd),exit(-1);
printf("listen ok!\n");
//初始化 count=0;
maxfd=0;
FD_ZERO(&fds);
for(i=0;i<100;i++)
{
fdall[i]=-1;
}
while(1)
{
//4.構造監聽的描述符號集合
//4.1.清空 FD_ZERO(&fds);
maxfd=0;
//4.2.加入伺服器描述符號 FD_SET(sfd,&fds);
maxfd=maxfd>=sfd?maxfd:sfd;
//4.3.加入客戶描述符號 for(i=0;i<count;i++)
{
if(fdall[i]!=-1)
{
FD_SET(fdall[i],&fds);
maxfd=maxfd>=fdall[i]?maxfd:fdall[i];
}
}
//5.使用select迴圈控制描述符號集合 r=select(maxfd+1,&fds,0,0,0);
if(r==-1)
{
printf("伺服器崩潰!\n");
break;
}
//6.分兩種情況處理:
//6.1.有客戶連線:伺服器描述符號 if(FD_ISSET(sfd,&fds))
{
fdall[count]=accept(sfd,0,0);
if(fdall[count]==-1)
{
printf("伺服器崩潰!\n");
//釋放所有客戶 break;
}
printf("有客戶連線!\n");
count++;
}
//6.2.有客戶傳送資料:客戶描述符號 for(i=0;i<count;i++)
{
//判定改變描述符號是否存在 if( fdall[i]!=-1 &&
FD_ISSET(fdall[i],&fds))
{
//讀取資料 r=recv(fdall[i],buf,1023,0);
if(r==0){
printf("有客戶退出!\n");
close(fdall[i]);
fdall[i]=-1;
}
if(r==-1){
printf("網路故障!\n");
close(fdall[i]);
fdall[i]=-1;
}
if(r>0)
{
//廣播資料 buf[r]=0;
printf("廣播資料:%s\n",buf);
for(j=0;j<count;j++)
{
if(fdall[j]!=-1)
{
send(fdall[j],buf,r,0);
}
}
}
}
}
}
}5.poll模式
int poll(
struct pollfd *fds,//監控的描述符號
int nfds,//監控的描述符號的個數
int timeout ); //阻塞超時
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <sys/poll.h>
main()
{
struct pollfd fds[1];
int r;
char buf[100];
fds[0].fd=0;
fds[0].events=POLLIN;
while(1)
{
r=poll(fds,1,-1);
if(fds[0].revents & POLLIN)
{
printf("有資料輸入!\n");
r=read(0,buf,99);
}
}
}二.Socket選項設定
1.socket有哪些選項可以設定
ARP
|
IP
|
|-----------------|
UDP TCP
通用選項:
SOL_SOCKET
SO_BROADCAST 廣播
SO_RCVBUF 描述符號的緩衝的大小
SO_SNDBUF 描述符號的緩衝的大小
SO_REUSEADDR 地址反覆繫結
SO_TYPE 描述符號型別SOCK_STREAM SOCK_DGRAM?
ICMP選項
IPPTOTO_ICMP
ICMP_FILTER
IP選項(干預系統生成IP頭)
IPPROTO_IP
......
......
UDP選項
IPPROTO_UDP
......
TCP選項
IPPROTO_TCP
......
setsockopt設定選項
getsockopt獲取選項
案例:
判定一個socket的資料型別AF_INET:SOCK_STREAM SOCK_DGRAM SOCK_RAW
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
main()
{
int fd;
int type;
int len;
len=sizeof(type);
fd=socket(AF_INET,SOCK_DGRAM,0);
getsockopt(fd,SOL_SOCKET,SO_TYPE,&type,&len);
printf("%u:%u\n",SOCK_STREAM,type);
if(type & SOCK_STREAM)
{
printf("流!\n");
}
if(type & SOCK_DGRAM)
{
printf("報文!\n");
}
}
//獲得緩衝大小#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
main()
{
int fd;
int type;
int len;
len=sizeof(type);
fd=socket(AF_INET,SOCK_DGRAM,0);
getsockopt(fd,SOL_SOCKET,SO_RCVBUF,&type,&len); //在這裡把引數SO_TYPE變成SO_RCVBUF即可 printf("緩衝大小:%u\n",type);
}案例:
使用選項進行資料廣播.
cast_A傳送
建立socket
設定廣播選項
傳送資料(廣播方式傳送)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
main()
{
int fd;
int opt=1;
int r;
struct sockaddr_in dr;
//1.選項設定 fd=socket(PF_INET,SOCK_DGRAM,0);
if(fd==-1) printf("1:%m\n"),exit(-1);
r=setsockopt(fd,SOL_SOCKET,SO_BROADCAST,
&opt,sizeof(opt));
if(r==-1) printf("2:%m\n"),exit(-1);
dr.sin_family=AF_INET;
dr.sin_port=htons(9999);
//2.使用廣播IP地址 dr.sin_addr.s_addr=inet_addr("192.168.180.255");
r=sendto(fd,"Hello",5,0,
(struct sockaddr*)&dr,sizeof(dr));
if(fd==-1) printf("3:%m\n");
close(fd);
} case_B接收
建立socket
設定地址可重用選項
繫結地址
接收資料
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
main()
{
int fd;
int opt=1;
char buf[100];
int r;
struct sockaddr_in dr;
fd=socket(PF_INET,SOCK_DGRAM,0);
if(fd==-1) printf("1:%m\n"),exit(-1);
//1.選項 r=setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,
&opt,sizeof(opt));
if(r==-1) printf("2:%m\n"),exit(-1);
dr.sin_family=AF_INET;
dr.sin_port=htons(9999);
//2.廣播地址 dr.sin_addr.s_addr=inet_addr("192.168.180.255");
r=bind(fd,(struct sockaddr*)&dr,sizeof(dr));
if(r==-1) printf("3:%m\n"),exit(-1);
r=recv(fd,buf,100,0);
if(r>0)
{
buf[r]=0;
printf("廣播資料:%s\n",buf);
}
close(fd);
}三.OOB資料(TCP)
優先資料(帶外資料)
send(,MSG_OOB);
recv(,MSG_OOB);
案例:
oob_server.c
recv MSG_OOB
oob_client.c
send MSG_OOB
//oobServer#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <fcntl.h>
int fd,cfd;
void handle(int s)
{
char data[100];
int r;
if(s==SIGURG)
{
r=recv(cfd,data,100,MSG_OOB);
data[r]=0;
printf("$$%s\n",data);
}
}
main()
{
int opt=1;
char buf[100];
int r;
struct sockaddr_in dr;
fd=socket(PF_INET,SOCK_STREAM,0);
if(fd==-1) printf("1:%m\n"),exit(-1);
printf("1\n");
dr.sin_family=AF_INET;
dr.sin_port=htons(10000);
dr.sin_addr.s_addr=inet_addr("192.168.180.92");
r=bind(fd,(struct sockaddr*)&dr,sizeof(dr));
if(r==-1) printf("2:%m\n"),exit(-1);
printf("2\n");
r=listen(fd,10);
if(r==-1) printf("3:%m\n"),exit(-1);
printf("3\n");
signal(SIGURG,handle);
cfd=accept(fd,0,0);
fcntl(cfd,F_SETOWN,getpid());
if(cfd==-1) printf("4:%m\n"),exit(-1);
printf("4\n");
while(1)
{
r=recv(cfd,buf,100,0);
if(r>0)
{
buf[r]=0;
printf("接收資料:%s\n",buf);
}
else
{
break;
}
}
close(cfd);
close(fd);
}
//oobClient#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
main()
{
int fd;
int opt=1;
char buf[100];
int r;
struct sockaddr_in dr;
fd_set fds;
fd=socket(PF_INET,SOCK_STREAM,0);
if(fd==-1) printf("1:%m\n"),exit(-1);
printf("1\n");
dr.sin_family=AF_INET;
dr.sin_port=htons(10000);
dr.sin_addr.s_addr=inet_addr("192.168.180.92");
r=connect(fd,(struct sockaddr*)&dr,sizeof(dr));
if(r==-1) printf("2:%m\n"),exit(-1);
while(1)
{
FD_ZERO(&fds);
FD_SET(fd,&fds);
select(fd+1,0,&fds,0,0);
send(fd,"Hello",5,MSG_OOB);
}
close(fd);
}
OOB總結:
1.OOB資料只能一個字元
2.普通資料使用一般方式接收與傳送,OOB資料使用MSG_OOB接收與傳送
3.一個數據使用MSG_OOB,則最後一個是OOB,其他非OOB資料
4.問題:OOB資料是優先資料。優先體現在什麼地方? 接收OOB資料的時候,會產生一個URG訊號
四.HTTP協議以及應用
1.HTTP協議版本HTTP1.0 HTTP1.1
2.HTTP是應用協議
3.HTTP協議分成:
請求協議
響應協議
4.請求協議的格式:
請求行(請求方法 請求資源 協議版本)
請求體(請求頭:請求值)
空行
資料(querystring:key=value&key=value)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
main()
{
int fd;
struct sockaddr_in dr;
char strreq[1024];
char buf[10*1024];
int r;
//建立socket fd=socket(AF_INET,SOCK_STREAM,0);
//連線伺服器192.168.0.72 dr.sin_family=AF_INET;
dr.sin_port=htons(80);
dr.sin_addr.s_addr=inet_addr("192.168.0.72");
r=connect(fd,(struct sockaddr*)&dr,sizeof(dr));
//構建http請求字串 sprintf(strreq,
"GET /index.php HTTP/1.1\r\n"
"Host: 192.168.0.72:80\r\n"
"User-Agent: Tarena5.0\r\n"
"Accept: text/html,image/png\r\n"
"Accept-Language: zh-cn\r\n"
"Accept-Charset: gb2312,utf-8\r\n"
"Keep-Alive: 300\r\n"
"Connection: keep-alive\r\n"
"\r\n");
//傳送http請求字串 r=send(fd,strreq,strlen(strreq),0);
//等待伺服器響應
//while(1)
//{ r=recv(fd,buf,1024,0);
//if(r<=0) break; printf("========================\n");
printf("%s\n",buf);
printf("========================\n");
//} close(fd);
}
5.響應協議的格式
響應行(協議版本 響應碼 響應碼的文字描述)
響應體(響應頭: 響應值)
空行
資料(普通資料/分塊資料)
響應碼:
1XX 正在處理
2XX 響應成功(200表示完全成功)
3XX 繼續處理
4XX 客戶錯誤
5XX 伺服器錯誤
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <fcntl.h>
#include <string.h>
int fd,cfd;
main()
{
char buf[1024];
int r;
struct sockaddr_in dr;
char strres[1024];
fd=socket(PF_INET,SOCK_STREAM,0);
if(fd==-1) printf("1:%m\n"),exit(-1);
printf("1\n");
dr.sin_family=AF_INET;
dr.sin_port=htons(10000);
dr.sin_addr.s_addr=inet_addr("192.168.180.92");
r=bind(fd,(struct sockaddr*)&dr,sizeof(dr));
if(r==-1) printf("2:%m\n"),exit(-1);
printf("2\n");
r=listen(fd,10);
if(r==-1) printf("3:%m\n"),exit(-1);
printf("3\n");
cfd=accept(fd,0,0);
if(cfd==-1) printf("4:%m\n"),exit(-1);
printf("4\n");
sprintf(strres,
"HTTP/1.1 200 OK\r\n"
"Server: tarena2.0\r\n"
"Content-Type: text/html\r\n"
"Content-Length: 28\r\n"
"Connection: keep-alive\r\n"
"\r\n"
"<font color=red>靚崽!</font>");
while(1)
{
r=recv(cfd,buf,1024,0);
if(r>0)
{
buf[r]=0;
printf("接收資料:%s\n",buf);
send(cfd,strres,strlen(strres),0);
}
else
{
break;
}
}
close(cfd);
close(fd);
}
五.ioctl函式
實現ifconfig工具
總結:
重點:
select
廣播
瞭解:
OOB資料
HTTP協議
應用:
獨立編寫TCP伺服器端的select模式
編寫廣播
能夠請求一個網頁,並且解析響應
作業:
1.把聊天程式使用poll實現
2.使用UDP的廣播,傳送一個檔案
3.隨意挑選網站,把主頁下載並儲存成html檔案