Telnet

Telnet protocol is a member of the TCP/IP protocol family and is a standard protocol for Internet remote login services. The purpose of Telnet protocol is to provide a relatively universal, bidirectional, octet-oriented communication method that allows interface terminal devices and terminal-oriented processes to interact with each other through a standard process. The application of Telnet protocol can turn the computer used by the local user into a terminal of the remote host system.

The Telnet protocol has the following characteristics:

  1. Adapt to heterogeneity  

In order to make Telnet interoperability between multiple operating systems possible, it is necessary to have a detailed understanding of heterogeneous computers and operating systems. For example, some operating systems require each line of text to end with an ASCII carriage return control character (CR), while others require the use of an ASCII line feed character (LF), and still others require the two-character sequence of carriage return-line feed (CR-LF). For another example, most operating systems provide users with a shortcut key to interrupt program execution, but this shortcut key may be different in each system (some systems use CTRL+C, while others use ESCAPE). If the heterogeneity between systems is not taken into account, then the characters or commands issued locally are likely to be inaccurate or erroneous after being transmitted to a remote location and interpreted by the remote system. Therefore, the Telnet protocol must solve this problem.

In order to adapt to heterogeneous environments, the Telnet protocol defines the transmission method of data and commands on the Internet. This definition is called the Network Virtual Terminal (NVT). Its application process is as follows:

For the sent data: the client software converts the keystrokes and command sequences from the user terminal into NVT format and sends it to the server. The server software converts the received data and commands from NVT format to the format required by the remote system. For the returned data: the remote server converts the data from the format of the remote machine into NVT format, and the local client converts the received NVT format data into the local format.

  1. Send remote command  

We know that most operating systems provide various shortcut keys to implement corresponding control commands. When users type these shortcut keys in the local terminal, the local system will execute the corresponding control commands instead of taking these shortcut keys as input. So what does Telnet use to implement remote transmission of control commands?

Telnet also uses NVT to define how to transmit control functions from the client to the server. We know that the USASCII character set includes 95 printable characters and 33 control codes. When the user types ordinary characters locally, NVT will transmit them according to their original meaning; when the user types shortcut keys (combination keys), NVT will convert them into special ASCII characters and transmit them on the network, and convert them into corresponding control commands after they reach the remote machine. There are two main reasons for distinguishing the normal ASCII character set from the control commands:

This distinction means that Telnet has greater flexibility: it can transmit all possible ASCII characters and all control functions between the client and the server; this distinction allows the client to specify signaling unambiguously without confusing control functions with ordinary characters.  

  1. Data Flow  

Designing Telnet as application-level software has a disadvantage: it is not efficient. Why is this? Here is the data flow in Telnet:

Data information is typed by the user from the local keyboard and transmitted to the client program through the operating system. The client program processes it and returns it to the operating system, which transmits it to the remote machine through the network. The remote operating system transmits the received data to the server program, which is processed again by the server program and returned to the pseudo-terminal entry point on the operating system. Finally, the remote operating system transmits the data to the application program that the user is running. This is a complete input process; the output will be transmitted from the server to the client through the same path.

Because for each input and output, the computer will switch process environments several times, this overhead is very expensive. Fortunately, the user's typing rate is not high, so this disadvantage is still acceptable to us.  

  1. Enforcement Order

We should consider the following situation: suppose the local user runs an endlessly looping error command or program on the remote machine, and this command or program has stopped reading input, then the buffer of the operating system may be filled up. If so, the remote server can no longer write data to the pseudo terminal, and eventually stops reading data from the TCP connection. The buffer of the TCP connection will eventually be filled up, thus preventing the flow of data into this connection. If the above situation really happens, the local user will lose control of the remote machine.

To solve this problem, the Telnet protocol must use out-of-band signaling to force the server to read a control command . We know that TCP uses the urgent data mechanism to implement out-of-band data signaling, so Telnet only needs to add a reserved octet called a date mark and notify the server by having TCP send a segment with the urgent data bit set. The segment carrying the urgent data will bypass flow control and reach the server directly. In response to the urgent signaling, the server will read and discard all data until it finds a date mark. The server will return to normal processing after encountering the date mark.

  1. Option Negotiation  

