网络LED矩阵显示器

前言

主要是Linux网络程序和多线程程序的编写,实现一个网络访问的LED矩阵显示器。客户端采用telnet进行连接。

socket的编程主要和计网实验课上的差不多,计网实验socket编程传送门

字符串的显示

在上次显示一个字符的基础上进行一个字符串的显示。字符的显示

对于字符串的显示可以直接写入一个字符串,在内核中采用内核队列进行实现。或者每次只能写入一个字符,在应用程序进行一个循环写入。由于上次没有实现内核队列的字符串显示,这里就采用了应用程序循环进行实现。

在上次的基础上,编写一个Display函数,传入值为一个字符串,每次只要将一个字符写入之后,延时500ms之后,不断写入即可。

 1 //Display a string on the MAX7219
 2 int Display(char str[])
 3 {
 4     int fd;
 5     int ret;
 6
 7     fd = open("/dev/MAX7219", O_WRONLY);
 8     if (fd < 0)
 9     {
10         fprintf(stderr, "Fail to open /dev/MAX7219!\n");
11         return -1;
12     }
13     int i=0;
14     while(str[i])
15     {
16         if((ret = write(fd, &str[i], 1))<0)
17         {
18             fprintf(stderr, "Fail to write /dev/MAX7219! %d\n", ret);
19             return -1;
20         }
21         ++i;
22         Delay_xms(500);
23     }
24     fprintf(stdout, "Write /dev/MAX7219 successfully! %d\n", ret);
25     close(fd);
26     return 0;
27 }

多线程Socket程序编写

服务端socket的创建

Socket程序的编写和计网的实验原理基本相同。对于实现多个客户端的连接可以采用IO复用的方式(Select)或者采用Linux下的Pthread库的多线程进行编写,每个线程对应一个客户端。这里采用的服务端多线程,主程序主要负责建立客户端后,阻塞的Accept客户端连接,当Accept一个客户端的一个连接之后就创建一个线程处理该连接。对于多线程编写,需要注意处理好多线程中的数据冲突问题。

首先在主程序建立服务端Socket连接,其过程如下:

  • 调用socket函数建立一个socket,其中传入的参数AF_INET参数表示其采用TCP协议,并且调用Linux系统提供的fcntl函数设置这个socket为非阻塞。
  • 然后初始化服务的地址,保护监听的IP(这里设置的监听服务器端所有的网卡),设置相应的端口以及协议类型(TCP),调用bind函数将其和前面的建立的socket绑定。
  • 设置该socket开始监听,这主要对应TCP连接上面的第一次握手,将TCP第一次握手成功的放在队列(SYN队列)中,其中传入的参数BACKLOG为Socket中队列的长度。
  • 然后调用Accept函数进行接收客户端的请求,其主要是从TCP上次握手成功的Accept队列中将客户端信息去除进行处理。
 1 int listenfd;
 2 struct sockaddr_in  servaddr;
 3 //create a socket
 4 if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
 5 {
 6     fprintf(stderr, "Create socket error: %s(errno: %d)\n", strerror(errno), errno);
 7     exit(0);
 8 }
 9 else
10     fprintf(stdout, "Create a socket successfully\n");
11 //set the server address
12 memset(&servaddr, 0, sizeof(servaddr));  //initialize the server address
13 servaddr.sin_family = AF_INET;           //AF_INET means using TCP protocol
14    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);    //any in address(there may more thaonenetwork card in the server)
15 servaddr.sin_port = htons(PORT);            //set the port
16
17 //bind the server address with the socket
18 if(bind(listenfd, (struct sockaddr*)(&servaddr), sizeof(servaddr)) == -1)
19 {
20      fprintf(stderr, "Bind socket error: %s(errno: %d)\n", strerror(errno), errno);
21      exit(0);
22 }
23 else
24 fprintf(stdout, "Bind socket successfully\n");
25
26 //listen
27 if(listen(listenfd, BACKLOG) == -1)
28 {
29     fprintf(stderr, "Listen socket error: %s(errno: %d)\n", strerror(errno), errno);
30     exit(0);
31 }
32 else
33     fprintf(stdout, "Listen socket successfully\n");

客户端对应线程的创建

