Bài 6 : Lập trình giao tiếp mạng TCP/IP Raspberry Pi phần 2
15:31 - 15/01/2019
Bài 6 : Lập trình giao tiếp mạng TCP/IP Raspberry Pi phần 2
Remote Desktop Raspberry Pi không cần Wifi, mạng LAN và IP
Camera nhiệt giải pháp tuyệt vời cho mùa Covid-19
Lập trình cơ bản với OpenPLC trên Raspberry Pi
Hướng dẫn cài đặt OpenPLC trên Raspberry Pi
Chuẩn bị phần cứng+ 1 board mạch Raspberry Pi 4 Model B (Chú ý : Các bạn có thể lựa chọn các phiên bản 1GB, 2GB hoặc 4GB RAM tại Mlab.vn) + 1 board mạch ESP8266 + 1 board mạch Arduino
Trong phần 1 mình đã trình bày các lý thuyết để xây dựng được mô hình server- client đơn giản với giao thức truyền nhận thông tin là TCP-IP. Các bạn đã có thể dùng Raspberry làm server trung tâm. Máy tính (client) có thể thông gửi và nhận dữ liệu tới Pi mà qua đó có thể gián tiếp điều khiển hoặc xem xét thông tin của các client khác. Tuy nhiên phần trước server mới chỉ có thể kết nối với duy nhất một client. Các client khác muốn kết nối tới đều phải chờ đợi client đang kết nối kết thúc. Phần tiếp theo đây sẽ trình bày cách giải quyết để server thực sự là server – có thể cùng lúc làm việc với nhiều client. Nền tảng UNIX hỗ trợ nhiều phương thức xử lý khác nhau và chia làm 2 nhánh:
Mỗi phương thức có nhiều ưu điểm, nhược điểm khác nhau. Trong bài này mình sẽ trình bày phương thức thread-base. Lý do vì nó đơn giản để thực hiện và có thể thỏa mãn nhu cầu về số lượng client lớn. 1. MultithreadTrong linux multithread được hỗ trợ bởi thư viện Pthread được nhúng sẵn trong linux. Các bạn không cần phải quan tâm gì thêm mà chỉ cần uống nhanh một tách trà, bật Raspberry lên và bắt tay vào ngay vào code :) Công việc luôn được bắt đầu bằng việc khai báo thư viện #include <pthread.h>
Hàm thiết lập và hủy bỏ thread : int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
void pthread_exit(void *retval);
Trong đó :
Hãy cùng xem ví dụ dưới đây, các bạn nên copy code vào để chạy thử trước : // thread.c
#include <stdio.h>
#include <unistd.h> // for sleep
#include <pthread.h>
// Ham xu ly cho thread 1
void *Thread1(void *threadid){
while(1){
printf("thread 1 : hello\n");
sleep(3);
}
}
// Ham xu ly cho thread 2
void *Thread2(void *threadid){
while(1){
printf("thread 2 : hello\n");
sleep(4);
}
}
int main () {
pthread_t threads[NUM_THREADS];
int rc, i = 0;
// Tao thread 1
printf("Creating thead %d, \n",i);
if ( (rc = pthread_create(&threads[i], NULL, Thread1, NULL)) ){
printf("Error:unable to create thread, %d\n",rc );
return 0;
}
i++;
// Tao thread 2
printf("Creating thead %d, \n",i);
if ( (rc = pthread_create(&threads[i], NULL, Thread2, NULL)) ){
printf("Error:unable to create thread, %d\n",rc );
return 0;
}
// Ham main van se chay nhiem vu rieng cua no
while(1){
printf("main thread : hello\n");
sleep(2);
}
pthread_exit(NULL);
return 0;
} Các bạn có thể down code trực tiếp từ github của mình. Biên dich chương trình với lệnh gcc -o thread thread.c –lpthread # -lpthread khai báo dùng thư viện pthread Kết quả sẽ hiển thị như sau: Chương trình sẽ chạy trêm 3 luồng riêng biệt là main thread, thread1 và thread2. Mỗi thread sẽ gọi hàm xử lý riêng và sẽ hiển thị câu chào lên màn hinh. Tất nhiên là các thread có thể gọi chung một hàm xử lý. Lưu ý rằng hàm xử lý luôn làm hàm con trỏ dạng void. Với úng dụng của thread chương trình có thẻ hoạt động đa nhiệm không chỉ với server-client mà với bất cứ chương trình nào bạn mong muốn thực hiện đa nhiệm. 2. Server – multitaskingTừ ví dụ của phần trước là client trên máy tính kết nối tới Pi. Bây giờ mình sẽ tận dụng luôn từ ví dụ đó để có thể test với server mới. Server mới có thể cùng một lúc nhận nhiều tin nhắn của client và hiển thị lên màn hình, server mới cũng sẽ biết chính xác là client nào gửi thông tin cho mình . Các bạn có thể sửa từ bản server.c cũ như sau : // cau truc struct cho thread, them vao phan dau trong server.c
struct ThreadArgs{
int clntSock; /* Socket descriptor for client */
};
// Ham xu ly client, them vao phan dau trong server.c
void *HandleClient(void *threadArgs){
int clntSock;
int recvMsgSize;
char buffer[BUFFSIZE];
bzero(buffer,BUFFSIZE);
clntSock = ((struct ThreadArgs *) threadArgs) -> clntSock;
while(1){
recvMsgSize = recv(clntSock,buffer,BUFFSIZE,0);
if (recvMsgSize < 0)
error("ERROR reading from socket");
else if(recvMsgSize>0){
printf(". Client[%d]: %s\n",clntSock,buffer);
bzero(buffer,strlen(buffer));
}
else{
printf("- Client[%d]: disconnected !\n",clntSock);
break;
}
}
close(clntSock);
}
// Thay the tu doan accept den het ham while(1) trong server.c, nhiem vu cua phan nay la tao thread khi co thread moi
while(1){
// Wait for a client to connect
if( (clntSock = accept(servSock, (struct sockaddr*) &cli_addr, &clntLen)) < 0)
error("accept() failed !");
getpeername(clntSock, (struct sockaddr *) &cli_addr, &clntLen);
/* Create separate memory for client argument */
if ((threadArgs = (struct ThreadArgs *) malloc(sizeof(struct ThreadArgs))) == NULL)
error("malloc() failed");
threadArgs -> clntSock = clntSock;
if (pthread_create(&threadID, NULL, HandleClient, (void *) threadArgs) != 0)
error("pthread_create() failed");
printf("\n+ New client[%d][Addr:%s][Port:%d]\n\n",
clntSock, inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
} Các bạn hãy xem kỹ code trên github nhé. Giải thích thêm : + struct ThreadArgs : Được dùng để lưu thông tin cho thread + HandleClient() : hàm xử lý tương ứng với từng client + Chương trình sẽ tạo ra thread tương ứng với từng client mỗi khi có client mới kết nối tới. + Hàm getpeername() : dùng để lấy lấy thông tin địa chỉ của client. Biên dich chương trình với lệnh gcc -o server-thread server-thread.c -lpthread Bây giờ các bạn hãy chạy thử ./server-thread, và bật nhiều client cùng một lúc trên nhiều terminal khác nhau để kết tới server. 3. Chương trình client với Esp8266Mình sẽ lấy esp8266 làm client. Các bạn xem thêm phần cài đặt thư viện và lập trình cho esp8266 trên arduino tại đây. Chương trình cho esp8266 cũng sẽ thực hiện công việc giống hệt client trên máy tính. Nó sẽ gửi tin nhắn đến cho server. Chương trình như sau : // WifiClientBasic.ino
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
ESP8266WiFiMulti WiFiMulti;
const uint16_t port = 8888; // port tương ứng với server
const char * host = "192.168.1.23"; // ip của raspberry
void setup() {
Serial.begin(115200);
delay(10);
// Can thay ten wifi và mat khau nha ban vao
WiFiMulti.addAP("ten wifi", "mat khau la");
Serial.println();
Serial.println();
Serial.print("Wait for WiFi... ");
while(WiFiMulti.run() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
delay(500);
}
void loop() {
Serial.print("connecting to ");
Serial.println(host);
// Use WiFiClient class to create TCP connections
WiFiClient client;
if (!client.connect(host, port)) {
Serial.println("connection failed");
Serial.println("wait 5 sec...");
delay(5000);
return;
}
while(1){
if (Serial.available() > 0) {
String str = Serial.readString();
// gui cho server
client.print(str);
Serial.print("message: ");
Serial.println(str);
}
}
//read back one line from server
//String line = client.readStringUntil('\r');
//client.println(line);
//Serial.println("closing connection");
client.stop();
}
Các bạn phải chú ý tới những thiết lập : + const uint16_t port = 8888; // thiết lập port tương ứng với server + const char * host = "192.168.1.23"; // địa chỉ của Pi + WiFiMulti.addAP("ten wifi", "mat khau la"); // tên wifi và mật khẩu wifi Kết quả |
Để cập nhật các tin tức công nghệ mới các bạn làm theo hướng dẫn sau đây :
Các bạn vào Trang chủ >> Tin tức. ở mục này có các bài viết kỹ thuật thuộc các lĩnh vực khác nhau các bạn có thể lựa chọn lĩnh vực mà mình quan tâm để đọc nhé !!!
Các bạn cũng có thế kéo xuống cuối trang để xem những tin tức công nghệ mới nhất.