Due to the heterogeneity of the machines and operating systems at both ends of Telnet, Telnet cannot and should not strictly stipulate the detailed configuration of each Telnet connection, otherwise it will greatly affect Telnet's adaptability to heterogeneity. Therefore, Telnet uses the option negotiation mechanism to solve this problem.

Telnet options cover a wide range of areas: some options extend the functionality of the general idea, while others control minor details. For example, there is an option that controls whether Telnet operates in half-duplex or full-duplex mode (the general idea); another option allows the server on the remote machine to determine the type of terminal the user is using (the minor details).

The way Telnet negotiates options is also very interesting. It handles each option symmetrically, that is, either end can issue a negotiation request; either end can accept or reject the request. In addition, if one end attempts to negotiate an option that the other end does not understand, the end that accepts the request can simply refuse the negotiation. Therefore, it is possible to interoperate newer, more complex Telnet client and server versions with older, less complex versions. If both the client and server understand the new options, the interaction may be improved. Otherwise, they will both switch to a less efficient but workable mode. All of these designs are to enhance adaptability to heterogeneity, which shows how important Telnet's adaptability to heterogeneity is to its application and development. 

The main body of the Telnet protocol consists of three parts:

Definition of Network Virtual Terminal (NVT); Definition of operation negotiation; Negotiation finite automaton;

2.1.1. NVT Working Principle

As the name implies, a Network Virtual Terminal (NVT) is a virtual terminal device used by clients and servers to establish consistency in data representation and interpretation.

2.1.2. Definition of NVT

  1. Composition of NVT

The network virtual terminal NVT consists of two parts:

Output device: output remote data, usually a display Input device: local data input

  1. Data format transmitted on NVT

The data transmitted on the network virtual terminal NVT uses 8-bit byte data, where the byte with the highest bit of 0 is used for general data and the byte with the highest bit of 1 is used for NVT commands.

  1. Use of NVT in TELNET

TELNET uses a symmetrical data representation. When each client sends data, it maps the character representation of its local terminal to the character representation of NVT. When receiving data, it maps the representation of NVT to the local character set.

At the beginning of communication, both parties support a basic subset of NVT terminal features (which can only distinguish between data and commands) in order to communicate at the lowest level. On this basis, the two parties negotiate NVT commands to determine the higher-level features of NVT and expand the NVT functions.

In TELNET, there are a large number of sub-protocols used to negotiate and expand the functions of the basic network virtual terminal NVT. Due to the diversity of terminal types, the TELNET protocol family has become huge.

2.2.1. Why negotiate operation options?

After the network virtual terminal device is defined, the communicating parties can achieve data communication at a lower level. However, the features of the basic NVT device are very limited. It can only receive and display 7-bit ASCII codes and does not have the most basic editing capabilities. Therefore, a simple NVT device has no practical application significance. For this reason, the TELNET protocol defines a family of protocols to extend the functions of the basic NVT, with the aim of enabling NVT to maximize the functions of the user terminal.

In order to support a variety of terminal features, the TELNET protocol stipulates that a negotiation mechanism should be used when extending the NVT function. Only features that the communicating parties agree on through negotiation can be used and the NVT can be given the feature. This can support the interconnection of terminal devices with different terminal features and ensure that they work within their own capabilities.

2.2.2. Operation Negotiation Command Format

TELNET operation negotiation uses NVT commands, which are byte streams with the highest bit being 1. Each NVT command starts with the byte IAC (0xFF). The principle is as follows:

Whenever a client or server wants to send a command sequence instead of a data stream, it inserts a special reserved character in the data stream. This reserved character is called the "Interpret As Command" (IAC) character. When the receiver finds the IAC character in an incoming data stream, it processes the subsequent bytes as a command sequence. The following is a list of all Telnet NVT commands, which are rarely used.

Table 1 TELNET commands

table.png

The commonly used TELNET option negotiations are as follows:

WILL (option code) 251 indicates the option to start execution or confirm the option to operate the instruction. WON'T (option code) 252 indicates the option to refuse to execute or continue the instruction. DO (option code) 253 indicates the option to ask the other party to execute or confirm the option to ask the other party to execute the instruction. DON'T (option code) 254 indicates the option to ask the other party to stop executing or confirm the option to ask the other party to stop executing the instruction. Then there are the following combinations for the receiver and the sender:

Table 2 Six cases of TELNET option negotiation

table.png

The sender wants the other party to invalidate an option, and the receiver must accept the request.