服务端调用Accept函数进行接收一个客户端的信息,如果存在连接上的客户端,则为其分配一个线程位置,然后调用pthread_create创建一个线程,并且将该客户端的信息参数传入线程。这里采用一个结构体的方式记录了客户端的相关信息,包括客户端IP,端口号,编号,是否连接等等。

这里为了防止服务器的无限的创建线程,设置了最大同时在线的客户端数目(MAXCONN),如果新连接上的客户端发现已经操作上线,那么该主线程就会睡眠,其等待在了一个客户端断开连接的信号量上面,如果有客户端断开连接,则主线程被唤醒,接受该客户端的连接。

需要注意的是这里涉及到很多的多线程的操作,很有可能发生数据冲突,需要很好使用pthread中的互斥锁防止发生数据冲突。

 1 while(1)
 2 {
 3     pthread_mutex_lock(&activeConnMutex);
 4     if(activeConn >= MAXCONN)
 5          pthread_cond_wait(&connDis, &activeConnMutex);  //wait on a signal
 6     pthread_mutex_unlock(&activeConnMutex);
 7
 8     //find an empty postion for a new connnetion
 9     int i=0;
10     while(i<MAXCONN)
11     {
12         pthread_mutex_lock(&clientsMutex[i]);
13         if(!clients[i].isConn)
14         {
15             pthread_mutex_unlock(&clientsMutex[i]);
16             break;
17         }
18         pthread_mutex_unlock(&clientsMutex[i]);
19         ++i;
20     }
21
22     //accept
23     struct sockaddr_in addr;
24     int clientfd;
25     int sin_size = sizeof(struct sockaddr_in);
26     if((clientfd = accept(listenfd, (struct sockaddr*)(&addr), &sin_size)) == -1)
27     {
28         sleep(1);
29         continue;
30     }
31     else
32         fprintf(stdout, "Accept socket successfully\n");
33
34     pthread_mutex_lock(&clientsMutex[i]);
35     clients[i].clientfd = clientfd;
36     clients[i].addr = addr;
37     clients[i].isConn = 1;
38     clients[i].index = i;
39     pthread_mutex_unlock(&clientsMutex[i]);
40
41     //create a thread for a client
42     pthread_create(&threadID[i], NULL, (void *)clientManager, &clients[i]);
43
44 }    //end-while  

客户端服务线程

在服务器创建了一个线程之后就会调用clientManager这个函数对于该连接进行处理。对于该线程,其主要是阻塞着等待在recv函数上面,接受客户端发送来的字符串,然后调用上面的Display函数将该字符串显示在MAX7219上面。

这里采用了一个Write的互斥锁,将其加在了Display函数前后,这样就保证了多个客户端连接的时候,只有在等一个客户端的字符串显示完成之后,另外一个客户端的字符串才会被显示,不会造成多个客户端的字符串交错显示的情况。

 1 void clientManager(void* argv)
 2 {
 3     ClientInfo *client = (ClientInfo *)(argv);
 4
 5     BYTE buff[BUFFSIZE];
 6     int recvbytes;
 7
 8     int i=0;
 9     int clientfd = client->clientfd;
10     struct sockaddr_in addr = client->addr;
11     int isConn = client->isConn;
12     int clientIndex = client->index;
13     //receive the string sent from the client
14     while((recvbytes = recv(clientfd, buff, BUFFSIZE, 0)) != -1)
15     {
16          buff[recvbytes] = ‘\0‘;
17          pthread_mutex_lock(&writeMutex);
18          Display(buff);
19          pthread_mutex_unlock(&writeMutex);
20     }   //end while
21
22     pthread_mutex_lock(&clientsMutex[clientIndex]);
23     client->isConn = 0;
24     pthread_mutex_unlock(&clientsMutex[clientIndex]);
25     pthread_cond_signal(&connDis);  //send a disconnetion signal and the waiting client can get response
26     if(close(clientfd) == -1)
27         fprintf(stderr, "Close %d client eroor: %s(errno: %d)\n", clientfd, strerror(errno), errno);
28     fprintf(stderr, "Client %d connetion is closed\n", clientfd);
29
30     pthread_exit(NULL);
31 }

Telnet连接显示

