网络LED矩阵显示器
2016-06-08 14:23
483 查看
前言
主要是Linux网络程序和多线程程序的编写,实现一个网络访问的LED矩阵显示器。客户端采用telnet进行连接。socket的编程主要和计网实验课上的差不多,计网实验socket编程传送门
字符串的显示
在上次显示一个字符的基础上进行一个字符串的显示。字符的显示对于字符串的显示可以直接写入一个字符串,在内核中采用内核队列进行实现。或者每次只能写入一个字符,在应用程序进行一个循环写入。由于上次没有实现内核队列的字符串显示,这里就采用了应用程序循环进行实现。
在上次的基础上,编写一个Display函数,传入值为一个字符串,每次只要将一个字符写入之后,延时500ms之后,不断写入即可。
//Display a string on the MAX7219 int Display(char str[]) { int fd; int ret; fd = open("/dev/MAX7219", O_WRONLY); if (fd < 0) { fprintf(stderr, "Fail to open /dev/MAX7219!\n"); return -1; } int i=0; while(str[i]) { if((ret = write(fd, &str[i], 1))<0) { fprintf(stderr, "Fail to write /dev/MAX7219! %d\n", ret); return -1; } ++i; Delay_xms(500); } fprintf(stdout, "Write /dev/MAX7219 successfully! %d\n", ret); close(fd); return 0; }
多线程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队列中将客户端信息去除进行处理。
int listenfd; struct sockaddr_in servaddr; //create a socket if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { fprintf(stderr, "Create socket error: %s(errno: %d)\n", strerror(errno), errno); exit(0); } else fprintf(stdout, "Create a socket successfully\n"); //set the server address memset(&servaddr, 0, sizeof(servaddr)); //initialize the server address servaddr.sin_family = AF_INET; //AF_INET means using TCP protocol servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //any in address(there may more thaonenetwork card in the server) servaddr.sin_port = htons(PORT); //set the port //bind the server address with the socket if(bind(listenfd, (struct sockaddr*)(&servaddr), sizeof(servaddr)) == -1) { fprintf(stderr, "Bind socket error: %s(errno: %d)\n", strerror(errno), errno); exit(0); } else fprintf(stdout, "Bind socket successfully\n"); //listen if(listen(listenfd, BACKLOG) == -1) { fprintf(stderr, "Listen socket error: %s(errno: %d)\n", strerror(errno), errno); exit(0); } else fprintf(stdout, "Listen socket successfully\n");
客户端对应线程的创建
服务端调用Accept函数进行接收一个客户端的信息,如果存在连接上的客户端,则为其分配一个线程位置,然后调用pthread_create创建一个线程,并且将该客户端的信息参数传入线程。这里采用一个结构体的方式记录了客户端的相关信息,包括客户端IP,端口号,编号,是否连接等等。这里为了防止服务器的无限的创建线程,设置了最大同时在线的客户端数目(MAXCONN),如果新连接上的客户端发现已经操作上线,那么该主线程就会睡眠,其等待在了一个客户端断开连接的信号量上面,如果有客户端断开连接,则主线程被唤醒,接受该客户端的连接。
需要注意的是这里涉及到很多的多线程的操作,很有可能发生数据冲突,需要很好使用pthread中的互斥锁防止发生数据冲突。
while(1) { pthread_mutex_lock(&activeConnMutex); if(activeConn >= MAXCONN) pthread_cond_wait(&connDis, &activeConnMutex); //wait on a signal pthread_mutex_unlock(&activeConnMutex); //find an empty postion for a new connnetion int i=0; while(i<MAXCONN) { pthread_mutex_lock(&clientsMutex[i]); if(!clients[i].isConn) { pthread_mutex_unlock(&clientsMutex[i]); break; } pthread_mutex_unlock(&clientsMutex[i]); ++i; } //accept struct sockaddr_in addr; int clientfd; int sin_size = sizeof(struct sockaddr_in); if((clientfd = accept(listenfd, (struct sockaddr*)(&addr), &sin_size)) == -1) { sleep(1); continue; } else fprintf(stdout, "Accept socket successfully\n"); pthread_mutex_lock(&clientsMutex[i]); clients[i].clientfd = clientfd; clients[i].addr = addr; clients[i].isConn = 1; clients[i].index = i; pthread_mutex_unlock(&clientsMutex[i]); //create a thread for a client pthread_create(&threadID[i], NULL, (void *)clientManager, &clients[i]); } //end-while
客户端服务线程
在服务器创建了一个线程之后就会调用clientManager这个函数对于该连接进行处理。对于该线程,其主要是阻塞着等待在recv函数上面,接受客户端发送来的字符串,然后调用上面的Display函数将该字符串显示在MAX7219上面。这里采用了一个Write的互斥锁,将其加在了Display函数前后,这样就保证了多个客户端连接的时候,只有在等一个客户端的字符串显示完成之后,另外一个客户端的字符串才会被显示,不会造成多个客户端的字符串交错显示的情况。
void clientManager(void* argv) { ClientInfo *client = (ClientInfo *)(argv); BYTE buff[BUFFSIZE]; int recvbytes; int i=0; int clientfd = client->clientfd; struct sockaddr_in addr = client->addr; int isConn = client->isConn; int clientIndex = client->index; //receive the string sent from the client while((recvbytes = recv(clientfd, buff, BUFFSIZE, 0)) != -1) { buff[recvbytes] = '\0'; pthread_mutex_lock(&writeMutex); Display(buff); pthread_mutex_unlock(&writeMutex); } //end while pthread_mutex_lock(&clientsMutex[clientIndex]); client->isConn = 0; pthread_mutex_unlock(&clientsMutex[clientIndex]); pthread_cond_signal(&connDis); //send a disconnetion signal and the waiting client can get response if(close(clientfd) == -1) fprintf(stderr, "Close %d client eroor: %s(errno: %d)\n", clientfd, strerror(errno), errno); fprintf(stderr, "Client %d connetion is closed\n", clientfd); pthread_exit(NULL); }
Telnet连接显示
将程序在树莓派上编译之后运行,可以看到服务器端的socket创建,绑定以及监听成功。然后在Ubuntu上用telnet去连接,这里用了三个客户端进行连接。可以看到服务端接受了三个客户端的连接,然后在三个客户端分别发送字符串,发现其字符串分别在MAX7219上依次正常显示。[b]
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/time.h> #include <sys/fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #include <string.h> #include <pthread.h> #define PORT 8888 #define BACKLOG 10 #define MAXCONN 100 #define BUFFSIZE 1024 typedef unsigned char BYTE; typedef struct ClientInfo { struct sockaddr_in addr; int clientfd; int isConn; int index; } ClientInfo; pthread_t threadID[MAXCONN]; pthread_mutex_t activeConnMutex; pthread_mutex_t clientsMutex[MAXCONN]; pthread_cond_t connDis; pthread_mutex_t writeMutex; ClientInfo clients[MAXCONN]; void Delay_xms(uint x) { uint i,j; for(i=0;i<x;i++) for(j=0;j<50000;j++); } int Display(char str[]) { int fd; int ret; fd = open("/dev/MAX7219", O_WRONLY); if (fd < 0) { fprintf(stderr, "Fail to open /dev/MAX7219!\n"); return -1; } int i=0; while(str[i]) { if((ret = write(fd, &str[i], 1))<0) { fprintf(stderr, "Fail to write /dev/MAX7219! %d\n", ret); return -1; } ++i; Delay_xms(500); } fprintf(stdout, "Write /dev/MAX7219 successfully! %d\n", ret); close(fd); return 0; } void clientManager(void* argv) { ClientInfo *client = (ClientInfo *)(argv); BYTE buff[BUFFSIZE]; int recvbytes; int i=0; int clientfd = client->clientfd; struct sockaddr_in addr = client->addr; int isConn = client->isConn; int clientIndex = client->index; while((recvbytes = recv(clientfd, buff, BUFFSIZE, 0)) != -1) { buff[recvbytes] = '\0'; pthread_mutex_lock(&writeMutex); Display(buff); pthread_mutex_unlock(&writeMutex); } //end while pthread_mutex_lock(&clientsMutex[clientIndex]); client->isConn = 0; pthread_mutex_unlock(&clientsMutex[clientIndex]); pthread_cond_signal(&connDis); //send a disconnetion signal and the waiting client can get response if(close(clientfd) == -1) fprintf(stderr, "Close %d client eroor: %s(errno: %d)\n", clientfd, strerror(errno), errno); fprintf(stderr, "Client %d connetion is closed\n", clientfd); pthread_exit(NULL); } int main() { int activeConn = 0; //initialize the mutex pthread_mutex_init(&activeConnMutex, NULL); pthread_mutex_init(&writeMutex, NULL); pthread_cond_init(&connDis, NULL); int i=0; for(;i<MAXCONN;++i) pthread_mutex_init(&clientsMutex[i], NULL); for(i=0;i<MAXCONN;++i) clients[i].isConn = 0; int listenfd; struct sockaddr_in servaddr; //create a socket if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { fprintf(stderr, "Create socket error: %s(errno: %d)\n", strerror(errno), errno); exit(0); } else fprintf(stdout, "Create a socket successfully\n"); //set the server address memset(&servaddr, 0, sizeof(servaddr)); //initialize the server address servaddr.sin_family = AF_INET; //AF_INET means using TCP protocol servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //any in address(there may more than one network card in the server) servaddr.sin_port = htons(PORT); //set the port //bind the server address with the socket if(bind(listenfd, (struct sockaddr*)(&servaddr), sizeof(servaddr)) == -1) { fprintf(stderr, "Bind socket error: %s(errno: %d)\n", strerror(errno), errno); exit(0); } else fprintf(stdout, "Bind socket successfully\n"); //listen if(listen(listenfd, BACKLOG) == -1) { fprintf(stderr, "Listen socket error: %s(errno: %d)\n", strerror(errno), errno); exit(0); } else fprintf(stdout, "Listen socket successfully\n"); while(1) { pthread_mutex_lock(&activeConnMutex); if(activeConn >= MAXCONN) pthread_cond_wait(&connDis, &activeConnMutex); //wait on a signal pthread_mutex_unlock(&activeConnMutex); //find an empty postion for a new connnetion int i=0; while(i<MAXCONN) { pthread_mutex_lock(&clientsMutex[i]); if(!clients[i].isConn) { pthread_mutex_unlock(&clientsMutex[i]); break; } pthread_mutex_unlock(&clientsMutex[i]); ++i; } //accept struct sockaddr_in addr; int clientfd; int sin_size = sizeof(struct sockaddr_in); if((clientfd = accept(listenfd, (struct sockaddr*)(&addr), &sin_size)) == -1) { sleep(1); continue; } else fprintf(stdout, "Accept socket successfully\n"); pthread_mutex_lock(&clientsMutex[i]); clients[i].clientfd = clientfd; clients[i].addr = addr; clients[i].isConn = 1; clients[i].index = i; pthread_mutex_unlock(&clientsMutex[i]); //create a thread for a client pthread_create(&threadID[i], NULL, (void *)clientManager, &clients[i]); } //end-while }
View Code
相关文章推荐
- 理解HTTP幂等性
- linux网络编程常用头文件总结
- java后台调用HttpURLConnection类模拟浏览器请求实例
- apache服务,或者说httpd服务,如何启动,如何开机启动。
- Apache服务器主配置文件httpd.conf详解2
- Apache主配置文件httpd.conf 详解
- ubuntu下,django结合网络爬虫环境的搭建
- apache/httpd安装
- Apache服务以及httpd.conf配置详解
- linux上安装apache以及httpd.conf基本配置
- 解决WebClient或HttpWebRequest首次连接缓慢问题
- jboss配置https
- iOS网络-06-监听Iphone的网络状态
- iOS网络-05-AFNetwoking原理及常用操作
- iOS网络-04-大文件下载
- iOS网络-03-NSURLSession与NSURLSessionTask
- http://blog.sina.com.cn/s/blog_4dd787e40102uysg.html
- svn:E175002:Unexpected HTTP status 405'Method Not Allowed
- 图解用Fiddler做http协议分析入门
- Android xutil3网络模块二次封装