Option negotiation requires 3 bytes: IAC, followed by WILL, DO, WONT or DONT; the last identification byte is used to indicate the option of the operation. Commonly used option codes are as follows:

Table 3 TELNET option codes

table.png

Normally, the client sends characters to the server and the server echoes them to the user's terminal. However, if network delays cause echoing to be too slow, the user may prefer to have the local system echo the characters. Before the client allows the local system to echo, it sends the following sequence to the server:

IAC DONT ECHO

After receiving the request, the server sends a 3-character response:

IAC WONT ECHO

Indicates that the server has agreed to turn off the echo as requested.

In addition to "on" or "off", some options require more information. For example, to specify the terminal type, the client must send a string to identify the terminal type, so sub-option negotiation must be defined.

RFC 1091 defines the suboption negotiation of terminal type. For example:

The client sends the byte sequence to request that an option be turned on:

<IAC, WILL, 24>

24 is the option identifier for the terminal type. If the server agrees to the request, the response is:

<IAC, DO, 24>

The server then sends

<IAC,SB,24,1,IAC,SE> Requests the client to give its terminal type.

SB is the suboption start command. The next byte 24 indicates that this suboption is a terminal type option. The next byte 1 indicates: Send your terminal type. The client's response is:

<IAC,SB,24,0,'I','B','M','P','C',IAC,SE>

The fourth byte 0 means "my terminal type is".

The entire protocol software is divided into three modules, and the functions of each module are as follows:

  1. Input/output module with local users: handles user input/output;

  2. Input/output module with remote system: handle input/output with remote system;

  3. TELNET protocol module: implements TELNET protocol and maintains the protocol state machine.

The telnet client does two things:

Read the characters typed by the user on the keyboard and send them to the remote server through the TCP connection. Read the characters received from the TCP connection and display them on the user's terminal.

studio.png
studio.png
  1. You can add the following code in main.c

extern void wlan_autoconnect_init(void); 

rt_wlan_config_autoreconnect(RT_TURE);

rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT);copymistakeCopy Success
  1. Projectspackagesnetutils - v1.3.1 -> telnet -> telnet.c

/*
 * File      : telnet.c
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2006-2018, RT-Thread Development Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Change Logs:
 * Date           Author       Notes
 * 2012-04-01     Bernard      first version
 * 2018-01-25     armink       Fix it on RT-Thread 3.0+
 */
#include <rtthread.h>
#include <rtdevice.h>

#ifdef PKG_NETUTILS_TELNET
#if defined(RT_USING_DFS_NET) || defined(SAL_USING_POSIX)
#include <sys/socket.h>
#else
#include <lwip/sockets.h>
#endif /* SAL_USING_POSIX */

#if defined(RT_USING_POSIX)
#include <dfs_posix.h>
#include <dfs_poll.h>
#include <libc.h>
static int dev_old_flag;
#endif

#include <finsh.h>
#include <msh.h>
#include <shell.h>

#define TELNET_PORT         23
#define TELNET_BACKLOG      5
#define RX_BUFFER_SIZE      256
#define TX_BUFFER_SIZE      4096

#define ISO_nl              0x0a
#define ISO_cr              0x0d

#define STATE_NORMAL        0
#define STATE_IAC           1
#define STATE_WILL          2
#define STATE_WONT          3
#define STATE_DO            4
#define STATE_DONT          5
#define STATE_CLOSE         6

#define TELNET_IAC          255
#define TELNET_WILL         251
#define TELNET_WONT         252
#define TELNET_DO           253
#define TELNET_DONT         254

struct telnet_session
{
    struct rt_ringbuffer rx_ringbuffer;
    struct rt_ringbuffer tx_ringbuffer;

    rt_mutex_t rx_ringbuffer_lock;
    rt_mutex_t tx_ringbuffer_lock;

    struct rt_device device;
    rt_int32_t server_fd;
    rt_int32_t client_fd;

    /* telnet protocol */
    rt_uint8_t state;
    rt_uint8_t echo_mode;

    rt_sem_t read_notice;
};

static struct telnet_session* telnet;