将程序在树莓派上编译之后运行,可以看到服务器端的socket创建,绑定以及监听成功。然后在Ubuntu上用telnet去连接,这里用了三个客户端进行连接。可以看到服务端接受了三个客户端的连接,然后在三个客户端分别发送字符串,发现其字符串分别在MAX7219上依次正常显示。

附(完整代码):

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <unistd.h>
  4 #include <sys/ioctl.h>
  5 #include <sys/time.h>
  6 #include <sys/fcntl.h>
  7 #include <sys/stat.h>
  8 #include <sys/types.h>
  9 #include <sys/socket.h>
 10 #include <netinet/in.h>
 11 #include <arpa/inet.h>
 12 #include <errno.h>
 13 #include <string.h>
 14 #include <pthread.h>
 15
 16
 17 #define PORT  8888
 18 #define BACKLOG 10
 19 #define MAXCONN 100
 20 #define BUFFSIZE 1024
 21
 22 typedef unsigned char BYTE;
 23 typedef struct ClientInfo
 24 {
 25     struct sockaddr_in addr;
 26     int clientfd;
 27     int isConn;
 28     int index;
 29 } ClientInfo;
 30
 31 pthread_t threadID[MAXCONN];
 32 pthread_mutex_t activeConnMutex;
 33 pthread_mutex_t clientsMutex[MAXCONN];
 34 pthread_cond_t connDis;
 35 pthread_mutex_t writeMutex;
 36
 37 ClientInfo clients[MAXCONN];
 38
 39 void Delay_xms(uint x)
 40 {
 41     uint i,j;
 42     for(i=0;i<x;i++)
 43         for(j=0;j<50000;j++);
 44 }
 45
 46 int Display(char str[])
 47 {
 48     int fd;
 49     int ret;
 50
 51     fd = open("/dev/MAX7219", O_WRONLY);
 52     if (fd < 0)
 53     {
 54         fprintf(stderr, "Fail to open /dev/MAX7219!\n");
 55         return -1;
 56     }
 57     int i=0;
 58     while(str[i])
 59     {
 60         if((ret = write(fd, &str[i], 1))<0)
 61         {
 62             fprintf(stderr, "Fail to write /dev/MAX7219! %d\n", ret);
 63             return -1;
 64         }
 65         ++i;
 66         Delay_xms(500);
 67     }
 68     fprintf(stdout, "Write /dev/MAX7219 successfully! %d\n", ret);
 69     close(fd);
 70     return 0;
 71 }
 72
 73 void clientManager(void* argv)
 74 {
 75     ClientInfo *client = (ClientInfo *)(argv);
 76
 77     BYTE buff[BUFFSIZE];
 78     int recvbytes;
 79
 80     int i=0;
 81     int clientfd = client->clientfd;
 82     struct sockaddr_in addr = client->addr;
 83     int isConn = client->isConn;
 84     int clientIndex = client->index;
 85
 86     while((recvbytes = recv(clientfd, buff, BUFFSIZE, 0)) != -1)
 87     {
 88          buff[recvbytes] = ‘\0‘;
 89          pthread_mutex_lock(&writeMutex);
 90          Display(buff);
 91          pthread_mutex_unlock(&writeMutex);
 92     }   //end while
 93
 94     pthread_mutex_lock(&clientsMutex[clientIndex]);
 95     client->isConn = 0;
 96     pthread_mutex_unlock(&clientsMutex[clientIndex]);
 97
 98     pthread_cond_signal(&connDis);  //send a disconnetion signal and the waiting client can get response
 99
100     if(close(clientfd) == -1)
101         fprintf(stderr, "Close %d client eroor: %s(errno: %d)\n", clientfd, strerror(errno), errno);
102     fprintf(stderr, "Client %d connetion is closed\n", clientfd);
103
104     pthread_exit(NULL);
105 }
106
107
108 int main()
109 {
110    int activeConn = 0;
111
112    //initialize the mutex
113    pthread_mutex_init(&activeConnMutex, NULL);
114    pthread_mutex_init(&writeMutex, NULL);
115    pthread_cond_init(&connDis, NULL);
116    int i=0;
117    for(;i<MAXCONN;++i)
118        pthread_mutex_init(&clientsMutex[i], NULL);
119
120    for(i=0;i<MAXCONN;++i)
121        clients[i].isConn = 0;
122
123    int listenfd;
124    struct sockaddr_in  servaddr;
125
126    //create a socket
127    if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
128    {
129        fprintf(stderr, "Create socket error: %s(errno: %d)\n", strerror(errno), errno);
130        exit(0);
131    }
132    else
133        fprintf(stdout, "Create a socket successfully\n");
134
135    //set the server address
136    memset(&servaddr, 0, sizeof(servaddr));  //initialize the server address
137    servaddr.sin_family = AF_INET;           //AF_INET means using TCP protocol
138    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);    //any in address(there may more than one network card in the server)
139    servaddr.sin_port = htons(PORT);            //set the port
140
141    //bind the server address with the socket
142    if(bind(listenfd, (struct sockaddr*)(&servaddr), sizeof(servaddr)) == -1)
143    {
144         fprintf(stderr, "Bind socket error: %s(errno: %d)\n", strerror(errno), errno);
145         exit(0);
146    }
147    else
148        fprintf(stdout, "Bind socket successfully\n");
149
150    //listen
151    if(listen(listenfd, BACKLOG) == -1)
152    {
153        fprintf(stderr, "Listen socket error: %s(errno: %d)\n", strerror(errno), errno);
154        exit(0);
155    }
156    else
157        fprintf(stdout, "Listen socket successfully\n");
158
159    while(1)
160    {
161        pthread_mutex_lock(&activeConnMutex);
162        if(activeConn >= MAXCONN)
163             pthread_cond_wait(&connDis, &activeConnMutex);    //wait on a signal
164        pthread_mutex_unlock(&activeConnMutex);
165
166        //find an empty postion for a new connnetion
167        int i=0;
168        while(i<MAXCONN)
169        {
170            pthread_mutex_lock(&clientsMutex[i]);
171            if(!clients[i].isConn)
172            {
173                pthread_mutex_unlock(&clientsMutex[i]);
174                break;
175            }
176            pthread_mutex_unlock(&clientsMutex[i]);
177            ++i;
178        }
179
180        //accept
181        struct sockaddr_in addr;
182        int clientfd;
183        int sin_size = sizeof(struct sockaddr_in);
184        if((clientfd = accept(listenfd, (struct sockaddr*)(&addr), &sin_size)) == -1)
185        {
186            sleep(1);
187            continue;
188        }
189        else
190            fprintf(stdout, "Accept socket successfully\n");
191
192        pthread_mutex_lock(&clientsMutex[i]);
193        clients[i].clientfd = clientfd;
194        clients[i].addr = addr;
195        clients[i].isConn = 1;
196        clients[i].index = i;
197        pthread_mutex_unlock(&clientsMutex[i]);
198
199        //create a thread for a client
200        pthread_create(&threadID[i], NULL, (void *)clientManager, &clients[i]);
201
202    }    //end-while
203 }

