回射服務器的註釋,全過程
#include "unp.h"
static int read_cnt;//剛開始可以置為一個負值(我的理解)
static char *read_ptr;
static char read_buf[MAXLINE];
static ssize_t
my_read(int fd, char *ptr)//每次最多讀取MAXLINE個字符,調用一次,每次只返回一個字符
{
if (read_cnt <= 0) {
again:
if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {//如果讀取成功,返回read_cnt=讀取的字符 if (errno == EINTR)
goto again;
return(-1);
} else if (read_cnt == 0)
return(0);
read_ptr = read_buf;
}
read_cnt--;//每次遞減1,直到<0讀完,才執行上面if的命令。
*ptr = *read_ptr++;//每次讀取一個字符,轉移一個字符
return(1);
}
ssize_t
readline(int fd,void *vptr,size_t maxlen)
{
ssize_t n,rc;
char c,*ptr;
ptr= vptr;
for(n=1;n<maxlen;n++)
{ if((rc=my_read(fd,&c))==1)
{
*ptr++ =c;
if(c==‘\n‘)
break;}
else if(rc == 0)
{*ptr=0;
return(n-1);
}
else return(-1);
}
*ptr = 0;
return(n);
}
void str_echo(int sockfd)
{
ssize_t n;
char line[MAXLINE];
for(;;)
{
if((n=readline(sockfd,line,MAXLINE))==0)
return;
//如果有socket中的有string讀出來然後加入line最大為MAXLINE
write(sockfd,line,n);
//然後寫入sockfd中去,將line中的string
}
}
int main(int argc,char**argv)
{
int listenfd,connfd;
pid_t childpid;
/*
#ifndef __pid_t_defined
typedef __pid_t pid_t;
# define __pid_t_defined
#endif
#define __S32_TYPE int
由此我們終於找到了pid_t的真實定義:實際他就是 int 類型的。
*/
socklen_t clilen;
//typedef int socklen_t;
struct sockaddr_in cliaddr,servaddr;
//兩個struct sockaddr_in cliaddr,servaddr;
listenfd = socket(AF_INET,SOCK_STREAM,0);
//文件標識
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
bind(listenfd,(SA*)&servaddr,sizeof(servaddr));
//綁定socket文件標識符和ip與端口號
listen(listenfd,LISTENQ);
//listen()將一個進程變為服務器,可以接受其他進程的請求,進而成為一個服務器進程
//並且把相應的套接字變為被動的套接字
//參數backlog
//這個參數 涉及到一些網絡的細節。在進程正理一個一個連接請求的時候,可能還存在其它的連接請求。因為TCP連接是一個過程,
//所以可能存在一種半連接的狀態,有時由 於同時嘗試連接的用戶過多,
//使得服務器進程無法快速地完成連接請求。如果這個情況出現了,服務器進程希望內核如何處理呢?
//內核會在自己的進程空間裏維護一 個隊列以跟蹤這些完成的連接但服務器進程還沒有接手處理或正在進行的連接,
//這樣的一個隊列內核不可能讓其任意大,所以必須有一個大小的上限。這個 backlog告訴內核使用這個數值作為上限。
//毫無疑問,服務器進程不能隨便指定一個數值,內核有一個許可的範圍。這個範圍是實現相關的。很難有某種統一,一般這個值會小30以內。
//當調用listen之後,服務器進程就可以調用accept來接受一個外來的請求。
for(;;)
{
clilen = sizeof(cliaddr);
connfd = accept(listenfd,(SA*)&cliaddr,&clilen);
//功能參數描述
//accept()系統調用主要用在基於連接的套接字類型,比如SOCK_STREAM和SOCK_SEQPACKET。
//它提取出所監聽套接字的等待連接隊列中第一個連接請求,創建一個新的套接字,並返回指向該套接字的文件描述符。
//新建立的套接字不在監聽狀態,原來所監聽的套接字也不受該系統調用的影響。
if((childpid = fork())==0)
{
close(listenfd);
//子進程==0進入 if 關閉監聽的進程
str_echo(connfd);
//調用str_echo(connfd)這個函數
exit(0);
}
close(connfd);
//父進程進入這個邏輯直接關閉accept()建立的套接字
}
}
回射服務器的註釋,全過程