网络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 }
View Code

 

posted @ 2016-06-08 14:23  JackWang822  阅读(548)  评论(0编辑  收藏  举报