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 ...
随机推荐
- hdu1421 dp
用dp[i][j]表示放了i件物品,j对时的最小值. dp[i-2][j-1]表示取当前的 dp[i-1][j]表示不取当前的. #include<stdio.h> #include< ...
- python 不定长参数**kwargs
- [转]embedding technic:从SNE到t-SNE再到LargeVis
http://bindog.github.io/blog/2016/06/04/from-sne-to-tsne-to-largevis/#top
- nodeJs学习-05 案例:http/fs/querystring/url
const http = require('http'); const fs = require('fs'); const querystring = require('querystring'); ...
- WPF中的简单水动画
原文 https://stuff.seans.com/2008/08/21/simple-water-animation-in-wpf/ 很多年前(80年代中期),我在一家拥有Silicon Grap ...
- Liunx vi/vim 2
移动光标的方法 H 光标移动到这个屏幕的最上方那一行的第一个字符 M 光标移动到这个屏幕的中央那一行的第一个字符 L 光标移动到这个屏幕的最下方那一行的第一个字符 G 移动到这个档案的最后一行(常用 ...
- ural 1519 Formula 1(插头dp)
1519. Formula 1 @ Timus Online Judge 干了一天啊!!!插头DP入门. 代码如下: #include <cstdio> #include <cstr ...
- @bzoj - 4709@ 柠檬
目录 @desription@ @solution@ @accepted code@ @details@ @desription@ 一共有 N 只贝壳,编号为 1...N,贝壳 i 的大小为 si. ...
- 通过Ajax提交form表单来提交上传文件
Ajax 提交form方式可以将form表单序列化 然后将数据通过data提交至后台,例如: $.ajax({ url : "http://localhost:8080/" ...
- ajax的状态
readyState:ajax对象的状态值,客户端与客户的交互过程 0:未初始化 1:已经调用了open方法 2:已经接收到响应头 3:已经接受了一部分数据(存在相应正文里) 4:已经接受了全部数据 ...