/* process tx data */
static void send_to_client(struct telnet_session* telnet)
{
    rt_size_t length;
    rt_uint8_t tx_buffer[32];

    while (1)
    {
        rt_memset(tx_buffer, 0, sizeof(tx_buffer));
        rt_mutex_take(telnet->tx_ringbuffer_lock, RT_WAITING_FOREVER);
        /* get buffer from ringbuffer */
        length = rt_ringbuffer_get(&(telnet->tx_ringbuffer), tx_buffer, sizeof(tx_buffer));
        rt_mutex_release(telnet->tx_ringbuffer_lock);

        /* do a tx procedure */
        if (length > 0)
        {
            send(telnet->client_fd, tx_buffer, length, 0);
        }
        else break;
    }
}

/* send telnet option to remote */
static void send_option_to_client(struct telnet_session* telnet, rt_uint8_t option, rt_uint8_t value)
{
    rt_uint8_t optbuf[4];

    optbuf[0] = TELNET_IAC;
    optbuf[1] = option;
    optbuf[2] = value;
    optbuf[3] = 0;

    rt_mutex_take(telnet->tx_ringbuffer_lock, RT_WAITING_FOREVER);
    rt_ringbuffer_put(&telnet->tx_ringbuffer, optbuf, 3);
    rt_mutex_release(telnet->tx_ringbuffer_lock);

    send_to_client(telnet);
}

/* process rx data */
static void process_rx(struct telnet_session* telnet, rt_uint8_t *data, rt_size_t length)
{
    rt_size_t index;

    for (index = 0; index < length; index++)
    {
        switch (telnet->state)
        {
        case STATE_IAC:
            if (*data == TELNET_IAC)
            {
                rt_mutex_take(telnet->rx_ringbuffer_lock, RT_WAITING_FOREVER);
                /* put buffer to ringbuffer */
                rt_ringbuffer_putchar(&(telnet->rx_ringbuffer), *data);
                rt_mutex_release(telnet->rx_ringbuffer_lock);

                telnet->state = STATE_NORMAL;
            }
            else
            {
                /* set telnet state according to received package */
                switch (*data)
                {
                case TELNET_WILL:
                    telnet->state = STATE_WILL;
                    break;
                case TELNET_WONT:
                    telnet->state = STATE_WONT;
                    break;
                case TELNET_DO:
                    telnet->state = STATE_DO;
                    break;
                case TELNET_DONT:
                    telnet->state = STATE_DONT;
                    break;
                default:
                    telnet->state = STATE_NORMAL;
                    break;
                }
            }
            break;

            /* don't option */
        case STATE_WILL:
        case STATE_WONT:
            send_option_to_client(telnet, TELNET_DONT, *data);
            telnet->state = STATE_NORMAL;
            break;

            /* won't option */
        case STATE_DO:
        case STATE_DONT:
            send_option_to_client(telnet, TELNET_WONT, *data);
            telnet->state = STATE_NORMAL;
            break;

        case STATE_NORMAL:
            if (*data == TELNET_IAC)
            {
                telnet->state = STATE_IAC;
            }
            else if (*data != '\r') /* ignore '\r' */
            {
                rt_mutex_take(telnet->rx_ringbuffer_lock, RT_WAITING_FOREVER);
                /* put buffer to ringbuffer */
                rt_ringbuffer_putchar(&(telnet->rx_ringbuffer), *data);
                rt_mutex_release(telnet->rx_ringbuffer_lock);
                rt_sem_release(telnet->read_notice);
            }
            break;
        }
        data++;
    }


#if !defined(RT_USING_POSIX)
    rt_size_t rx_length;
    rt_mutex_take(telnet->rx_ringbuffer_lock, RT_WAITING_FOREVER);
    /* get total size */
    rx_length = rt_ringbuffer_data_len(&telnet->rx_ringbuffer);
    rt_mutex_release(telnet->rx_ringbuffer_lock);

    /* indicate there are reception data */
    if ((rx_length > 0) && (telnet->device.rx_indicate != RT_NULL))
    {
        telnet->device.rx_indicate(&telnet->device, rx_length);
    }
#endif

    return;
}

/* client close */
static void client_close(struct telnet_session* telnet)
{
    /* set console */
    rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
    /* set finsh device */
#if defined(RT_USING_POSIX)
    ioctl(libc_stdio_get_console(), F_SETFL, (void *) dev_old_flag);
    libc_stdio_set_console(RT_CONSOLE_DEVICE_NAME, O_RDWR);
#else
    finsh_set_device(RT_CONSOLE_DEVICE_NAME);
#endif /* RT_USING_POSIX */

    rt_sem_release(telnet->read_notice);

    /* close connection */
    closesocket(telnet->client_fd);

    /* restore shell option */
    finsh_set_echo(telnet->echo_mode);

    rt_kprintf("telnet: resume console to %s\n", RT_CONSOLE_DEVICE_NAME);
}

