recv

int recv(SOCKET s, char FAR, int len, int flags)
recv只能用于套接口io,不能用于文件io,而read函数则都可以。
recv多了个flags选项,通过这个选项能够执行接收行为,但是一般都填0
主要两个参数为
- MSG_OOB 接收通过紧急指针发送的数据
- MSG_PEEK 接收缓冲区的数据,并不清楚缓冲区的数据,而read函数读取之后会清楚缓冲区数据。
封装一个recv_peek
ssize_t recv_peek(int socked, void *buf, size_t len){while(1){int ret = recv(sockfd, buf, len, MSG_PEEK);if(ret == -1&&errno == EINTR){continue;//此时是中断的情况} else if(ret < 4){printf("client close\n");break;}}}
使用上面封装的recv_peek函数来实现readline功能
readline实现按行读取,遇到换行符\n即停止读取。
readline也能解决粘包问题。
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 -= nreadret = readn(sockfd, bufp, nread);//这里不是很懂,为什么要在使用一边readn呢?为什么不直接使用上面的bufp呢?if(ret != nread){exit(EXIT_FAILURE);}bufp += nread;}return -1;}
使用readline解决粘包
修改服务器端
void do_service(int conn){char recvbuf[1024];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));}}
修改客户端
#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <netinet/in.h>#include <arpa/inet.h>#include <string.h>struct packet{int len;char buf[1024];}ssize_t readn(int fd, void *buf, size_t count){//size_t为无符号整数,ssize_t为有符号的整数。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;//这里传输的是整个函数已经接收了多少个。} else {bufp += nread;//更新nleft -= nread;}}return count;}ssize_t writen(int fd, void *buf, size_t count){//size_t为无符号整数,ssize_t为有符号的整数。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 (nread == 0){continue;//如果等于零,对于write操作等于无事发生} else {bufp += nwritten;//更新nleft -= nwritten;}}return count;}int main(){int sock;if(sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) < 0)//其实前两个已经表示为是tcp协议了,第三个参数可以填0{ERR_EXIT("socket");//报错误的宏}struct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));//全部填充0servaddr.sin_family = AF_INET;servaddr.sin_prot = htons(5188);servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");if(connect(sock, (struct sockaddr*)servaddr, sizeof(servaddr)) < 0){ERR_EXIT("connect");}char recvbuf[1024] = {0};char sendbuf[1024] = {0};memset(sendbuf, 0, sizeof(sendbuf));memset(recvbuf, 0, sizeof(recvbuf));while(fgets(sendbuf, sizeof(sendbuf), stdin)!= NULL){//此处使用结构体里面的成员n = strlen(sendbuf.buf);sendbuf.len = htonl(n);//此处多此一举是为了统一字节序转换为网络字节序,此处是包体的长度writen(sock, sendbuf, strlen(sendbuf));//包头有四个字节int ret = readline(sock, recvbuf, sizeof(recvbuf));if{ret == -1 }{ERR_EXIT("readline");} else if(ret == 0){printf("client_close\n");break;}fputs(recvbuf, stdout);memset(sendbuf, 0, sizeof(sendbuf));memset(recvbuf, 0, sizeof(recvbuf));}close(sock);return 0;}
getsockname
可以在客户端上面使用,获取自己的地址和端口号
sturct sockaddr_in localaddr;if(getsockname(sock, (struct sockaddr*)&localaddr, &addrlen) < 0){ERROR_EXIT("connect");}printf("ip = %s port = %d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port));//inet_ntoa网络地址转换为点分十进制地址。
getpeername
通过一个已连接的套接口,返回对方的地址和端口号
getpeername(sock, (struct sockaddr*)&peeraddr, &addrlen)
gethostname
获取主机名
char host[100] = {0};if(gethostname(host, sizeof(host))<0){ERR_EXIT("gethostname");}
gethostbyname
通过主机名,获取主机上所有的ip地址
上面的 h_addr_list为主机ip列表
gethostbyname的输入参数可以是ip地址和域名
#include <netdb.h>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])); //in_addr是网络地址,转换为点分十进制地址i++;}
gethostaddr
根据本机的地址结构,获取所有的地址保存在结构体当中。
