一、网络编程初体验

1.客户端

客户端的流程如下:socket()→connect()→recv()→send()→close()

下面是一个简单的echo代码。

1)由于buff限制,recv函数一次无法完全接收完所有数据,可以判断返回值。循环调用recv进行接收。

2)如果recv的返回值为0,说明连接被断开。

#include <FreeRTOS.h>
#include <task.h>
#include "sys/types.h"
#include "sys/socket.h"
#include "arpa/inet.h"
#include "stdio.h"
#include "unistd.h"
#include "string.h"

int tcp_client_test(void)
{
    int sock = 0;
    struct sockaddr_in addr = { 0 };
    int len = 0;
    int r = 0;
    char buf[128] = { 0 };
    sock = socket(PF_INET, SOCK_STREAM, 0);

    if (sock == -1) {
        log_e("socket error");
        return -1;
    }
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("192.168.1.10");
    addr.sin_port = htons(8888);

    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
        log_e("connect error");
        return -1;
    }
    log_i("connect success");

    do {
        r = recv(sock, buf, sizeof(buf), 0);
        send(sock, buf, r, 0);
        log_hex_dump("tcp", 32, buf, r);
    } while (r != 0);

    close(sock);
    return 0;
}

2.服务端

服务端需要先用一个socket获取连接的socket,再用获取的socket去进行实际的通讯。

1)服务端socket只用于接收连接不进行实际通信。

2)绑定(bind) socket之后用listen进入阻塞状态,监听是否有连接。

3)监听到有连接之后,accept()函数返回与客户端通信的socket。这个才是实际进行通讯的socket。

#include <FreeRTOS.h>
#include <task.h>
#include "sys/types.h"
#include "sys/socket.h"
#include "arpa/inet.h"
#include "stdio.h"
#include "unistd.h"
#include "string.h"

/**
 * @brief 1.服务端socket只用于接收连接不进行实际通信.
 * 2.accept()函数返回与客户端通信的socket.
 *
 * socket究竟是什么?
 * @return {*}
 */
int tcp_server_test(void)
{
    int ret = -1;
    int server = 0;
    int client = 0;
    struct sockaddr_in saddr = { 0 };
    struct sockaddr_in caddr = { 0 };
    socklen_t asize = 0;
    int len = 0;
    char buf[32] = { 0 };
    int r = 0;
    do {
        /* 1.新建socket */
        server = socket(PF_INET, SOCK_STREAM, 0);
        if (server == -1) {
            log_e("socket error");
            break;
        }
        saddr.sin_family = AF_INET;
        saddr.sin_addr.s_addr = htonl(INADDR_ANY);
        saddr.sin_port = htons(8899);
        /* 2.绑定 bind(sock,*addr, addrlen) */
        if (bind(server, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
            log_e("bind error");
            break;
        }
        /* 3.监听 listen(sock, backlog) 第二个参数是监听队列大小 */
        if (listen(server, 1) == -1) {
        }
        log_i("server start success!");
        /* 4.接受请求 accept(sock,*addr, addrlen) */
        asize = sizeof(caddr);
        client = accept(server, (struct sockaddr *)&caddr, &asize);
        if (client == -1) {
            log_e("accept error");
            break;
        }
        log_i("client:%d connect success!", client);
        len = 0;
        do {
            r = recv(client, buf, sizeof(buf), 0);
            log_hex_dump("client", 32, buf, r);
            if (r > 0) {
                len += r;
            }
        } while (len < 64);
        send(client, "Hello World!", 12, 0);

        vTaskDelay(1000 / portTICK_PERIOD_MS);
        close(server);
        close(client);
    } while (0);

    return ret;
}

3.socket究竟是什么?

socket是一个多功能的函数,返回值是资源标识符。

close函数释放资源。

4.字节序

#include "arpa/inet.h" 头文件中有很多处理地址的函数,我以前不知道,傻傻的自己处理

htonlhtons函数将本机字节序转换为网络字节序。

inet_addr 将IP字符串转换成符合网络字节序的整数。