模拟智能家居项目Part8

来自于两周的粤嵌实习,主要是网络编程和线程控制部分比较重要,贯穿了常用的C语言用法,值得做一个复盘,共有11个部分,本节介绍了Socket套接字的使用和网络编程的理论。

Posted by LJJ on July 2, 2020

模拟智能家居项目

基本原理

  • socket套接字 socket是一个编程接口(网络编程接口) 是一种特殊的文件描述符(write/read) socket支持TCP协议 socket独立于具体的协议的编程接口,这个接口位于TCP四层模型的应用层与传输层之间。

  • 基于TCP套接字编程流程

    任何网络通信都有  通信双方:
    Client 
      
    Server 
      
    "网络地址"
    任何网络应用一方都需要有一个“网络地址”(ip + 端口号)
      
      
    TCP网络应用的数据传输的大概过程:
    “三次握手”
      
      
    发送/接收网络数据
    write 
      
    read 
      
    关闭连接:
    “四路挥手”
      
    TCP网络应用的编程流程
    TCP Server
    1.socket:创建一个套接字
    2.bind:把一个套接字和网络地址 绑定在一起。
    如果你想要让其他人来主动联系或连接你
    你就需要bind一个地址,并且把这个地址
    告诉其他人
      
    3.listen:让套接字进入一个“监听模式”
      
    4.accept:接收客户端的连接
    多次调用accetp就可以与不同的客户
    建立连接
      
    5.write 
      
    read 
      
      
    6.close:"四路挥手"
      
    TCP Client
    1.socket:
    //2.bind:可要可不要
    3.connect: 主动与TCP Server建立连接
    “三次握手”
      
    4.write read 
    5.close 
    
  • socket具体的API函数解析

    3.1头文件
    #include <sys/types.h>         
    #include <sys/socket.h>
    3.2函数的功能
    创建一个套接字文件 
    3.3函数的原型
    int socket(int domain, int type, int protocol);
    3.4函数的参数
    int domain:指定域,协议族。socket 接口不仅仅局限于
    TCP,它还可以用于bluetooth、...
    每一种下面都有自己的许多的协议,
    AF_INET 	IPV4 协议族 
    AF_INET6 	
    AF_BLUETOOTH
    int type: 指定要创建的套接字的类型
    SOCK_STREAM : tcp
      
    int protocol:指定具体的应用层协议
    可以指定为0(不知名的私有应用协议)
      
    3.5函数的返回值:
    成功:返回一个套接字描述符(> 0,文件描述符)
    失败:返回-1
    
  • 函数使用

    4.bind函数
    4.1头文件 
    #include <sys/types.h>         
    #include <sys/socket.h>
    4.2函数的功能 
    把一个网络地址IPV4 绑定到一个 socket上面去
    4.3函数的原型 
    int bind(int sockfd, const struct sockaddr *addr,
    socklen_t addrlen);
    4.4函数的参数 
    int sockfd:					要绑定的地址的套接字描述符
    const struct sockaddr *addr:	通用网络地址结构体的指针
    socklen_t addrlen:				指定第二个实参指向的地址结构体的长度
    4.5函数的返回值 
    成功:返回0 
    失败:返回-1
      
      
    下午
    1.网络地址结构体								
    通用的地址结构体:							
    struct sockaddr 	//linux/socket.h
    {				
    sa_family_t sa_family;	 //指定协议族
    char        sa_data[14];
    }
    以太网协议地址结构体:
    struct sockaddr_in
    {
    sa_family_t sin_family; //指定协议族
    u_int16_t sin_port;	//端口号 
    struct in_addr sin_addr; //ip地址
    char sin_zero[8];//填充8个字节,为了和其他协议族地址结构体大小一样
    }
      
    /*IP地址*/
    struct in_addr
    {
    in_addr_t s_addr;
    }
      
      
    struct sockaddr_in a; 
    //(struct sockaddr *)&a
    a.sin_family = AF_INET;
    a.sin_port = htons(10086);
    a.sin_addr.s_addr = inet_addr("192.168.0.102");
      
    2.ip地址之间的转换函数inet_addr()
    2.1头文件 
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    2.2函数的功能
    把人认识的IP地址转换成计算机认识的IP地址
    2.3函数的原型 
    in_addr_t inet_addr(const char *cp);
    2.4函数的参数 
    const char *cp		//填要转换的IP地址的字符串
    2.5	函数的返回值
    成功: 返回转换后的计算机识别的IP地址
    失败: 返回-1;
      
    3.主机字节序转换成网络字节序转换函数htons()
    3.1头文件 
    #include <arpa/inet.h>
    3.2函数的功能 
    将主机字节序转换成网络字节序
    3.3函数的原型 
    uint16_t htons(uint16_t hostshort);
    3.4函数的参数 
    uint16_t hostshort   //填要转的字节序的数据
    3.4返回值 
    成功:返回转换后的字节序数据
    失败:-1
      
      
    4.网络字节序转换成主机字节序转换函数ntohs()			
      
      
    5.让套接字进入”监听模式“API函数 listen()
    5.1头文件 
    #include <sys/types.h>         
    #include <sys/socket.h>
    5.2函数的功能 
    让套接字进入监听模式
    5.3函数的原型 
    int listen(int sockfd, int backlog);
    5.4函数的参数 
    int sockfd		//要进入监听模式的套接字描述符
    int backlog		//同时能够处理连接请求的数目
    如:5,10
      
    5.5函数的返回值 
    成功:返回0 
    失败:返回-1
      
    6.用于接收客户端连接的API函数accept()
    6.1头文件 
    #include <sys/types.h>         
    #include <sys/socket.h>
    6.2函数的功能 
    等待接收一个来自客户端的TCP connectiont请求
    6.3函数的原型 
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    6.4函数的参数 
    int sockfd				//一个监听的套接字文件描述符
    struct sockaddr *addr	//网络地址结构体的指针,用来保存客服端的地址信息的。
    socklen_t *addrlen		//网络地址结构体长度类型的指针,用来保存网络地址结构体的长度
    6.5函数的返回值 
    成功:返回与该客户端的连接套接字描述符,后续与该客户端的数据交互都是通过 该
    连接套接字描述符
    失败:返回-1 
      
    7.用于TCP client去连接TCP server 的API函数 connect()
    7.1头文件 
    #include <sys/types.h>         
    #include <sys/socket.h>
    7.2函数的功能 
    用于TCP的客户端去连接TCP的服务端
    7.3函数的原型 
    int connect(int sockfd, const struct sockaddr *addr,
    socklen_t addrlen);
    7.4函数的参数 
    int sockfd					//套接字文件描述符
    const struct sockaddr *addr //对方的地址,TCP server的地址 
    socklen_t addrlen			//第二个参数指向的地址结构体的长度
      
    7.5函数的返回值 
    成功返回0
    失败返回-1
    

