read、write 与recv、send区别 gethostname
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的更多相关文章
- C++socket编程write()、read()简介及与send()、recv()的区别
1. write 函数原型:ssize_t write(int fd, const void*buf,size_t nbytes)write函数将buf中的nbytes字节内容写入文件描述符fd.成功 ...
- recv send 阻塞和非阻塞
http://blog.csdn.net/xiaofei0859/article/details/6037814 int send( SOCKET s, const char FAR *buf, in ...
- 接口处理篇 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/ ...
- Solidity transfer vs send 区别
原文地址: https://ethereum.stackexchange.com/questions/19341/address-send-vs-address-transfer-best-pract ...
- linux下recv 、send阻塞、非阻塞区别和用法
非阻塞IO 和阻塞IO: 在网络编程中对于一个网络句柄会遇到阻塞IO 和非阻塞IO 的概念, 这里对于这两种socket 先做一下说明: 基本概念: 阻塞IO:: socket 的阻塞模式 ...
- UNIX网络编程-recv、send、read、write之间的联系与区别
1.read ----------------------------------------------------------------------- #include <unistd.h ...
- 套接字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 ...
- 一种构造WEB服务器端recv和send接口阻塞现象的方法
send阻塞 socket recv send接口阻塞,会导致服务器端不在响应客户端任何请求,所以一般情况, 会将socket设置为非阻塞状态, 但是有些场景,例如ssl_accept就需要使用阻塞的 ...
- linux内核中send与recv函数详解
Linux send与recv函数详解 1.简介 #include <sys/socket.h> ssize_t recv(int sockfd, void *buff, size_t n ...
随机推荐
- Extended Traffic
题目链接 题意:有n个路口,m条通路,如果经过一条路则会得到(终点 - 起点)^3的权值,求从1点到其他点的最小权值,如果权值小于3或无法到达输出'?'. 题解:因为权值可能为负,所以用SPFA来解题 ...
- Spring Security入门篇——标签sec:authorize的使用
Security框架可以精确控制页面的一个按钮.链接,它在页面上权限的控制实际上是通过它提供的标签来做到的 Security共有三类标签authorize authentication accessc ...
- python 清空文件夹
#!/usr/bin/env python# -*- coding:utf-8 -*-import os def del_file(path): for i in os.listdir(path): ...
- jq 监听返回事件
<script> $(document).ready(function(e) { var counter = 0; if (window.hi ...
- 在Linux/Unix上运行SuperSocket
SuperSocket通过(Mono 2.10或更新版本)来实现跨平台的特性 由于Unix/Linux不同于Windows上的文件路径格式,SuperSocket提供了专用于Unix/Linux系统上 ...
- 推荐几个web前端比较实用的网站
第一次写博客,说实在的有点紧张和兴奋,哈哈哈哈,本人工作了有两年的时间,平时也有做笔记的习惯,但是都做得乱七八糟的,所以就想通过写博客来记录.好了,废话不多说了,先来几个觉得在工作中使用到的,还不错的 ...
- [C#] Parallel.For的线程数
Parallel.For会自动判断同时运行多少个线程,但你也可以进行干预. ParallelOptions可以设置Parallel.For最大的并发线程.缺省的最大线程数是CPU核数.这通常是不够多的 ...
- 【DCN】路由操作
offset */interface in/out access-list/prefix-list <1-16> // 修改路由偏移量 RIP偏移列表 ...
- Python--day47--mysql分页性能相关方案
提高分页性能: 分页的时候,如果是正常的数据全局扫描,分页越大的时候花费的时间越长. 这时候要提高效率的话就不能全局扫描,如下面的例子,扫描索引且从最大或最小页开始扫描.
- PHP+MySQL实现对一段时间内每天数据统计优化操作实例
http://www.jb51.net/article/136685.htm 这篇文章主要介绍了PHP+MySQL实现对一段时间内每天数据统计优化操作,结合具体实例形式分析了php针对mysql查询统 ...