前言
本次实验延续上次实验的环境,使用MAX7219驱动8x8点阵。上位机使用Ubuntu 14.04,下位机使用Raspberry pi 2。
使用的还是上次实验编译好的非阻塞式写入点阵支持内核模块。
这次的实验跟计网实验好像啊好像啊。
控制点阵显示字符
通过write函数向device写入数据即可。
#include
#include
#include
#include
#include
int Matrix;
#define ALLCHAR "0123456789abcdefghijklmnopqrstuvwxyz"
int main(){
Matrix = open("/dev/matrix", O_WRONLY);
if (Matrix < 0){
fprintf(stderr, "Cound not open matrix device");
exit(1);
}
write(Matrix, ALLCHAR, strlen(ALLCHAR));
return 0;
}
编写网络程序接受TCP请求
使用linux下的socket编程,接受外部TCP请求,并将其发送来的所有数据写入matrix设备即可实现显示功能。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int Matrix;
int server;
#define PORT 8080
#define ADDR "0.0.0.0"
#define QUEUE 20
#define BUFF_SIZE 2048
int main(){
// 打开matrix
Matrix = open("/dev/matrix", O_WRONLY);
if (Matrix < 0){
fprintf(stderr, "Cound not open matrix device\n");
exit(1);
}
// 建立服务器
int server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = inet_addr(ADDR);
serverAddr.sin_port = htons(PORT);
// 绑定ip以及端口
if (bind(server, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1){
fprintf(stderr, "Count not bind %s:%d\n", ADDR, PORT);
exit(1);
}
if (listen(server, QUEUE) == -1){
fprintf(stderr, "Listen error\n");
exit(1);
}
printf("Server running at %s:%d\n", ADDR, PORT);
while (1){
struct sockaddr_in clientAddr;
socklen_t length = sizeof(clientAddr);
// 对连入的连接进行处理
int conn = accept(server, (struct sockaddr*)&clientAddr, &length);
if (conn < 0){
fprintf(stderr, "Connect error");
exit(1);
}
printf("A new connection from %s:%d\n", inet_ntoa(clientAddr.sin_addr), clientAddr.sin_port);
// 处理连接发送过来的字符
while (1){
char receiveBuf[BUFF_SIZE];
int count;
memset(receiveBuf, 0, sizeof(receiveBuf));
// 接收字符
count = recv(conn, receiveBuf, sizeof(receiveBuf), 0);
// 如果接收到的字符为空,则表示离开
if (count == 0){
close(conn);
break;
}
// 将接收到的所有字符发送给matrix进行显示
write(Matrix, receiveBuf, count);
}
}
close(server);
return 0;
}
使用线程处理多个连接
上一节的程序使用的是阻塞式的处理连接,这将导致服务器每次accept了一个连接之后,无法再处理其余连接。
而这种情况的处理方式有很多。可以使用一个线程对应一个socket fd的方式进行阻塞式监听。也可以使用IO多路复用,如select、poll、epoll,在单线程的环境下进行高效的网络IO操作。
使用线程的方式对于阻塞式程序的改动较小,基本上逻辑还是一致的。
…………
void* serverRecv(void* data){
int conn = *(int*)data;
while (1){
char receiveBuf[BUFF_SIZE];
int count;
memset(receiveBuf, 0, sizeof(receiveBuf));
count = recv(conn, receiveBuf, sizeof(receiveBuf), 0);
if (count == 0){
close(conn);
break;
}
write(Matrix, receiveBuf, count);
}
pthread_exit(NULL);
return NULL;
}
…………
int main(){
…………
printf("Server running at %s:%d\n", ADDR, PORT);
while (1){
pthread_t thread;
struct sockaddr_in clientAddr;
socklen_t length = sizeof(clientAddr);
int conn = accept(server, (struct sockaddr*)&clientAddr, &length);
int result;
if (conn < 0){
fprintf(stderr, "Connect error");
exit(1);
}
printf("A new connection from %s:%d\n", inet_ntoa(clientAddr.sin_addr), clientAddr.sin_port);
result = pthread_create(&thread, NULL, serverRecv, &conn);
if (result < 0){
printf("Create thread error\n");
exit(1);
}
}
…………
}
在这个版本中,主程序只负责接受连接,而接受到的连接交由子线程进行监听处理。
参考资料
- Linux的SOCKET编程详解
- Linux IO 多路复用是什么意思?
- Linux TCP server系列(4)-浅谈listen与大并发TCP连接
- Linux TCP server系列(5)-select模式下的单进程server