您的位置:首页 > 理论基础 > 计算机网络

网络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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: