php編寫多程式web伺服器
同樣是使用select來實現,以上是php版本的原始碼,select IO模型複用主要是輪詢方式監聽檔案描述符集,它依然是阻塞方式
具體可以參閱我做的說明。在此不再細說。網路卡是怎麼接收到資料,怎麼把資料寫入對應的sock的,到底怎麼搞的,自己去看看。
在您閱讀時:勿必瞭解下程式,程式組,程式排程,守護程式,前臺程式,IO模型,網路卡讀寫原理,中斷系統,主機位元組序和網路位元組序列,tcp狀態轉移圖,tcp抓包,socket地址型別,一些相關的資料結構如線性表的連結串列儲存,二叉樹插入和刪除,以及平衡二叉樹【紅黑樹】相關概念【以便你深入理解IO模型select和epoll的區別相關知識】
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <libgen.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
int pids[10];
int main(int argc,char *argv[])
{
if(argc<2){
printf("useage:%s ip and port\n",basename(argv[0]));
exit(0);
}
char const *ip = argv[1];
int port = atoi(argv[2]);
struct sockaddr_in address;
bzero(&address,sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
inet_pton(AF_INET,ip,&address.sin_addr);
int sockfd = socket(PF_INET,SOCK_STREAM,0);//建立sock檔案描述符,預設從最小的值開始,最小的值是多少,可以自己去想想程式啟動時預設開啟幾個檔案描述符,程式退出時又關閉了幾個,想想程式啟動前幹什麼了,在此不再多說,建立的sock會關聯自己的接受和傳送緩衝區
assert(sockfd>0);
int ret = bind(sockfd,(struct sockaddr*)&address,sizeof(address));//繫結ip和埠號
assert(ret!=-1);
ret = listen(sockfd,1024);//啟用監聽 並且處於LISTEN狀態【tcp狀態轉移圖自己去看本人前面說過的資料】
//檔案描述集合初始化【或許你在linux玩上一陣子才好理解^_^】
fd_set readfds;
fd_set writefds;
fd_set exceptionfds;
fd_set reads;
fd_set writes;
fd_set exceptions;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&exceptionfds);
FD_ZERO(&reads);
FD_ZERO(&writes);
FD_ZERO(&exceptions);
int i;
int listenfd = sockfd;
FD_SET(sockfd,&readfds);
FD_SET(sockfd,&writefds);
FD_SET(sockfd,&exceptionfds);
struct timeval tv;
tv.tv_sec=0;
tv.tv_usec=0;
pid_t pid = fork();
if(pid<0|pid>0){
exit(0);
}
if(setsid()<0){
exit(0);
}
for(i=0;i<4;i++){
pid_t pid = fork();
if(pid<0){
exit(0);
}
else if(pid>0){
pids[i] = pid;
}else if(pid==0){
//FD_SET(sockfd,&readfds);
//FD_SET(sockfd,&writefds);
//FD_SET(sockfd,&exceptionfds);
int j;
srand((unsigned int)time(NULL));
while(1){
//FD_ZERO(&readfds);
//FD_ZERO(&writefds);
//FD_ZERO(&exceptionfds);
//FD_SET(sockfd,&readfds);
//FD_SET(sockfd,&writefds);
//FD_SET(sockfd,&exceptionfds);
reads = readfds;
writes = writefds;
exceptions = exceptionfds;
// printf("%d child process select\n",getpid());
//不用等,輪詢【檔案集合】時直接返回,tv為null時會阻塞【tv=0時直接返回】
int ret = select(listenfd+1,&reads,&writes,&exceptions,&tv);
// printf("ret=%d\n",ret);
if(ret<0){
printf("某程式%d退出\n",getpid());
exit(0);
}
if(ret==0){
continue;
}
for(j=0;j<listenfd+1;j++){
if(FD_ISSET(j,&reads)){
if(j == sockfd){
struct sockaddr_in client;
socklen_t client_len = sizeof(client);
int connfd = accept(sockfd,(struct sockaddr*)&client,&client_len);
// if(connfd>0){
printf("%d child 當前的客戶端檔案描述符是:%d\n",getpid(),connfd);
FD_SET(connfd,&readfds);
FD_SET(connfd,&writefds);
FD_SET(connfd,&exceptionfds);
if(listenfd<connfd)listenfd = connfd;
printf("listenfd=%d\n",listenfd);
// }
// write(connfd,"hi",3);
}else{
char data[1024];
int len = read(j,data,sizeof(data));
if(len==0){
FD_CLR(j,&readfds);
FD_CLR(j,&writefds);
FD_CLR(j,&exceptionfds);
printf("555");
}
//FD_SET(j,&writefds);
printf("接受內容是:%s of %d\n",data,len);
}
}
if(FD_ISSET(j,&writes)){
char *data = "hello,world";
int len = write(j,data,strlen(data));
printf("某程式[%d]傳送了%d的%s\n",getpid(),len,data);
FD_CLR(j,&readfds);
FD_CLR(j,&writefds);
FD_CLR(j,&exceptionfds);
close(j);
}
if(FD_ISSET(j,&exceptions)){
printf("出錯了\n");
FD_CLR(j,&readfds);
FD_CLR(j,&writefds);
FD_CLR(j,&exceptionfds);
}
}
}
}
}
close(sockfd);
return EXIT_SUCCESS;
}