/* RT-Thread Device Driver Interface */
static rt_err_t telnet_init(rt_device_t dev)
{
    return RT_EOK;
}

static rt_err_t telnet_open(rt_device_t dev, rt_uint16_t oflag)
{
    return RT_EOK;
}

static rt_err_t telnet_close(rt_device_t dev)
{
    return RT_EOK;
}

static rt_size_t telnet_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
{
    rt_size_t result;

    rt_sem_take(telnet->read_notice, RT_WAITING_FOREVER);

    /* read from rx ring buffer */
    rt_mutex_take(telnet->rx_ringbuffer_lock, RT_WAITING_FOREVER);
    result = rt_ringbuffer_get(&(telnet->rx_ringbuffer), buffer, size);
    if (result == 0)
    {
        /**
         * MUST return unless **1** byte for support sync read data.
         * It will return empty string when read no data
         */
        *(char *) buffer = '\0';
        result = 1;
    }
    rt_mutex_release(telnet->rx_ringbuffer_lock);

    return result;
}

static rt_size_t telnet_write (rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)
{
    const rt_uint8_t *ptr;

    ptr = (rt_uint8_t*) buffer;

    rt_mutex_take(telnet->tx_ringbuffer_lock, RT_WAITING_FOREVER);
    while (size)
    {
        if (*ptr == '\n')
            rt_ringbuffer_putchar(&telnet->tx_ringbuffer, '\r');

        if (rt_ringbuffer_putchar(&telnet->tx_ringbuffer, *ptr) == 0) /* overflow */
            break;
        ptr++;
        size--;
    }
    rt_mutex_release(telnet->tx_ringbuffer_lock);

    /* send data to telnet client */
    send_to_client(telnet);

    return (rt_uint32_t) ptr - (rt_uint32_t) buffer;
}

static rt_err_t telnet_control(rt_device_t dev, int cmd, void *args)
{
    return RT_EOK;
}

#ifdef RT_USING_DEVICE_OPS
    static struct rt_device_ops _ops = {
        telnet_init,
        telnet_open,
        telnet_close,
        telnet_read,
        telnet_write,
        telnet_control
    };
