در بخش های قبل راه اندازی گذرگاه Ethernet مفاهیم مقدماتی و موردنیاز مرتبط با شبکه های کامپیوتری و Ethernet بیان شد و در بخش دوم نحوه برنامه نویسی سوکت TCP و UDP با رابط کاربری Raw بطور مفصل بررسی شد. در این بخش نحوه نوشتن برنامه سوکت TCP و UDP با رابط کاربری Netconn و با استفاده از سیستم عامل FreeRTOS شرح داده شده است.
فهرست مطالب
در بحث شبکههای کامپیوتری، یکی از بهترین روشها به منظور تشریح مفهومی نحوه کارکرد و پیادهسازی شبکه، استفاده از مدلهای ارائه شده در این زمینه مانند OSI و TCP/IP است. این مدلها را میتوان در اکثر کتابها و مقالات در زمینه شبکه های کامپیوتری مشاهده کرد. در ادامه این گزارش ابتدا با این دو مدل و لایههای مختلف آن بصورت مختصر بیان شدهاست.
رابط کاربری Netconn
Netconn یک رابط کاربری سطح بالا است که نحوه اجرای آن بر مبنای open-read-write-close است. Netconn برای عملکرد صحیح به سیستم عامل و محیط مالتی تسک نیازمند است.
نحوه تنظیم سیستم عامل FreeRTOS
در بخش آموزش سیستم عامل FreeRTOS نحوه تنظیم آن بطور کامل بیان شده است. لذا از تکرار مجدد آن خودداری شده است. تنها نکتهای که باید به آن توجه کرد آن است که مقدار حافظه heap در فیلد TOTAL_HEAP_SIZE را مقدار کافی قرار دهید. در اینجا مقدار آن 40000 بایت تنظیم شده است. در اینجا اینترفیس سیستم عامل CMSIS_V1 انتخاب شده است ولی میتوان از CMSIS_V2 نیز استفاده کرد. در شکل 1 نحوه تنظیم FreeRTOS نشان داده شدهاست.
نحوه تنظیم کتابخانه LwIP
در بخشهای قبل نحوه تنظیم کتابخانه LwIP در نرم افزار STM32CubeMX با رابط کاربری Raw شرح داده شد. در این بخش نحوه تنظیم آن با رابط کاربری Netconn بیان شده است. در شکل 2 نحوه تنظیم کتابخانه LwIP نشان داده شده است.
پس از انجام تنظیمات مربوط به FreeRTOS، Ethernet و LwIP به سربرگ Project Manager بروید و مقدار حافظه Stack و Heap را به مقدار کافی بزرگ دهید. در اینجا اندازه حافظه Stack و Heap به ترتیب برابر 0x8000 و 0x4000 تنظیم شدهاند. بعد از انجام تنظیمات فوق نامه و مسیر پروژه را انتخاب کرده و بر روی دکمه Project Manager کلیک کنید تا پروژه اولیه ایجاد شود.
برنامه نویسی سوکت با استفاده از کتابخانه LwIP با رابط کاربری Netconn
در بخشهای قبل پروتکلهای TCP و UDP معرفی شدند. همچنین نحوه برنامهنویسی سوکت شرح داده شد. لذا در این قسمت از تکرار آن خودداری کرده و فقط مسائل مربوط به رابط کاربری Netconn بیان شده است.
در جدول 1 توابعی که رابط کاربری Raw برای کار با سوکت TCPدر اختیار برنامهنویس قرار داده شده نشان داده شده است. همانطور که مشاهده میکنید، رابط کاربری Netconn بسیار شبیه به کتابخانه Winsock و یا سایر کتابخانههایی که در محیط ویندوز یا لینوکس برای Socket Programming استفاده میشود، است.
برای یادگیری نحوه استفاده از توابع فوق دو مثالی که در بخش قبل با استفاده از رابط کاربری Raw انجام شد را با استفاده از رابط کاربری Raw انجام میدهیم.
مثال اول: TCP Server Socket
کد این برنامه بصورت زیر است.
static void tcpecho_thread(void *arg)
{
struct netconn *conn, *newconn;
err_t err;
LWIP_UNUSED_ARG(arg);
/* Create a new connection identifier. */
/* Bind connection to well known port number 7. */
conn = netconn_new(NETCONN_TCP);
netconn_bind(conn, IP_ADDR_ANY, 7);
LWIP_ERROR("tcpecho: invalid conn", (conn != NULL), return;);
/* Tell connection to go into listening mode. */
netconn_listen(conn);
while (1) {
/* Grab new connection. */
err = netconn_accept(conn, &newconn);
/*printf("accepted new connection %p\n", newconn);*/
/* Process the new connection. */
if (err == ERR_OK) {
struct netbuf *buf;
void *data;
u16_t len;
while ((err = netconn_recv(newconn, &buf)) == ERR_OK) {
if (*(uint8_t*)(buf->p->payload) == 'A')
{
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_SET);
}
else if (*(uint8_t*)(buf->p->payload) == 'B')
{
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_RESET);
}
/*printf("Recved\n");*/
do {
netbuf_data(buf, &data, &len);
err = netconn_write(newconn, data, len, NETCONN_COPY);
} while (netbuf_next(buf) >= 0);
netbuf_delete(buf);
}
/*printf("Got EOF, looping\n");*/
/* Close connection and discard connection identifier. */
netconn_close(newconn);
netconn_delete(newconn);
}
}
}
در برنامه فوق ابتدا با فراخوانی تابع netconn_new یک سوکت از نوع TCP تعریف شده و با استفاده از تابع netconn_bind به پورت هفت متصل شده است. سپس با فراخوانی تابع netconn_listen به سوکت گوش میدهد. تابع netconn_accept بصورت Blocking است. بطوریکه تا زمانیکه Request دریافت نکند در این تابع میماند. بعد از دریافت Request منتظر دریافت داده از Client مانده و بعد از دریافت آن با توجه به مقدار آن LED را روشن و یا خاموش میکند. همچنین داده دریافتی را به Client ارسال میکند.
برای بهینهسازی میتوان هنگامیکه یک Request دریافت کرد، Thread جدیدی تعریف کرد و در آن پردازش موردنظر را انجام داد.
مثال دوم: UDP Server Socket
کد مثال UDP Server Socket بصورت زیر است.
static void udpecho_thread(void *arg)
{
struct netconn *conn;
struct netbuf *buf, *tx_buf;
err_t err;
LWIP_UNUSED_ARG(arg);
conn = netconn_new(NETCONN_UDP);
netconn_bind(conn, IP_ADDR_ANY, 7);
LWIP_ERROR("udpecho: invalid conn", (conn != NULL), return;);
while (1) {
err = netconn_recv(conn, &buf);
if (err == ERR_OK) {
if (*(uint8_t*)(buf->p->payload) == 'A')
{
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_SET);
}
else if (*(uint8_t*)(buf->p->payload) == 'B')
{
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_6, GPIO_PIN_RESET);
}
tx_buf = netbuf_new();
netbuf_alloc(tx_buf, buf->p->tot_len);
pbuf_take(tx_buf->p, (const void *)buf->p->payload, buf->p->tot_len);
err = netconn_sendto(conn, tx_buf, (const ip_addr_t *)&(buf->addr), buf->port);
if(err != ERR_OK) {
LWIP_DEBUGF(LWIP_DBG_ON, ("netconn_send failed: %d\n", (int)err));
} else {
LWIP_DEBUGF(LWIP_DBG_ON, ("got %s\n", buffer));
}
netbuf_delete(tx_buf);
}
netbuf_delete(buf);
}
}
در این برنامه ابتدا یک سوکت UDP تعریف شده و به پورت 7 متصل شده است. سپس منتظر دریافت داده از Client میماند و در صورت دریافت داده آن را بررسی کرده و در صورتیکه داده حرف اول آن “A” باشد LED را روشن و اگر “B” باشد آن را خاموش میکند. در نهایت داده دریافت شده را به Client ارسال میکند.
خروجی برنامه سوکت UDP Server
در شکل 4 فریم های جابجا شده بین میکروکنترلر و کامپییوتر نشان داده شده است.
سلام و ممنون از سایت خوبتون، یه سوال دارم چرا من سورس کد های مثال اول و مثال دوم را نمی بینم. ممنون می شم راه نماییم نمایید.
با سلام و احترام،
کدهای مربوط به مثالها گذاشته شدند.
سپاس از توجه شما
tnx a lot