美文网首页
实践2 . 树莓派A20 TCP客户端服务端测试程序

实践2 . 树莓派A20 TCP客户端服务端测试程序

作者: wit_yuan | 来源:发表于2017-08-03 10:43 被阅读0次

    1 说明

    本篇是两个程序,分为客户端和服务端程序,作为工作中测试的一个通信程序例程。

    2 客户端

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <string.h>
    
    #define SERVER_ADDR     "192.168.25.15"
    #define SERVER_PORT     4819
    
    #define SOCKET_BUF_LEN 1024
    
    
    #define Tranverse32(X)                 ((((unsigned int)(X) & 0xff000000) >> 24) | \
                                                               (((unsigned int)(X) & 0x00ff0000) >> 8) |\
                                                               (((unsigned int)(X) & 0x0000ff00) << 8) |\
                                                               (((unsigned int)(X) & 0x000000ff) << 24))
    
    
    int main()
    {
        int fd = 0;
        int i = 0;
        struct sockaddr_in serveraddr;
        int socket_opt_val = 0;
        unsigned char buf[SOCKET_BUF_LEN] = {'\0'};
        unsigned char rcv_buf[SOCKET_BUF_LEN] = {0};
        unsigned int  i_value = 0;
        int len = 0;
         
        if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("Cread Sock Error, Return");
    
            return -1;
        }
        printf("[%d]Cread Sock Ok\n", fd);
    
        //设置该套接字接口可以与已经在使用的地址绑定(BF548有多个套接字)
        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, \
            &socket_opt_val, sizeof(socket_opt_val)) < 0) {
            perror("Setsockopt Error, Return");
    
            return -1;
        }
        printf("Setsockopt Ok\n");
    
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_port   = htons(SERVER_PORT);
        serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
    
        if (connect(fd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_in)) < 0) {
            perror("Connect Error, Return");
    
            return -1;
        }
        printf("Connect Ok\n");
    
        buf[0] = 0x06;
        buf[1] = 0x01;
        buf[2] = 0x00;
        buf[3] = 0x00;
        if (send(fd, buf, 4, 0) < 0) {
            perror("Send Error, Close This Channel, Return");
            close(fd);
    
            return -1;
        }
        printf("Send Ok\n");
    
        while (1) {
            memset(buf, 0, SOCKET_BUF_LEN);
            if ((len = recv(fd, rcv_buf, SOCKET_BUF_LEN, 0)) < 0) {
                perror("Recv Error, Break");
    
                break;
            } else {
                rcv_buf[len] = '\0';
    
                if((len % 4 == 0) && (len != 0 ))
                {    
                    for(i = 0 ; i < len ; i = i + 4)
                    {
                    //  printf("0x%0x,0x%0x,0x%0x,0x%0x\n",rcv_buf[i+0],rcv_buf[i+1],rcv_buf[i+2],rcv_buf[i+3]);
                        //i_value = (rcv_buf[i] << 24) | (rcv_buf[i+1] << 16) | ((rcv_buf[i+2] << 8)) | ((rcv_buf[i+3] << 0));
                        i_value = (rcv_buf[i]) | (rcv_buf[i+1] << 8) | ((rcv_buf[i+2] << 16)) | ((rcv_buf[i+3] << 24));
        
                        printf("0x%08x\n",i_value);
    
                        //if(i_value == 0xc612c)
                        //  printf("---recv delay----------\n");
    
                        //if(i_value == 0x2135)
                        //  printf("------first channel------\n");
    
                        i_value = Tranverse32(i_value);
    
                        if((i_value >> (32-6)) == 15)
                        {
                            printf("-----row:%0x,col:%d,val:%d-----\n",(i_value>>21)&0x1f,(i_value>>16)&0x1f,i_value&0xff);
                        } 
                    }
                }
            
            }
        }
    
        printf("Quit\n");
        close(fd);
    
        return 0;
    }
    
    
    

    3 服务端

    3.1 简单的服务端程序

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <string.h>
    
    #define SERVER_ADDR     "192.168.25.15"
    #define SERVER_PORT     4819
    
    #define SOCKET_BUF_LEN 1024
    
    
    int main(int argc,char *argv[])
    {
        int server_fd = 0;
        int i = 0;
        struct sockaddr_in serveraddr;
        int socket_opt_val = 0;
        unsigned char buf[SOCKET_BUF_LEN] = {'\0'};
        unsigned char rcv_buf[SOCKET_BUF_LEN] = {0};
        unsigned int  i_value = 0;
        int len = 0;
         
        if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("Cread Sock Error, Return");
            return -1;
        }
        printf("[%d]Cread Sock Ok\n", server_fd);
    
        //ctrl+c prevent bind already
         int mw_optval = 1;
         setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&mw_optval,sizeof(mw_optval));
    
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_port   = htons(SERVER_PORT);
        serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
    
         if(bind(server_fd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0)   {
            printf("----bind server error----\r\n");
            close(server_fd);
            return -1;
         }
    
        //listen the socket
        int listen_sock = listen(server_fd, 5);  
        if(listen_sock < 0)  {
            printf("---listen server socket error---\r\n");
            close(server_fd);
            return -1;
        }
    
        struct sockaddr_in peer;  
        socklen_t peer_len = sizeof(struct sockaddr_in); 
        
        int accept_fd = accept(server_fd, (struct sockaddr*)&peer, &peer_len);  
        if(accept_fd < 0)  
        {  
            printf("---accept client error:%d----\r\n",accept_fd);  
            close(server_fd);
            return -1;  
        }  
        else  
        {  
            printf("connected with ip: %s  and port: %d\n", inet_ntop(AF_INET,&peer.sin_addr, buf, 1024), ntohs(peer.sin_port));  
        }   
        while(1)  {
            memset(buf, '\0', sizeof(buf)); 
            ssize_t size = read(accept_fd, buf, sizeof(buf) - 1);  
            if(size > 0)  
            {  
                printf("client#: %s\n", buf);  
            }
            else if(size == 0)  
            {  
                  printf("read is done...\n");  
                  break;  
             }  
            else   
            {  
                printf("----read error----\r\n");  
                break;  
            }       
            printf("server please enter: ");  
            fflush(stdout);  
            size = read(0, buf, sizeof(buf) - 1);  
            if(size > 0)  
            {  
                buf[size - 1] = '\0';  
                printf("console:read:%s\r\n",buf);
            }  
            else if(size == 0)  
            {  
                  printf("read is done...\n");  
                  break;  
              }  
              else  
              {  
                  perror("read");  
                  break;  
              }     
              write(accept_fd, buf, strlen(buf));  
         }  
         
        close(server_fd);
            
        return 0;
    }
    

    服务端的测试,可以使用sokit工具来连接,做简单的消息交互。

    sokit工具如下所示:


    sokit工具

    3.2 使用select优化服务端程序

    有这样的需求,服务器只需要接受一个客户端即可,但是,有这么一个情况:在开始的时候,客户端和服务器是连在一起的,但是,途中将网线拔掉了,接着客户端也关闭了。此时再连接网线,再次开启客户端,就会发现一般情况下,会发生连不上服务器的情况。

    这种情况发生的原因主要在,第二次连接的时候,服务器的第一次连接还没有关闭。

    问题点就在两次连接,产生了两个客户端。上边的程序是属于单客户端单服务器的情况。

    所以,我们需要解决的问题点,那就变成了,在第二次连接的时候,服务端只需要关闭第一个客户端即可。

    这里就要谈到在linux应用层上的select函数的使用。

    select函数原型如下:

    int select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
    

    解释:
    select系统调用是用来监测多个文件句柄的状态变化的。错误返回-1。

    最后一个参数,表明的是需要等待的时间:

    struct timeval{        
      long tv_sec;   /*秒 */  
      long tv_usec;  /*微秒 */     
    }  
    

    若timeout=NULL的时候,表示等待无限长时间。可以被一个信号中断。
    若tv_sec=0并且tv_usec=0,表示不等待。加入的描述符集都会被测试的。
    若tv_sec!=0或者tv_usec!=0,表示等待指定的时间。

    fd_set类型变量的每一位表示的是一个描述符。我摘抄一张图:

    fd_set结构描述符

    控制fd_set变量,可以使用如下几个宏:

    #include <sys/select.h>     
    int FD_ZERO(int fd, fd_set *fdset);     
    int FD_CLR(int fd, fd_set *fdset);     
    int FD_SET(int fd, fd_set *fd_set);     
    int FD_ISSET(int fd, fd_set *fdset); 
    

    如何使用这几个宏?

    首先需要将fd_set变量设置为0,也就需要使用FD_ZERO,然后可以设置我们需要关心的描述符,可以使用FD_SET,相对应的就要使用FD_CLR,最后是select函数返回,可以使用FD_ISSET来判断所关心的fd_set变量是否有变化。

    关于这些内容,我们可以直接参考链接: 链接

    到这里,可以直接贴上一部分简单的程序:

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <string.h>
    
    #define SERVER_ADDR     "192.168.25.15"
    #define SERVER_PORT     4819
    
    #define SOCKET_BUF_LEN 1024
    
    
    unsigned char recv_data[SOCKET_BUF_LEN];
    
    typedef struct _CLIENT{    
        int       fd;        
        struct sockaddr_in addr;                              
    } CLIENT;
    
    
    int main(int argc,char *argv[])
    {
        int server_fd = 0;
        int i = 0;
        struct sockaddr_in serveraddr;
        int socket_opt_val = 0;
        unsigned char buf[SOCKET_BUF_LEN] = {'\0'};
        unsigned char rcv_buf[SOCKET_BUF_LEN] = {0};
        unsigned int  i_value = 0,sockfd,bytes_received;
        int len = 0;
         
        if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("Cread Sock Error, Return");
            return -1;
        }
        printf("[%d]Cread Sock Ok\n", server_fd);
    
        //ctrl+c prevent bind already
         int mw_optval = 1;
         setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&mw_optval,sizeof(mw_optval));
    
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_port   = htons(SERVER_PORT);
        serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
    
         if(bind(server_fd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0)   {
            printf("----bind server error----\r\n");
            close(server_fd);
            return -1;
         }
    
        //listen the socket
        int listen_sock = listen(server_fd, 5);  
        if(listen_sock < 0)  {
            printf("---listen server socket error---\r\n");
            close(server_fd);
            return -1;
        }
    
        struct sockaddr_in peer;  
        socklen_t peer_len = sizeof(struct sockaddr_in); 
        int count = 0;
    
    
        fd_set server_fd_set;    
        int max_fd = -1;    
        while(1){
            FD_ZERO(&server_fd_set); 
            FD_SET(server_fd, &server_fd_set);    
            max_fd = server_fd;
            CLIENT client[5];
            int     nready;
    
            for (i = 0; i < 5; i++) {
                client[i].fd = -1;  
            }       
            
    
            while(1){
                nready = select(max_fd+1, &server_fd_set, NULL, NULL, NULL);     
    
                /* select the server fd to check if a new connection is avaliable...*/
                if (FD_ISSET(server_fd, &server_fd_set)) {
                    int accept_fd = accept(server_fd, (struct sockaddr*)&peer, &peer_len);  
    
                    if(accept_fd == -1){
                        printf("---accept error----\r\n");
                        continue;
                    }
                    else  
                    {  
                        printf("connected with ip: %s  and port: %d\n", inet_ntop(AF_INET,&peer.sin_addr, buf, 1024), ntohs(peer.sin_port));  
                    }   
                    count ++;
    
                }
            }
        }
        close(server_fd);   
        return 0;
    }
    

    一些说明

    在这个程序里,测试的时候,可以先运行程序,然后在一切正常的情况下,使用sokit连接服务器,然后拔掉网线,断开客户端tcp连接,再接上网线,连接客户端,然后拔掉网线,断掉客户端tcp连接。然后再连接网线,开启客户端。。。测试结果如下:

    程序测试结果

    所以,我们可以得到一个结论,使用select可以监测到server_fd的变化的。在此基础上,我们在检测到第二个连接的时候,断开第一个连接即可。

    同样,也是直接贴上代码:

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <string.h>
    
    #define SERVER_ADDR     "192.168.1.16"
    #define SERVER_PORT     4819
    
    #define SOCKET_BUF_LEN 1024
    
    unsigned char recv_data[SOCKET_BUF_LEN];
    typedef struct _CLIENT{    
        int       fd;        
        struct sockaddr_in addr;                              
    } CLIENT;
    
    
    int main(int argc,char *argv[])
    {
        int server_fd = 0;
        int i = 0;
        struct sockaddr_in serveraddr;
        int socket_opt_val = 0;
        unsigned char buf[SOCKET_BUF_LEN] = {'\0'};
        unsigned char rcv_buf[SOCKET_BUF_LEN] = {0};
        unsigned int  i_value = 0,sockfd,bytes_received;
        int len = 0;
         
        if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            perror("Cread Sock Error, Return");
            return -1;
        }
        printf("[%d]Cread Sock Ok\n", server_fd);
    
        //ctrl+c prevent bind already
         int mw_optval = 1;
         setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&mw_optval,sizeof(mw_optval));
    
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_port   = htons(SERVER_PORT);
        serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
    
         if(bind(server_fd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0)   {
            printf("----bind server error----\r\n");
            close(server_fd);
            return -1;
         }
    
        //listen the socket
        int listen_sock = listen(server_fd, 5);  
        if(listen_sock < 0)  {
            printf("---listen server socket error---\r\n");
            close(server_fd);
            return -1;
        }
    
        struct sockaddr_in peer;  
        socklen_t peer_len = sizeof(struct sockaddr_in); 
        int count = 0;
    
    
        fd_set server_fd_set;    
        int max_fd = -1;    
        while(1){
    
            CLIENT client;
            int    nready;
    
            client.fd = -1;         
            
            while(1){
                FD_ZERO(&server_fd_set); 
                FD_SET(server_fd, &server_fd_set);    
                max_fd = server_fd;
    
                if(server_fd > max_fd)
                    max_fd = server_fd;
    
                if(client.fd!=-1){
                    FD_SET(client.fd, &server_fd_set);
                    if(client.fd > max_fd)
                        max_fd = client.fd;
                }
                
                nready = select(max_fd+1, &server_fd_set, NULL, NULL, NULL);     
    
                /* select the server fd to check if a new connection is avaliable...*/
                if (FD_ISSET(server_fd, &server_fd_set)) {
                    int accept_fd = accept(server_fd, (struct sockaddr*)&peer, &peer_len);  
    
                    if(accept_fd == -1){
                        printf("---accept error----\r\n");
                        continue;
                    }
                    else  
                    {  
                        printf("port: %d\n", ntohs(peer.sin_port));
                        //printf("connected with ip: %s  and port: %d\n", inet_ntop(AF_INET,&peer.sin_addr, buf, 1024), ntohs(peer.sin_port));  
                    }   
                    count ++;
    
                    /*if client exists,clear the last client fd*/
                    if(client.fd != -1){
                        close(client.fd);
                        client.fd = -1;
                    }
                    /* add a fd to client*/
                    client.fd = accept_fd;
                }
    
                if(client.fd != -1){
                    /* poll the client status*/
                    if (FD_ISSET(client.fd, &server_fd_set))
                    {
                        int byte_num = recv(client.fd,rcv_buf,SOCKET_BUF_LEN,0);    
                        if(byte_num>0){
                            if(byte_num>SOCKET_BUF_LEN){
                                byte_num=SOCKET_BUF_LEN;
                            }
                            rcv_buf[byte_num]='\0';
                            printf("client:%s\n",rcv_buf);
                        }
                        else if(byte_num < 0)
                        {
                            printf("client error.\n");
                        }
                        else
                        {
                            FD_CLR(client.fd,&server_fd_set);
                            close(client.fd);
                            client.fd=-1;
                            printf("client quit\n");
                        }
                    }
                }
                
            }
        }
        close(server_fd);   
        return 0;
    }
    

    实现的效果如下图所示:


    实现结果
    sokit发送的数据

    我做一个基本的简单的讲解:
    1.在第二个while(1)中,先清除所有检测的值,这是因为,在第二个客户端连接的时候,第一个客户端就关闭了,这个时候检测的set集合就改变了,我们能清除第一个客户端在集合中的值,但是并没有办法改变max的值,所以清除所有的要检测的值,然后重新设置值,是一个办法。
    2.select检测到服务端有变化,就知道有客户端连接上了,所以获取到accept_fd,然后才能置位set集合,也就能检测客户端是否接收数据了。
    3.客户端接收数据,处理数据。

    一点注意:

    我前面的测试过程,都是在树莓派A20上进行的,在使用inet_ntop的时候,并没有出错。但是在使用64位主机的时候,最好还是加上头文件:

    #include <arpa/inet.h>
    

    错误信息如下所示:

    core dump

    加上头文件后,发送数据:

    主机端接收数据并打印消息 客户端发送数据

    4 使用QtCreator创建客户端

    相关文章

      网友评论

          本文标题:实践2 . 树莓派A20 TCP客户端服务端测试程序

          本文链接:https://www.haomeiwen.com/subject/sjdifxtx.html