recv相對於read有什麼區別呢?html
其實它跟read函數功能同樣,均可以從套接口緩衝區sockfd中取數據到buf,可是recv僅僅只可以用於套接口IO,並不能用於文件IO以及其它的IO,而read函數能夠用於任何的IO;web
recv函數相比read函數多了一個flags參數,經過這個參數能夠指定接收的行爲,比較有用的兩個選項是:緩存
這個此次要學習的,它能夠接收緩衝區中的數據,可是並不從緩衝區中清除,這是跟read函數有區別的地方,read函數一旦讀取了,就會直接從緩衝區中清除。網絡
readline實現socket
也就是實現按行讀取,讀取直到\n字符,實際上,它也能解決上節中提到的粘包問題,回顧下上節的粘包問題解決方案:函數
包尾加\r\n(ftp)學習
咱們只要解釋\n爲止,表示前面是一個條合法的消息,對於readline的實現,能夠有三種方案:spa
①、最簡單的方案就是一個字符一個字符的讀取,而後作判斷是否有"\n",可是這種效率比較低,由於會屢次掉用read或recv系統函數。code
②、用一個static變量保存接收到的數據進行緩存,在下次時從這個緩存變量中讀取而後估"\n"判斷。可是一旦用到了static變量,這意味着用到的函數是不可重錄函數【關於這個概念,能夠參考博文:http://www.cnblogs.com/webor2006/p/3744002.html】。server
③、偷窺的方法,也就是此次要採用的方案。下面就利用咱們封裝的recv_peek函數實現readline:
server.c
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #define ERR_EXIT(m)\ do\ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0); ssize_t readn(int fd, void *buf, size_t count)//讀取count個字節數,其中size_t是無符號 的整數,ssize_t是有符號的整數 { size_t nleft = count;//剩餘的字節數 printf("nleft = %d\n",nleft); ssize_t nread;//已接收的字節數 char *bufp = (char*)buf; while (nleft > 0) {//因爲不能保證一次讀操做可以返回字節數是多少,因此須要進行循環來接收 if ((nread = read(fd, bufp, nleft)) < 0) { if (errno == EINTR)//被信號中斷了,則繼續執行,由於不是出錯 continue; return -1;//表示讀取失敗了 } else if (nread == 0)//對等方關閉了 return count - nleft;//返回已經讀取的字節數 bufp += nread; nleft -= nread; } return count; } ssize_t writen(int fd, const void *buf, size_t count) { size_t nleft = count; ssize_t nwritten; char *bufp = (char*)buf; while (nleft > 0) { if ((nwritten = write(fd, bufp, nleft)) < 0) { if (errno == EINTR) continue; return -1; } else if (nwritten == 0)//若是是這種狀況,則表示什麼都沒發生,繼續還得執行 continue; bufp += nwritten; nleft -= nwritten; } return count; } ssize_t recv_peek(int sockfd, void *buf, size_t len) { while(1) { int ret = recv(sockfd,buf,len,MSG_PEEK); if(ret == -1 && errno == EINTR) continue; return ret; } } ssize_t readline(int sockfd, void *buf, size_t maxline) { int ret; int nread; char *bufp = buf; int nleft = maxline; while(1) { ret = recv_peek(sockfd,bufp,nleft); if(ret < 0) return ret; else if(ret == 0) return ret; nread = ret; int i; for(i = 0; i<nread; i++) { if(bufp[i] == '\n') { ret = readn(sockfd,bufp,i+1); if(ret != i+1) exit(EXIT_FAILURE); return ret; } } if(nread > nleft) exit(EXIT_FAILURE); nleft -= nread; ret = readn(sockfd,bufp,nread); if(ret != nread) exit(EXIT_FAILURE); bufp += nread; } return -1; } void do_service(int conn) { char recvbuf[1024]; //struct packet recvbuf; int n; while(1) { memset(recvbuf, 0, sizeof(recvbuf)); int ret=readline(conn,recvbuf,1024); if(ret == -1) { ERR_EXIT("readline"); } if(ret == 0) { printf("client close\n"); break; } fputs(recvbuf,stdout); writen(conn,recvbuf,strlen(recvbuf)); } } int main(void) { int listenfd; if((listenfd = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) { ERR_EXIT("socket"); } struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5188); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/ /*inet_aton("127.0.0.1",&servaddr.sin_addr);*/ //地址重用 int on=1; if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < 0) { ERR_EXIT("setsockopt"); } if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0) { ERR_EXIT("bind"); } if(listen(listenfd,SOMAXCONN) < 0) { ERR_EXIT("listen"); } struct sockaddr_in peeraddr; socklen_t peerlen = sizeof(peeraddr); int confd; pid_t pid; while(1) { if((confd = accept(listenfd,(struct sockaddr*)&peeraddr, &peerlen)) < 0) { ERR_EXIT("accept"); } printf("ip = %s, port = %d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port)); pid = fork(); if(pid == -1) { ERR_EXIT("fork"); } if(pid == 0) { close(listenfd); do_service(confd); exit(EXIT_SUCCESS); } else { close(confd); } } return 0; }
client.c
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #define ERR_EXIT(m)\ do\ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0); ssize_t readn(int fd, void *buf, size_t count)//須要將函數的定義也挪過來 { size_t nleft = count; ssize_t nread; char *bufp = (char*)buf; while (nleft > 0) { if ((nread = read(fd, bufp, nleft)) < 0) { if (errno == EINTR) continue; return -1; } else if (nread == 0) return count - nleft; bufp += nread; nleft -= nread; } return count; } ssize_t writen(int fd, const void *buf, size_t count) { size_t nleft = count; ssize_t nwritten; char *bufp = (char*)buf; while (nleft > 0) { if ((nwritten = write(fd, bufp, nleft)) < 0) { if (errno == EINTR) continue; return -1; } else if (nwritten == 0) continue; bufp += nwritten; nleft -= nwritten; } return count; } ssize_t recv_peek(int sockfd, void *buf, size_t len) { while(1) { int ret = recv(sockfd,buf,len,MSG_PEEK); if(ret == -1 && errno == EINTR) continue; return ret; } } ssize_t readline(int sockfd, void *buf, size_t maxline) { int ret; int nread; char *bufp = buf; int nleft = maxline; while(1) { ret = recv_peek(sockfd,bufp,nleft); if(ret < 0) return ret; else if(ret == 0) return ret; nread = ret; int i; for(i = 0; i < nread; i++) { if(bufp[i] == '\n') { ret = readn(sockfd,bufp,i+1); if(ret != i+1) exit(EXIT_FAILURE); return ret; } } if(nread > nleft) exit(EXIT_FAILURE); nleft -= nread; ret = readn(sockfd,bufp,nread); if(ret != nread) exit(EXIT_FAILURE); bufp += nread; } return -1; } int main(void) { int sockfd; if((sockfd = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) { ERR_EXIT("socket"); } struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(5188); servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); /*inet_aton("127.0.0.1",&servaddr.sin_addr);*/ if (connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0) { ERR_EXIT("connect"); } struct sockaddr_in localaddr; socklen_t addrlen = sizeof(localaddr); if(getsockname(sockfd,(struct sockaddr*)&localaddr,&addrlen) < 0) ERR_EXIT("getsockname"); printf("ip = %s, port = %d\n",inet_ntoa(localaddr.sin_addr),ntohs(localaddr.s in_port)); char sendbuf[1024] = {0}; char recvbuf[1024] = {0}; //struct packet sendbuf; //struct packet recvbuf; //memset(&sendbuf,0,sizeof(sendbuf)); //memset(&recvbuf,0,sizeof(recvbuf)); int n; while(fgets(sendbuf,sizeof(sendbuf),stdin) != NULL) { //writen(sockfd,sendbuf,sizeof(sendbuf)); //readn(sockfd,recvbuf,sizeof(recvbuf)); //n =strlen(sendbuf.buf); //sendbuf.len = htonl(n);//網絡字節序 writen(sockfd,sendbuf,strlen(sendbuf)); int ret = readline(sockfd,recvbuf,sizeof(recvbuf)); //int ret = readn(sockfd,&recvbuf.len,4); if(ret == -1) { ERR_EXIT("read"); } else if(ret == 0) { printf("client close\n"); break; } fputs(recvbuf,stdout); memset(sendbuf,0,sizeof(sendbuf)); memset(recvbuf,0,sizeof(recvbuf)); } close(sockfd); return 0; }
Makefile
.PHONY: clean all CC=gcc CFLAGE= -G -Wall BIN=client server getiplist all:$(BIN) %.o:%.c $(CC) $(cflags) -C $< -O $@ clean: rm -f *.o $(BIN)
getsockname:獲取套接口本地的地址
當客戶端成功與服務端鏈接以後,若是想知道客戶端的地址,就能夠經過它來獲取,
getpeername:獲取對等方的地址
因爲它的使用方法跟getsockname同樣,這裏就不說明了,注意:sockfd需是鏈接成功的套接口,另外對於服務端獲取客戶端ip,像這種狀況下也需用這個接口來得到:
gethostname:獲取主機的名稱
gethostbyname:經過主機名來獲取主機上全部的ip地址
#include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define ERR_EXIT(m) \ do \ { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) int getlocalip(char *ip) { char host[100] = {0}; if (gethostname(host, sizeof(host)) < 0) return -1; struct hostent *hp; if ((hp = gethostbyname(host)) == NULL) return -1; strcpy(ip, inet_ntoa(*(struct in_addr*)hp->h_addr_list[0])); return 0; } int main(void) { char host[100] = {0}; if (gethostname(host, sizeof(host)) < 0) ERR_EXIT("gethostname"); struct hostent *hp; if ((hp = gethostbyname(host)) == NULL) ERR_EXIT("gethostbyname"); int i = 0; while (hp->h_addr_list[i] != NULL) { printf("%s\n", inet_ntoa(*(struct in_addr*)hp->h_addr_list[i])); i++; } char ip[16] = {0}; getlocalip(ip); printf("localip=%s\n", ip); return 0; }