recv相对于read有什么区别呢?

其实它跟read函数功能一样,都可以从套接口缓冲区sockfd中取数据到buf,但是recv仅仅只能够用于套接口IO,并不能用于文件IO以及其它的IO,而read函数可以用于任何的IO;

recv函数相比read函数多了一个flags参数,通过这个参数可以指定接收的行为,比较有用的两个选项是:

这个这次要学习的,它可以接收缓冲区中的数据,但是并不从缓冲区中清除,这是跟read函数有区别的地方,read函数一旦读取了,就会直接从缓冲区中清除。

readline实现

也就是实现按行读取,读取直到\n字符,实际上,它也能解决上节中提到的粘包问题,回顾下上节的粘包问题解决方案:

包尾加\r\n(ftp)

我们只要解释\n为止,表示前面是一个条合法的消息,对于readline的实现,可以有三种方案:

①、最简单的方案就是一个字符一个字符的读取,然后做判断是否有"\n",但是这种效率比较低,因为会多次掉用read或recv系统函数。

②、用一个static变量保存接收到的数据进行缓存,在下次时从这个缓存变量中读取然后估"\n"判断。但是一旦用到了static变量,这意味着用到的函数是不可重录函数【关于这个概念,可以参考博文:http://www.cnblogs.com/webor2006/p/3744002.html】