时间: 2024-11-13 03:20:28

网络LED矩阵显示器的相关文章

用LED矩阵屏 显示网络摄像头图像

介绍 在现代Web浏览器中,JavaScript公开了一些非常强大的特性.其中之一就是webkitGetUserMedia 功能,它可以让你访问一个电脑的webcam(在允许的情况下) 在这个例子中,我们将用Espruino板子来将服务器连接一个可以访问Webcam的页面,并发送一个低像素的图片给Espruino板,然后就可以显示在一个LED矩阵屏上. 你将需要 一个 RGB123 矩阵 - 我用的是16*16的 一个 WIZnet W5500模块 一个带有Webcam的笔记本/平板电脑 连线

Magnostics Image-based Search of Interesting Matrix Views for Guided Network Exploration(一种基于网络信息矩阵图像的网络探索方法)

网络.关系等数据变成如图的邻接矩阵时(红色代表两个节点也就是人,之间有联系),但是得到的矩阵会因为顺序的问题而出现不同的排列方式,在第一种中会发现因为有聚集的块状区域而很容易地把数据分为两个部分,然后根据数据的具体含义而得知其代表的意思,在此图中可以看出是两个集团. 当分析数据时候,把它转换成矩阵的形式,并运用一些矩阵重排序算法将矩阵变形,变成特定的图案pattern.而现在希望基于图像来查询哪些变形好的矩阵属于同一种pattern. 当前的数据量特别大,数据维度特别多,样式复杂多变,对于探测特

