创客神器NanoPi
直播中

donatello1996

9年用户 737经验值
擅长:MEMS/传感技术
私信 关注
[经验]

【NanoPi K1 Plus试用体验】多线程&TCP通信

       TCP通信是最常见的传输层网络通信协议,在Linux系统中非常常见,建立TCP通信的基础是传输层以下的网络层,即IP协议,而要建立TCP连接,则需要设置两个设备的IP地址,以及在服务器端设备开启通信端口。端口号分为三类,0~1023之间的为公认端口(Well Known Ports),这些端口由 IANA 分配管理。IANA 把这些端口分配给最重要的一些应用程序,让所有的用户都知道,当一种新的应用程序出现后,IANA必须为它指派一个公认端口;用户可以自由使用的注册端口1024-49151,在大多数情况下,这些应用软件和普通程序一样可以被非特权用户打开;客户端使用的端口号49152~65535.这类端口号仅在客户进程运行时才动态选择,因此又叫做短暂端口号。被保留给客户端进程选择暂时使用的。也可以理解为,客户端启动的时候操作系统随机分配一个端口用来和服务器通信,客户端进程关闭下次打开时,又重新分配一个新的端口以最常见的SSH登录为例,主机(开发板)和从机(电脑)的IP地址必须设置为同一网段,主机服务器端口需设置为22。在本帖中,我个人使用的TCP通信端口号为8086,比较好记。
    要使用TCP通信的相关函数,需要
  1. #include
    #include
    #include
    #include
    #include tinet/in.h>

五个头文件;
要使用多线程相关函数,需要
  1. #include

这一个头文件。为什么要在有TCP通信的程序中使用多线程功能呢?因为常规的、不带中断服务机制的TCP通信程序中,接收线程是阻塞的,如果要让服务器在轮询接收客户端发来信息的同时还能处理其它任务,就需要多开一个线程给其它任务,或者把TCP轮询接收放到一个新开辟的线程中。当然,我们也可以启用TCP中断服务,不过程序实现起来比较复杂,而使用多线程解决TCP接收阻塞问题则较为简单。多线程启用的函数,需要在主线程里面添加:
  1. pthread_create(&id1,NULL,Thread_Show_CurSor,NULL);
其中第二个变量就是多线程函数的名称。
  1. void *Thread_Show_CurSor(void *arg)
  2. {
  3.     while(1)
  4.     {
  5.         LCD_Show_ASCII_64(length*32,locate_over,0xffffffff,0,'_');
  6.         LCD_Effect();
  7.         usleep(100*1000);
  8.         LCD_Show_ASCII_64(length*32,locate_over,0xffffffff,0,' ');
  9.         LCD_Effect();
  10.         usleep(100*1000);
  11.     }
  12. }

    要开启Linux TCP通信,我们需要两个结构体,六个函数:
  1. struct sockaddr_in sockaddr_in_comm,sockaddr_in_settings;

sockaddr_in_comm的用于通信的结构体,sockaddr_in_settings是用于初始化设置的结构体。

  fd_socket=socket(AF_INET,SOCK_STREAM,0);
  1.     if(fd_socket==-1)
        {
                printf("套接字初始化失败!n");
                return -1;
        }
        ret=bind(fd_socket,(struct sockaddr *)&sockaddr_in_settings,addrsize);
        if(ret==-1)
        {
                printf("套接字绑定失败!n");
                return -1;
        }
        ret=listen(fd_socket,5);
        if(ret==-1)
        {
                printf("服务器监听失败!n");
                return -1;
        }
        newsock=accept(fd_socket,(struct sockaddr *)&sockaddr_in_comm,&addrsize);
        if(newsock==-1)
        {
                printf("服务器接听失败!n");
                return -1;
        }

recv(newsock,recvbuf,100,0);
//接收数据
send(newsock,s,len,0);
//发送数据

值得注意的是,(一)刚刚上文提到的TCP通信阻塞轮询的特点,阻塞的函数正是recv(),如果某个线程运行到了此函数,则除非TCP另一端有数据发过来,该线程将会一直卡死在此处。(二)TCP通信中,传输层通信用的数据格式为char*字符串,字符串最后一定会有''结束符,可以用这个方法判定字符串的长度大小,直接用strlen()函数即可。(三)发送端数据链路层中传输的数据远不止传输层识别到的数据,还包括协议头、协议尾等一连串规定的格式,这些格式在传输层到数据链路层中间的几层经过逐层插入,再下发到物理层进行比特流传输,传到接收端的数据链路层再进行逐层剥离,才提取出传输层中的有用数据。

代码写好之后,我们就可以做一个小实验,结合FrameBuffer显示设备体验TCP通信的乐趣。

在开发板端运行程序,打开串口调试助手,输入开发板的IP地址和端口号8086:
62.JPG
在串口调试助手那里写几行东西,注意只能是ASCII码:
63.jpg 64.jpg
屏幕那里也会显示相应的东西,类似Linux的命令行效果:
IMG_20180913_000858R.jpg IMG_20180913_001628R.jpg
为了做得逼真一点,我用多线程实现了个光标闪烁的效果:
3.gif















更多回帖

相关帖子

NanoPi
发帖
登录/注册
×
20
完善资料,
赚取积分