任务要求

  • 自己写一个客户端 再写一个服务器
  • 能进行简单的数据交互

代码实现

  • 服务端
#include <stdio.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<linux/socket.h>
#include <unistd.h>

int main()
{
	//1.socket:创建一个套接字
	int socketfd0 = socket(AF_INET, SOCK_STREAM, 0);
	if(socketfd0 == -1)
	{
		perror("creat socketfd0 fail\n");
		return -1;
	}
	//2.bind:把一个套接字和网络地址 绑定在一起。
		struct sockaddr_in a; 
		a.sin_family = AF_INET;
		a.sin_port = htons(10086);
		a.sin_addr.s_addr = inet_addr("192.168.0.110");	
		
	int num = bind(socketfd0, (struct sockaddr *)&a,sizeof(struct sockaddr));
	if(num == -1)
	{
		perror("bind address fail\n");
		return -1;
	}
	//3.listen:让套接字进入一个“监听模式”
	num = listen(socketfd0, 5);
	if(num == -1)
	{
		perror("set listen fail\n");
		return -1;
	}

	//4.accept:接收客户端的连接
	struct sockaddr ad;
	socklen_t adl;
	int txfd  = accept(socketfd0, &ad, &adl); //等待客户端连接,如果没有客户端连接
	if(txfd == -1)								//程序会停止在这里等待(即程序阻塞在这里)
	{
		perror("accept fail\n");
	}
			
	//5.write 
	char buf[6] = "hello";
	num = write(txfd,buf,5);
	if(num == -1)
	{
		perror("write txfd fail\n");
	}
	
	  //read 
	
	
	//6.close:"四路挥手"
	close(txfd);
}
  • 客户端

    #include <stdio.h>
    #include <sys/types.h>         
    #include <sys/socket.h>
    #include <linux/socket.h>
    #include <unistd.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
      
    int main()
    {
    	//1.socket:
    	int sockefd = socket(AF_INET, SOCK_STREAM, 0);
    	if(sockefd == -1)
    	{
    		perror("creat socket fail\n");
    		return -1;
    	} 
    	//2.bind:可要可不要
    	//3.connect: 主动与TCP Server建立连接
    				//“三次握手”
    	struct sockaddr_in a; 
    	//(struct sockaddr *)&a
    		a.sin_family = AF_INET;
    		a.sin_port = htons(10520);
    		a.sin_addr.s_addr = inet_addr("112.74.113.206");
    	int num = connect(sockefd, (struct sockaddr *)&a,sizeof(struct sockaddr));
    	if(num == -1)
    	{
      		
    		perror("connect fail\n");
    		return -1;
      		
    	}
      			
    	//4.write 
    	// read 
      
      	
    	char buf[2] ={0,1};//门 开
    	char buf1[2] = {8,8};
      
    	while(1)
    	{
    		num = write(sockefd,buf,2);
    		if(num == -1)
    		{
    			perror("write fail\n");
    			return -1;
    		}
      
    		sleep(1);
      		
    		num = read(sockefd,buf1,2);
    		if(num == -1)
    		{
    			perror("read sockefd fail\n");
    		}
      		
    		printf("buf1[0] is %d buf1[1] is %d\n",buf1[0],buf1[1]);
    		sleep(5);
    	}
      	
      
      	
      			
    	//5.close 
    	close(sockefd);
      
      
    	return 0;
    }