③、偷窥的方法,也就是这次要采用的方案。下面就利用我们封装的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(); 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 > )
{//由于不能保证一次读操作能够返回字节数是多少,所以需要进行循环来接收
if ((nread = read(fd, bufp, nleft)) < )
{
if (errno == EINTR)//被信号中断了,则继续执行,因为不是出错
continue;
return -;//表示读取失败了
}
else if (nread == )//对等方关闭了
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 > )
{
if ((nwritten = write(fd, bufp, nleft)) < )
{
if (errno == EINTR)
continue;
return -;
}
else if (nwritten == )//如果是这种情况,则表示什么都没发生,继续还得执行
continue; bufp += nwritten;
nleft -= nwritten;
} return count;
} ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
while()
{
int ret = recv(sockfd,buf,len,MSG_PEEK);
if(ret == - && 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()
{
ret = recv_peek(sockfd,bufp,nleft);
if(ret < )
return ret;
else if(ret == )
return ret;
nread = ret;
int i;
for(i = ; i<nread; i++)
{
if(bufp[i] == '\n')
{
ret = readn(sockfd,bufp,i+);
if(ret != i+)
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 -;
} void do_service(int conn)
{
char recvbuf[];
//struct packet recvbuf;
int n;
while()
{
memset(recvbuf, , sizeof(recvbuf));
int ret=readline(conn,recvbuf,);
if(ret == -)
{
ERR_EXIT("readline");
}
if(ret == )
{
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)) < )
{
ERR_EXIT("socket");
}
struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons();
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=;
if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < )
{
ERR_EXIT("setsockopt");
} if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < )
{
ERR_EXIT("bind");
} if(listen(listenfd,SOMAXCONN) < )
{
ERR_EXIT("listen");
} struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(peeraddr);
int confd; pid_t pid;
while()
{
if((confd = accept(listenfd,(struct sockaddr*)&peeraddr, &peerlen)) < )
{
ERR_EXIT("accept");
}
printf("ip = %s, port = %d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
pid = fork();
if(pid == -)
{
ERR_EXIT("fork");
}
if(pid == )
{
close(listenfd);
do_service(confd);
exit(EXIT_SUCCESS);
}
else
{
close(confd);
}
} return ;
}

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(); ssize_t readn(int fd, void *buf, size_t count)//需要将函数的定义也挪过来
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf; while (nleft > )
{
if ((nread = read(fd, bufp, nleft)) < )
{
if (errno == EINTR)
continue;
return -;
}
else if (nread == )
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 > )
{
if ((nwritten = write(fd, bufp, nleft)) < )
{
if (errno == EINTR)
continue;
return -;
}
else if (nwritten == )
continue;
bufp += nwritten;
nleft -= nwritten;
} return count;
} ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
while()
{
int ret = recv(sockfd,buf,len,MSG_PEEK);
if(ret == - && 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()
{
ret = recv_peek(sockfd,bufp,nleft);
if(ret < )
return ret;
else if(ret == )
return ret;
nread = ret;
int i;
for(i = ; i < nread; i++)
{
if(bufp[i] == '\n')
{
ret = readn(sockfd,bufp,i+);
if(ret != i+)
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 -;
} int main(void)
{
int sockfd;
if((sockfd = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < )
{
ERR_EXIT("socket");
}
struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons();
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)) < )
{
ERR_EXIT("connect");
} struct sockaddr_in localaddr;
socklen_t addrlen = sizeof(localaddr);
if(getsockname(sockfd,(struct sockaddr*)&localaddr,&addrlen) < )
ERR_EXIT("getsockname");
printf("ip = %s, port = %d\n",inet_ntoa(localaddr.sin_addr),ntohs(localaddr.s
in_port)); char sendbuf[] = {};
char recvbuf[] = {};
//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 == -)
{
ERR_EXIT("read");
}
else if(ret == )
{
printf("client close\n");
break;
} fputs(recvbuf,stdout);
memset(sendbuf,,sizeof(sendbuf));
memset(recvbuf,,sizeof(recvbuf));
}
close(sockfd);
return ;
}

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() int getlocalip(char *ip)
{
char host[] = {};
if (gethostname(host, sizeof(host)) < )
return -;
struct hostent *hp;
if ((hp = gethostbyname(host)) == NULL)
return -;
strcpy(ip, inet_ntoa(*(struct in_addr*)hp->h_addr_list[]));
return ; } int main(void)
{
char host[] = {};
if (gethostname(host, sizeof(host)) < )
ERR_EXIT("gethostname"); struct hostent *hp;
if ((hp = gethostbyname(host)) == NULL)
ERR_EXIT("gethostbyname"); int i = ;
while (hp->h_addr_list[i] != NULL)
{
printf("%s\n", inet_ntoa(*(struct in_addr*)hp->h_addr_list[i]));
i++;
} char ip[] = {};
getlocalip(ip);
printf("localip=%s\n", ip);
return ;
}

read、write 与recv、send区别 gethostname的更多相关文章

  1. C++socket编程write()、read()简介及与send()、recv()的区别

    1. write 函数原型:ssize_t write(int fd, const void*buf,size_t nbytes)write函数将buf中的nbytes字节内容写入文件描述符fd.成功 ...

  2. recv send 阻塞和非阻塞

    http://blog.csdn.net/xiaofei0859/article/details/6037814 int send( SOCKET s, const char FAR *buf, in ...

  3. 接口处理篇 accept bind connect atan2 htons inet_addr inet_aton inet_ntoa listen ntohl recv send sendto socket

    accept(接受socket连线) 相关函数 socket,bind,listen,connect 表头文件 #include<sys/types.h> #include<sys/ ...

  4. Solidity transfer vs send 区别

    原文地址: https://ethereum.stackexchange.com/questions/19341/address-send-vs-address-transfer-best-pract ...

  5. linux下recv 、send阻塞、非阻塞区别和用法

    非阻塞IO 和阻塞IO: 在网络编程中对于一个网络句柄会遇到阻塞IO 和非阻塞IO 的概念, 这里对于这两种socket 先做一下说明:       基本概念: 阻塞IO:: socket 的阻塞模式 ...

  6. UNIX网络编程-recv、send、read、write之间的联系与区别

    1.read ----------------------------------------------------------------------- #include <unistd.h ...

  7. 套接字I/O函数write/read writev/readv send/recv sendto/recvfrom sendmsg/recvmsg

    函数原型 read/write系原型 #include <unistd.h> ssize_t read(int fd, void *buf, size_t count); #include ...

  8. 一种构造WEB服务器端recv和send接口阻塞现象的方法

    send阻塞 socket recv send接口阻塞,会导致服务器端不在响应客户端任何请求,所以一般情况, 会将socket设置为非阻塞状态, 但是有些场景,例如ssl_accept就需要使用阻塞的 ...

  9. linux内核中send与recv函数详解

    Linux send与recv函数详解 1.简介 #include <sys/socket.h> ssize_t recv(int sockfd, void *buff, size_t n ...

随机推荐

  1. 干货 | 解读MySQL 8.0新特性:Skip Scan Range

    MySQL从8.0.13版本开始支持一种新的range scan方式,称为Loose Skip Scan.该特性由Facebook贡献.我们知道在之前的版本中,如果要使用到索引进行扫描,条件必须满足索 ...

  2. Uva 10334

    UVa 10334 这道题几乎和UVa 495是一样的. #include<iostream> #include<cstdio> #define mod 1000000 usi ...

  3. iOS打包上传ipa文件时,报错<ERROR ITMS-90096: "Your binary is not optimized for iPhone 5 - New iPhone apps......>的解决方案

    很长一段时间习惯了用企业级证书发布,最近的新项目使用Xcode 9.1发布到AppStore时遇到了一个小问题(emm..其实问题跟Xcode版本没关系,我也不知道为什么要声明这个233),如下: E ...

  4. Python基础:03序列:字符串、列表和元组

    一:序列 1:连接操作符(+) 这个操作符允许把一个序列和另一个相同类型的序列做连接,生成新的序列.语法如下:sequence1 + sequence2 该表达式的结果是一个包含sequence1和s ...

  5. behavior planning——13. implement a cost function in C++

    In the previous quizzes, you designed a cost function to choose a lane when trying to reach a goal i ...

  6. 使用php函数ini_set()重新设置某个配置的设置值

    使用PHP的ini_set()函数 ini_set (PHP 4, PHP 5, PHP 7) ini_set — 为一个配置选项设置值 说明 string ini_set ( string $var ...

  7. 详解ThinkPHP支持的URL模式有四种普通模式、PATHINFO、REWRITE和兼容模式

    URL模式     URL_MODEL设置 普通模式    0 PATHINFO模式     1 REWRITE模式     2 兼容模式     3 如果你整个应用下面的模块都是采用统一的URL模式 ...

  8. js读取cookie 根据cookie名称获取值、赋值

    借鉴:原作者https://blog.csdn.net/zouxuhang/article/details/80548417   //方法1   //存在问题:如果cookie中存在 aaaname= ...

  9. html5在微信中不允许放大缩小页面

    在头部添加 <meta name="viewport" content="width=device-width, initial-scale=1, maximum- ...

  10. HDU 5971"Wrestling Match"(二分图染色)

    传送门 •题意 给出 n 个人,m 场比赛: 这 m 场比赛,每一场比赛中的对决的两人,一个属于 "good player" 另一个属于 "bad player" ...