Poor God Water(ACM-ICPC 2018 焦作赛区网络预赛 矩阵快速幂)

题目描述 God Water likes to eat meat, fish and chocolate very much, but unfortunately, the doctor tells him that some sequence of eating will make them poisonous.Every hour, God Water will eat one kind of food among meat, fish and chocolate. If there are

LCD/LED/OLED/等离子显示器区别

LCD液晶显示器: LCD(Liquid Crystal Display),其构造是在两片平行的玻璃当中放置液态的晶体(液晶),在玻璃后面,以CCFL冷光灯管(类似日光灯)作背光源.液晶的成像原理可以简单的理解为,外界施加电压使杆状液晶分子改变方向,便如闸门般地阻隔背光源发出的光线的通透度,进而将光线投射在不同颜色的彩色滤光片中形成图像. 液晶显示器主要参数有:     对比度:对比度很重要,例如我们在看流媒体时,要看出人物场景的明暗对比,头发丝灰到黑的质感变化,就要靠对比度的高低来显现.   

不得不知的LED显示屏选购的常用术语

LED显示屏: LED显示屏(LED panEL):LED就是light emitting diode ,发光二极管的英文缩写,简称LED.它是一种通过控制半导体发光二极管的显示方式,用来显示文字.图形.图像.动画.行情.视频.录像信号等各种信息的显示屏幕. LED显示屏分为图文显示屏和视频显示屏,均由LED矩阵块组成.图文显示屏可与计算机同步显示汉字.英文文本和图形;视频显示屏采用微型计算机进行控制,图文.图像并茂,以实时.同步.清晰的信息传播方式播放各种信息,还可显示二维.三维动画.录像.电

【线性代数】图与网络

前面的关于线性代数的文章都是从数学的角度来解说的.本文将换个角度来解说问题. 导师时常告诉我,凡事都要想想它的物理或实际意义,须要透过现象看本质,这样就能更加深刻的理解,这样就能够看看线性代数有什么实际的用途. 如果有例如以下电路网络: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdGVuZ3dlaXR3/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center"

单片机与控制实验(2)——LED点阵显示屏

一.实验目的和要求 了解LED点阵显示的基本原理和实现方法.掌握点阵汉字库的编码和从标准字库中提取汉字编码的方法. 二.实验设备 单片机测控实验系统 LED点阵显示器实验模块 Keil开发环境 STC-ISP程序下载工具 三.实验内容 了解16*16点阵电路的原理.编写汇编语言程序,编写一行汉字字符(至少三个字)的显示程序,并且能够从左到右(或从右到左)循环显示. 四.实验步骤 1. 掌握点阵式LED显示屏的控制方法:2. 使用MCS-51汇编语言,使用LED点阵显示器显示出正确的汉字字符及动态

第五篇:本人多年来积累的单片机程序 资料下载(绝对干货,内附下载链接)

首先打个广告:如果需要做毕设以及嵌入式项目合作,欢迎进入我们工作室:创想嵌入式设计工作室 本人搜集的大量单片机源码资料及毕业设计资料,足有1200+套,很多实用的demo源码和毕业设计参考方案,可用于做项目时借鉴,博尾附有下载链接.(内附本人联系方式,可一起交流探讨,交朋友) 为了方便 查找,我已将项目源码的目录整理出来了,方便 采用"ctrl +F"快速查找验证是否有需要的demo.可以一键打包下载,也可只下载需要的demo. 下载链接内附本人联系方式,如果在安装过程中遇到问题,可以

嵌入式

作者:秋枫链接:https://www.zhihu.com/question/35134077/answer/62683025来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. 去淘宝买小车底盘,可以用全直流的可以用直流+舵机的,买驱动板,然后用单片机做智能小车,加上各种传感器可以实现不同的功能,比如红外的,可以做避障,可以做寻迹,原理就是根据不同的红外传感器传回的有没有返回信号,哪些灯有信号,甚至是强弱来判断事件,单片机做出动作处理.红外遥控器非常便宜,只要知道协议