#endif
/* telnet server thread entry */
static void telnet_thread(void* parameter)
{
#define RECV_BUF_LEN 64

    struct sockaddr_in addr;
    socklen_t addr_size;
    rt_uint8_t recv_buf[RECV_BUF_LEN];
    rt_int32_t recv_len = 0;

    if ((telnet->server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        rt_kprintf("telnet: create socket failed\n");
        return;
    }

    addr.sin_family = AF_INET;
    addr.sin_port = htons(TELNET_PORT);
    addr.sin_addr.s_addr = INADDR_ANY;
    rt_memset(&(addr.sin_zero), 0, sizeof(addr.sin_zero));
    if (bind(telnet->server_fd, (struct sockaddr *) &addr, sizeof(struct sockaddr)) == -1)
    {
        rt_kprintf("telnet: bind socket failed\n");
        return;
    }

    if (listen(telnet->server_fd, TELNET_BACKLOG) == -1)
    {
        rt_kprintf("telnet: listen socket failed\n");
        return;
    }

    /* register telnet device */
    telnet->device.type     = RT_Device_Class_Char;
#ifdef RT_USING_DEVICE_OPS
    telnet->device.ops = &_ops;
#else
    telnet->device.init     = telnet_init;
    telnet->device.open     = telnet_open;
    telnet->device.close    = telnet_close;
    telnet->device.read     = telnet_read;
    telnet->device.write    = telnet_write;
    telnet->device.control  = telnet_control;
#endif

    /* no private */
    telnet->device.user_data = RT_NULL;

    /* register telnet device */
    rt_device_register(&telnet->device, "telnet", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STREAM);

    while (1)
    {
        rt_kprintf("telnet: waiting for connection\n");

        /* grab new connection */
        if ((telnet->client_fd = accept(telnet->server_fd, (struct sockaddr *) &addr, &addr_size)) == -1)
        {
            continue;
        }

        rt_kprintf("telnet: new telnet client(%s:%d) connection, switch console to telnet...\n", inet_ntoa(addr.sin_addr), addr.sin_port);

        /* process the new connection */
        /* set console */
        rt_console_set_device("telnet");
        /* set finsh device */
#if defined(RT_USING_POSIX)
        /* backup flag */
        dev_old_flag = ioctl(libc_stdio_get_console(), F_GETFL, (void *) RT_NULL);
        /* add non-block flag */
        ioctl(libc_stdio_get_console(), F_SETFL, (void *) (dev_old_flag | O_NONBLOCK));
        /* set tcp shell device for console */
        libc_stdio_set_console("telnet", O_RDWR);
        /* resume finsh thread, make sure it will unblock from last device receive */
        rt_thread_t tid = rt_thread_find(FINSH_THREAD_NAME);
        if (tid)
        {
            rt_thread_resume(tid);
            rt_schedule();
        }
#else
        /* set finsh device */
        finsh_set_device("telnet");
#endif /* RT_USING_POSIX */

        /* set init state */
        telnet->state = STATE_NORMAL;

        telnet->echo_mode = finsh_get_echo();
        /* disable echo mode */
        finsh_set_echo(0);
        /* output RT-Thread version and shell prompt */
#ifdef FINSH_USING_MSH
        msh_exec("version", strlen("version"));
#endif
        rt_kprintf(FINSH_PROMPT);

        while (1)
        {
            /* try to send all data in tx ringbuffer */
            send_to_client(telnet);

            /* do a rx procedure */
            if ((recv_len = recv(telnet->client_fd, recv_buf, RECV_BUF_LEN, 0)) > 0)
            {
                process_rx(telnet, recv_buf, recv_len);
            }
            else
            {
                /* close connection */
                client_close(telnet);
                break;
            }
        }
    }
}

/* telnet server */
void telnet_server(void)
{
    rt_thread_t tid;

    if (telnet == RT_NULL)
    {
        rt_uint8_t *ptr;

        telnet = rt_malloc(sizeof(struct telnet_session));
        if (telnet == RT_NULL)
        {
            rt_kprintf("telnet: no memory\n");
            return;
        }
        /* init ringbuffer */
        ptr = rt_malloc(RX_BUFFER_SIZE);
        if (ptr)
        {
            rt_ringbuffer_init(&telnet->rx_ringbuffer, ptr, RX_BUFFER_SIZE);
        }
        else
        {
            rt_kprintf("telnet: no memory\n");
            return;
        }
        ptr = rt_malloc(TX_BUFFER_SIZE);
        if (ptr)
        {
            rt_ringbuffer_init(&telnet->tx_ringbuffer, ptr, TX_BUFFER_SIZE);
        }
        else
        {
            rt_kprintf("telnet: no memory\n");
            return;
        }
        /* create tx ringbuffer lock */
        telnet->tx_ringbuffer_lock = rt_mutex_create("telnet_tx", RT_IPC_FLAG_FIFO);
        /* create rx ringbuffer lock */
        telnet->rx_ringbuffer_lock = rt_mutex_create("telnet_rx", RT_IPC_FLAG_FIFO);

        telnet->read_notice = rt_sem_create("telnet_rx", 0, RT_IPC_FLAG_FIFO);

        tid = rt_thread_create("telnet", telnet_thread, RT_NULL, 2048, 25, 5);
        if (tid != RT_NULL)
        {
            rt_thread_startup(tid);
            rt_kprintf("Telnet server start successfully\n");
        }
    }
    else
    {
        rt_kprintf("telnet: server already running\n");
    }

}

#ifdef RT_USING_FINSH
#include <finsh.h>
FINSH_FUNCTION_EXPORT(telnet_server, startup telnet server);
#ifdef FINSH_USING_MSH
MSH_CMD_EXPORT(telnet_server, startup telnet server)
#endif /* FINSH_USING_MSH */
#endif /* RT_USING_FINSH */
#endif /* PKG_NETUTILS_TELNET */copymistakeCopy Success

The main function in the code is void telnet_server(void)

Successfully connected to the telnet server and started running.

Last updated

Assoc. Prof. Wiroon Sriborrirux, Founder of Advance Innovation Center (AIC) and Bangsaen Design House (BDH), Electrical Engineering Department, Faculty of Engineering, Burapha University