• 10socket编程


    这一节主要关注的还是粘包问题,我们利用recv实现一个recv_peek函数,它的目的是偷窥目的,它是利用recv的一个msg_peek参数与read的区别,read读取后

    会擦除缓冲区的内容,而recv结合msg_peek会读取的时候并不擦除缓冲区的内容,起到一个可以偷窥的目的,利用这个特性可以用recv实现一个readline函数

    进而利用readline来给出另一个解决回射服务器的粘包问题:

    客户端:

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.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)
    
    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;
    }
    // recv_peek的实现
    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;
            }
    }
    
    // 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] == '
    ')
                            {
                                    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 sock;
    	if ((sock = 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");
    
    	if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
    		ERR_EXIT("connect");
            //这里假如了一个getsockname函数的使用,获取自己的ip和端口号
    	struct sockaddr_in localaddr;
    	socklen_t addrlen = sizeof(localaddr);  //要初始化
    	if (getsockname(sock, (struct sockaddr*)&localaddr, &addrlen) < 0)
    		ERR_EXIT("getsockname");
    
    	printf("ip=%s port=%d
    ", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port));
    
    
    	char sendbuf[1024] = {0};
    	char recvbuf[1024] = {0};
    	while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
    	{
    		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
    ");
                            break;
                    }
    
    		fputs(recvbuf, stdout);
    		memset(sendbuf, 0, sizeof(sendbuf));
    		memset(recvbuf, 0, sizeof(recvbuf));
    	}
    
    	close(sock);
    	
    	return 0;
    }
                                
    

    服务器端:

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.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)
    
    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] == '
    ')
    			{
    				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];
            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
    ");
    			break;
    		}
    		
                    fputs(recvbuf, stdout);
                    writen(conn, recvbuf, strlen(recvbuf));
            }
    }
    
    int main(void)
    {
    	int listenfd;
    	if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    /*	if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 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 conn;
    
    	pid_t pid;
    	while (1)
    	{
    		if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)
    			ERR_EXIT("accept");
    
    		printf("ip=%s port=%d
    ", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
    
    		pid = fork();
    		if (pid == -1)
    			ERR_EXIT("fork");
    		if (pid == 0)
    		{
    			close(listenfd);
    			do_service(conn);
    			exit(EXIT_SUCCESS);
    		}
    		else
    			close(conn);
    	}
    	
    	return 0;
    }
    

    这里的程序都是和先前的关系密切,有一些知识淡忘了可以回顾。

    下面介绍两个函数:

    gethostname:获取主机名

    gethostbyname:利用主机名获取主机地址。

    #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 main(void)
    {
    	char host[100] = {0};
    	if (gethostname(host, sizeof(host)) < 0)
    		ERR_EXIT("gethostname");
    
    	struct hostent *hp;  //查看一下gethostbyname的返回值
    	if ((hp = gethostbyname(host)) == NULL)
    		ERR_EXIT("gethostbyname");
    
    	int i = 0;  //这里务必要初始化
    	while (hp->h_addr_list[i] != NULL)
    	{
    		printf("%s
    ", inet_ntoa(*(struct in_addr*)hp->h_addr_list[i]));
    		i++;
    	}
    	
    	char ip[16] = {0};
    	getlocalip(ip);
    	printf("localip=%s
    ", ip);
    	return 0;
    }
    

    会有好几个ip都是本机的ip。

    下面我们在写一个获取本机默认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));
    	return 0;
    
    }
    
    int main(void)
    {
    	char ip[16] = {0};
    	getlocalip(ip);
    	printf("localip=%s
    ", ip);
    	return 0;
    }
    

    写的比较简单,不会的随时度娘。。。。。睡觉了。。。。

  • 相关阅读:
    hdu1150&&POJ1325 Machine Schedule---最小点覆盖
    hdu-1068&&POJ1466 Girls and Boys---最大独立集
    hdu-2680 Choose the best route---dijkstra+反向存图或者建立超级源点
    hdu-1317 XYZZY---Floyd判连通+bellman最短路
    hdu-1874 畅通工程续---模板题
    hdu-2112 HDU Today---dijkstra+标号
    hdu-2066 一个人的旅行---模板题
    hdu-3790 最短路径问题---dijkstra两重权值
    hdu-2544 最短路---模板题
    BZOJ3529: [Sdoi2014]数表
  • 原文地址:https://www.cnblogs.com/DamonBlog/p/4461869.html
Copyright © 2020-2023  润新知