UNIX网络编程学习笔记(代码超详细解析)(持续更新)
生活随笔
收集整理的這篇文章主要介紹了
UNIX网络编程学习笔记(代码超详细解析)(持续更新)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
1. 其他函數(shù)準備
1. TCP 回射服務(wù)器程序: str_echo 函數(shù)
#include “unp.h”void str_echo(int sockfd) {ssize_t n;char buf[MAXLINE];again:/*write() 函數(shù)定義:ssize_t write (int fd, const void * buf, size_t count); 函數(shù)說明:write()會把參數(shù)buf所指的內(nèi)存寫入count個字節(jié)到參數(shù)fd所指的文件內(nèi)。 返回值:如果順利write()會返回實際寫入的字節(jié)數(shù)(len)。當有錯誤發(fā)生時則返回-1,錯誤代碼存入errno中。 *//* read()函數(shù)定義:ssize_t read(int fd, void * buf, size_t count);函數(shù)說明:read()會把參數(shù)fd所指的文件傳送count 個字節(jié)到buf 指針所指的內(nèi)存中。返回值:返回值為實際讀取到的字節(jié)數(shù), 如果返回0, 表示已到達文件尾或是無可讀取的數(shù)據(jù)。若參數(shù)count 為0, 則read()不會有作用并返回0。另外,以下情況返回值小于count: */while((n = read(sockfd, buf, MAXLINE)) > 0)Writen(sockfd, buf, n);if(n < 0 && errno == EINTR) // 被中斷后繼續(xù)執(zhí)行goto again;else if(n < 0)err_sys("str_echo: read error"); }2. TCP 回射客戶端程序: str_cli 函數(shù)
#include "unp.h"void str_cli(FILE *fp, int sockfd) {char sendline[MAXLINE], recvline[MAXLINE];while(Fgets(sendline, NAXLINE, fp) != NULL){Writen(sockfd, sendline, strlen(sendline));if(Readline(sockfd, recvline, MALINE) == 0)err_quit("str_cli: server terminated prematurely");Fputs(recvline, stdout);}}顯示客戶IP地址和端口號的時間獲取服務(wù)器程序
#include "unp.h" #include <time.h>#pragma clang diagnostic push #pragma ide diagnostic ignored "EndlessLoop" //這是防止IDE輸出警告的,不用管。 int main(int argc, char **argv) {int listenfd, connfd;// //len是一個 值——結(jié)果 變量socklen_t len;struct sockaddr_in servaddr,cliaddr;//cliaddr存放客戶的協(xié)議地址//緩沖區(qū)存放轉(zhuǎn)換結(jié)果char buff[MAXLINE];//存放時間time_t ticks;//初始化套接字,指定協(xié)議類型(第一個參數(shù)),套接字類型(字節(jié)流,數(shù)據(jù)報,或者原始套接字(極少使用))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(13);//本地協(xié)議地址賦予套接字//參數(shù)一:套接字描述符,用于標識套接字//參數(shù)二:指定特定于協(xié)議的地址結(jié)構(gòu)的指針//參數(shù)三:地址結(jié)構(gòu)的長度/*詳細解釋下為什么要傳如地址結(jié)構(gòu)的長度:由于該結(jié)構(gòu)體的傳遞方向為從進程到內(nèi)核,所以內(nèi)核需要知道有多大的內(nèi)容復(fù)制進來,所以需要一個參數(shù)告知內(nèi)核傳入地址的大小,這種變量叫做“值——結(jié)果”變量*/Bind(listenfd,(SA*) &servaddr,sizeof (servaddr));//使用socket創(chuàng)建的套接字最初為主動套接字,listen將其轉(zhuǎn)換為一個被動套接字,指示內(nèi)核應(yīng)該接受指向該套接字的連接請求Listen(listenfd,LISTENQ);while (1){len =sizeof (cliaddr);//accept()函數(shù)功能是,從處于 established 狀態(tài)的連接隊列頭部取出一個已經(jīng)完成的連接,如果這個隊列沒有已經(jīng)完成的連接,accept()函數(shù)就會阻塞,直到取出隊列中已完成的用戶連接為止。//如果accept成功,那么它的返回值是由內(nèi)核生成的全新的描述符,代表與客戶之間的 已成功建立 的TCP鏈接(已連接套接字描述符)//參數(shù)一:監(jiān)聽套接字描述符//參數(shù)二:客戶進程的協(xié)議地址//參數(shù)三:該地址的大小//監(jiān)聽套接字與已連接套接字的區(qū)別:監(jiān)聽套接字在該服務(wù)的生命周期內(nèi)一直存在,而每個已完成三次握手的TCP鏈接都會分配一個已連接套接字connfd = Accept(listenfd,(SA*) &cliaddr,&len);printf("connection from %s , port %d\n",inet_ntop(AF_INET,&cliaddr.sin_addr,buff,sizeof (buff)),//打印IP地址ntohs(cliaddr.sin_port));//打印端口號//獲取時間ticks = time(NULL);//為什么不使用printf:因為printf的緩沖區(qū)不可控制,對我們來說難以控制snprintf(buff,sizeof (buff),"%.24s\r\n",ctime(&ticks));Write(connfd,buff,strlen(buff));Close(connfd);}}#pragma clang diagnostic pop4.2作業(yè)
#include "unp.h"int main(int argc, char **argv) {int sockfd, n;char recvline[MAXLINE + 1];struct sockaddr_in servaddr;if (argc != 2)err_quit("usage: a.out <IPaddress>");if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)err_sys("socket error");bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(13); /* daytime server */if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)err_quit("inet_pton error for %s", argv[1]);if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)err_sys("connect error");struct sockaddr_in ss;char buff[MAXLINE];socklen_t len;len =sizeof (ss);if (getsockname(sockfd,(SA*) &ss,&len) < 0) printf("錯誤嘍!");else{printf("connection from %s,port %d\n",inet_ntop(AF_INET,&ss.sin_addr,buff,sizeof (buff)),ntohs(ss.sin_port));}while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {recvline[n] = 0; /* null terminate */if (fputs(recvline, stdout) == EOF)err_sys("fputs error");}if (n < 0)err_sys("read error");exit(0); }5.2
#include "unp.h"#pragma clang diagnostic push #pragma ide diagnostic ignored "EndlessLoop" int main(int argc, char **argv) {int listenfd,confd;pid_t childpid;socklen_t clilen;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));Listen(listenfd,LISTENQ);while (1){clilen =sizeof (cliaddr);confd = Accept(listenfd,(SA*) &cliaddr,&clilen);if((childpid = Fork()) == 0)//fork為每個客戶派生一個處理它們的子進程//紫禁城關(guān)閉監(jiān)聽套接字,父進程關(guān)閉已連接套接字,紫禁城接著調(diào)用str_echo處理客戶{Close(listenfd);str_echo(listenfd); exit(0);}Close(confd);}} #pragma clang diagnostic popTCP回射客戶程序
#include "unp.h"#pragma clang diagnostic push #pragma ide diagnostic ignored "EndlessLoop" int main(int argc, char **argv) {int sockfd;struct sockaddr_in servaddr;if (argc !=2 )err_quit("請輸入IP地址。");sockfd = Socket(AF_INET,SOCK_STREAM,0);bzero(&servaddr,sizeof (servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERV_PORT);Inet_pton(AF_INET,argv[1],&servaddr.sin_addr);//將IP地址轉(zhuǎn)換為二進制機器可讀Connect(sockfd,(SA*) &servaddr,sizeof (servaddr));str_cli(stdin,sockfd);//回射函數(shù)exit(0); } #pragma clang diagnostic pop僵死進程:
(1)概念: 進程實體已經(jīng)釋放,但進程對應(yīng)的PCB進程控制塊(進程描述符)還在.(2)產(chǎn)生的條件: 子進程比父進程結(jié)束的早,且父進程沒有調(diào)用 wait() 獲取子進程的退出碼,這時子進程就變?yōu)榻┧肋M程.(3)若子進程比父進程結(jié)束的晚,則在父進程結(jié)束后,子進程的父進程會變成pid為1 的進程最終版本:
#include "unp.h"int main(int argc, char **argv) {int listenfd, connfd;pid_t childpid;socklen_t clilen;struct sockaddr_in cliaddr, servaddr;void sig_chld(int);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));Listen(listenfd, LISTENQ);/*按我現(xiàn)階段的理解:(1)Signal捕獲信號SIGCHLD,并交給sig_chld處理(2)Signal第二個參數(shù)實際上是一個函數(shù)指針,將sig_chld傳入*//** sig_chld()函數(shù)解析:* 使用waitpid函數(shù),并指定WNOHANG參數(shù)(有尚未終止的子進程是不要阻塞)* 為什么不適用wait函數(shù),防止產(chǎn)生僵死進程,因為沒有辦法防止wait在正在運行的子進程尚有未終止阻塞(具體參見《unix網(wǎng)絡(luò)編程》110頁)* 阻塞:此處的阻塞不同于此前使用的同名詞語,這里的阻塞是指阻塞信號或某個信號集,防止他們在阻塞期間提交。* 本節(jié)的目的是示范網(wǎng)絡(luò)編程中可能遇到的情況:* (1)當fork某個進程時,必須捕獲SIGCHLD信號* (2)當捕獲信號時,必須處理被中斷的系統(tǒng)調(diào)用* (3)SIGCHLD的信號處理函數(shù)必須正確編寫,應(yīng)使用waitpid函數(shù)以免留下僵死進程*/Signal(SIGCHLD, sig_chld); /* must call waitpid() */for ( ; ; ){clilen = sizeof(cliaddr);if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {if (errno == EINTR)continue; /* back to for() */elseerr_sys("accept error");}if ( (childpid = Fork()) == 0) { /* child process */Close(listenfd); /* close listening socket */str_echo(connfd); /* process the request */exit(0);}Close(connfd); /* parent closes connected socket */} }select()函數(shù)
#include "unp.h"#pragma clang diagnostic push #pragma ide diagnostic ignored "EndlessLoop"void str_cli(FILE *fp, int sockfd) {//需要檢查的文件描述符個數(shù)int maxfdp1;/* fd_set其實這是一個數(shù)組的宏定義,實際上是一long類型的數(shù)組,* 每一個數(shù)組元素都能與一打開的文件句柄(socket、文件、管道、設(shè)備等)建立聯(lián)系,建立聯(lián)系的工作由程序員完成,* 當調(diào)用select()時,由內(nèi)核根據(jù)IO狀態(tài)修改fd_set的內(nèi)容,* 由此來通知執(zhí)行了select()的進程哪個句柄可讀。*/fd_set rset;char sendline[MAXLINE], recvline[MAXLINE];FD_ZERO(&rset);while (1) {//將fd加入set集合/** FD_CLR(int fd, fd_set *fdset); //將fd從set集合中清除*FD_ISSET(int fd, fd_set *fdset); //檢測fd是否在set集合中,不在則返回0*FD_ZERO(fd_set *fdset); //將set清零使集合中不含任何fd*///fileno()用來取得參數(shù)stream 指定的文件流所使用的文件描述詞FD_SET(fileno(fp), &rset);//將套接字描述符加入集合FD_SET(sockfd, &rset);//為什么要加1呢:我們需要描述符的個數(shù),而不是最大值,而描述符是從0開始的(妙吧!)maxfdp1 = max(fileno(fp), sockfd )+ 1;/*int select(int nfds, fd_set* readset, fd_set* writeset, fe_set* exceptset, struct timeval* timeout);* 參數(shù):nfds 需要檢查的文件描述字個數(shù)readset 用來檢查可讀性的一組文件描述字。writeset 用來檢查可寫性的一組文件描述字。exceptset 用來檢查是否有異常條件出現(xiàn)的文件描述字。(注:錯誤不包括在異常條件之內(nèi))timeout 超時,填NULL為阻塞,填0為非阻塞,其他為一段超時時間返回值:返回fd的總數(shù),錯誤時返回SOCKET_ERROR*/Select(maxfdp1, &rset, NULL, NULL, NULL);//FD_ISSET(int fd, fd_set *fdset); //檢測fd是否在set集合中,不在則返回0if (FD_ISSET(sockfd, &rset)) {if (Readline(sockfd, recvline, MAXLINE) == 0)err_quit("服務(wù)器超時");Fputs(recvline, stdout);}if (FD_ISSET(fileno(fp), &rset)) {if (Fgets(sendline, MAXLINE, fp) == NULL)return;Write(sockfd, sendline, strlen(sendline));}}}#pragma clang diagnostic pop /* include fig01 */ #include "unp.h"#pragma clang diagnostic push #pragma ide diagnostic ignored "EndlessLoop" int main(int argc, char **argv) {int i, maxi, maxfd, listenfd, connfd, sockfd;int nready, client[FD_SETSIZE];ssize_t n;fd_set rset, allset;char buf[MAXLINE];socklen_t clilen;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));Listen(listenfd, LISTENQ);maxfd = listenfd; /* 初始化 */maxi = -1; /* index into client[] array *///將所有描述符初始化為-1for (i = 0; i < FD_SETSIZE; i++)client[i] = -1; /* -1 indicates available entry */FD_ZERO(&allset);FD_SET(listenfd, &allset); /* end fig01 *//* include fig02 */for ( ; ; ) {rset = allset; /* structure assignment *///等待某個事件的發(fā)生nready = Select(maxfd+1, &rset, NULL, NULL, NULL);if (FD_ISSET(listenfd, &rset)) { /* new client connection */clilen = sizeof(cliaddr);connfd = Accept(listenfd, (SA *) &cliaddr, &clilen); #ifdef NOTDEFprintf("new client: %s, port %d\n",Inet_ntop(AF_INET, &cliaddr.sin_addr, 4, NULL),ntohs(cliaddr.sin_port)); #endiffor (i = 0; i < FD_SETSIZE; i++)if (client[i] < 0) {client[i] = connfd; /* 保存描述符 */break;/*保存了就跳出*/}if (i == FD_SETSIZE)err_quit("too many clients");FD_SET(connfd, &allset); /*添加新的描述符來設(shè)置*/if (connfd > maxfd)maxfd = connfd; /* for select */if (i > maxi)maxi = i; /* max index in client[] array */if (--nready <= 0)continue; /* no more readable descriptors */}for (i = 0; i <= maxi; i++) { /* check all clients for data */if ( (sockfd = client[i]) < 0)continue;if (FD_ISSET(sockfd, &rset)) {if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {/*4connection closed by client */Close(sockfd);FD_CLR(sockfd, &allset);client[i] = -1;} elseWriten(sockfd, buf, n);if (--nready <= 0)break; /* no more readable descriptors */}}}} /* end fig02 */ #pragma clang diagnostic poppselect()函數(shù)
函數(shù)select()是用一種超時輪循的方式來查看文件的讀寫錯誤可操作性。在Linux下,還有一個相似的函數(shù)pselect()。
poll()函數(shù)
int poll(struct pollfd *fds, nfds_t nfds, int timeout);功能:
監(jiān)視并等待多個文件描述符的屬性變化
參數(shù):
fds:指向一個結(jié)構(gòu)體數(shù)組的第0個元素的指針,每個數(shù)組元素都是一個struct pollfd結(jié)構(gòu),用于指定測試某個給定的fd的條件
struct pollfd{int fd; //文件描述符short events; //等待的事件short revents; //實際發(fā)生的事件 };總結(jié)
以上是生活随笔為你收集整理的UNIX网络编程学习笔记(代码超详细解析)(持续更新)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python doc转pdf
- 下一篇: 删库造成损失 0.87 亿,微盟程序员被