物联网LWIP网络开发3

D3Socket编程

3.1Socket基本概念

Socket基本概念

一、定义与功能
  • 定义:Socket是一种通信协议,用于在网络中实现进程间的通信。它是一种抽象的编程接口,允许应用程序通过网络发送和接收数据。Socket可以被视为网络通信的端点,它在网络上标识了一个通信链路的两端,并提供了通信双方所需的接口和功能。
  • 功能:Socket允许应用程序通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。它允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。
二、类型与特点

Socket主要有两种类型:流式套接字(Stream Socket)和数据报套接字(Datagram Socket)。

  • 流式套接字(TCP Socket):基于TCP协议,提供面向连接、可靠的数据传输服务。数据在传输过程中会被分成多个数据包,按照顺序发送和接收。TCP协议通过三次握手建立连接,确保数据的可靠性和顺序性。
  • 数据报套接字(UDP Socket):基于UDP协议,提供无连接、不可靠的数据传输服务。数据以数据报的形式发送,不保证数据包的顺序和完整性。UDP协议适用于一些实时性要求高、允许一定数据丢失的应用场景。
三、套接字地址与标识

套接字地址由两部分组成:IP地址和端口号。IP地址用于标识网络中的设备,而端口号用于标识设备上的特定应用程序。一个套接字地址可以唯一地标识网络中的一个通信实体。

四、通信过程

Socket在通信过程中会经历不同的状态,如CLOSED、LISTEN、SYN-SENT、SYN-RECEIVED、ESTABLISHED、FIN-WAIT-1、FIN-WAIT-2、CLOSE-WAIT、CLOSING、LAST-ACK、TIME-WAIT等。这些状态描述了Socket在建立连接、传输数据和关闭连接过程中的状态变化。

  • 建立连接:对于流式套接字,连接的建立需要经过三次握手过程。客户端首先发送一个SYN包给服务器,服务器收到后回复一个SYN-ACK包,客户端再次发送一个ACK包,完成连接建立。
  • 数据传输:连接建立后,双方就可以通过Socket发送和接收数据。对于流式套接字,数据会按照顺序传输;而对于数据报套接字,则不保证数据的顺序和完整性。
  • 关闭连接:连接的关闭需要经过四次挥手过程。任一方都可以发起关闭请求,发送一个FIN包,对方收到后回复一个ACK包,然后进入半关闭状态。当数据传输完成后,另一方发送FIN包,对方回复ACK包,完成连接关闭。
五、编程接口

套接字API是一组用于创建、配置和管理Socket的函数。常见的函数包括socket()(创建一个新的套接字)、bind()(将套接字绑定到一个特定的IP地址和端口号)、listen()(使套接字进入监听状态,等待客户端连接请求)、accept()(接受客户端的连接请求,创建一个新的套接字用于与客户端通信)、connect()(向服务器发起连接请求)、send()和recv()(发送和接收数据)、close()(关闭套接字)等。

六、应用场景

Socket广泛应用于各种网络编程场景,如Web服务器与客户端之间的HTTP通信、文件传输应用程序(如FTP)的文件上传和下载、即时通讯软件的消息传输等。Web服务器通常使用流式套接字与客户端建立HTTP连接,接收客户端的请求并发送响应数据。文件传输应用程序可以根据需要选择流式套接字或数据报套接字来实现文件的上传和下载。

综上所述,Socket是计算机网络编程中不可或缺的一部分,它提供了一种标准的通信方式,使得不同的程序能够在网络上进行高效、可靠的数据交换。

LWIP怎么实现的socket

3.2 Socket编程预备知识1

大端模式与小端模式

  1. 大端模式(Big-Endian)
    • 在大端模式下,数据的高位字节保存在内存的低地址中,而数据的低位字节保存在内存的高地址中。
    • 这种存储模式类似于我们阅读和书写的方式,从左到右,高位在前,低位在后。
    • 大端模式也被称为网络字节序,因为在网络传输中常使用大端模式以确保数据在不同机器间的兼容性。
  2. 小端模式(Little-Endian)
    • 在小端模式下,数据的低位字节保存在内存的低地址中,而数据的高位字节保存在内存的高地址中。
    • 这种存储模式与我们的阅读顺序相反,先看到的是低位,后看到的是高位。
    • 小端模式在计算机内部处理中较为常见,因为它符合计算机从低地址开始处理数据的习惯。

网络字节序

地址转换接口
//def.h
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

htonl (Host to Network Long)是一个在网络编程中经常使用的函数,它属于标准库函数,用于长整型的将主机字节顺序(Host Byte Order)转换为网络字节顺序(Network Byte Order)。

 htons(Host to Network Short):短整型的将主机字节序转换为网络字节序(发送数据前)

ntohl(Network to Host Long),ntohs(Network to Host Short)分别用于长,短整型的主机到网络、网络到主机的字节顺序转换。(接收数据后)

ip地址转换(重点)

socket接口
//sockets.h
int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

  inet_pton

作用:人类可读的人类可读的ip地址(文本形式)(十进制)通过该接口直接转换为网络字节序的IP(v4或v6)地址(二进制)

  • af:地址族(Address Family),指定源地址字符串src的格式。常用的值有AF_INET(表示IPv4地址)和AF_INET6(表示IPv6地址)。
  • src:指向源地址字符串的指针,即需要转换的人类可读的IP地址文本。
  • dst:指向缓冲区的指针,用于存储转换后的二进制形式的网络地址。调用者需要确保该缓冲区足够大,以容纳转换后的地址。

inet_ntop

作用:是将网络字节序的IP(v4或v6)地址转换为人类可读的IP地址。

LWIP接口
//inet.h
#define inet_addr(cp)                   ipaddr_addr(cp)
#define inet_aton(cp, addr)             ip4addr_aton(cp, (ip4_addr_t*)addr)
#define inet_ntoa(addr)                 ip4addr_ntoa((const ip4_addr_t*)&(addr))
#define inet_ntoa_r(addr, buf, buflen)  ip4addr_ntoa_r((const ip4_addr_t*)&(addr), buf, buflen)

 inet_addr 函数用于将点分十进制的IPv4地址字符串转换为网络字节序的二进制值。这个函数只能处理IPv4地址,不能处理IPv6地址。

3.3Socket编程预备知识2

sockaddr数据结构

sockaddr

struct sockaddr {
  u8_t        sa_len;
  sa_family_t sa_family;
  char        sa_data[14];
};

 长度,IPv4orIPv6

  sockaddr_in

typedef u32_t socklen_t;
struct sockaddr_in {
  u8_t            sin_len;
  sa_family_t     sin_family;
  in_port_t       sin_port;
  struct in_addr  sin_addr;
#define SIN_ZERO_LEN 8
  char            sin_zero[SIN_ZERO_LEN];
};

  sockaddr_in6

struct sockaddr_in6 {
  u8_t            sin6_len;      /* length of this structure    */
  sa_family_t     sin6_family;   /* AF_INET6                    */
  in_port_t       sin6_port;     /* Transport layer port #      */
  u32_t           sin6_flowinfo; /* IPv6 flow information       */
  struct in6_addr sin6_addr;     /* IPv6 address                */
  u32_t           sin6_scope_id; /* Set of interfaces for scope */
};

socklen_t

typedef u32_t socklen_t;

socket编程模型

 3.4Socket接口函数分析上(了解即可)

socket

1创建Socket:使用socket()函数创建Socket

bind

2绑定Socket:使用bind()函数将Socket与IP地址和端口号绑定。

listen

3监听连接:对于服务器,使用listen()函数监听来自客户端的连接请求。

accept

4监听连接:对于服务器,使用listen()函数监听来自客户端的连接请求。

connect

5发送和接收数据:使用send()recv()函数在Socket上发送和接收数据。

close

6关闭Socket:使用close()函数关闭Socket,释放资源。

示例代码

以下是一个简单的TCP服务器和客户端的示例代码。

服务器端

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
  
#define PORT 8080  
  
int main() {  
    int server_fd, new_socket;  
    struct sockaddr_in address;  
    int addrlen = sizeof(address);  
    char buffer[1024] = {0};  
    char *hello = "Hello from server";  
  
    // 创建Socket  
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {  
        perror("socket failed");  
        exit(EXIT_FAILURE);  
    }  
  
    // 绑定Socket  
    address.sin_family = AF_INET;  
    address.sin_addr.s_addr = INADDR_ANY;  
    address.sin_port = htons(PORT);  
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {  
        perror("bind failed");  
        exit(EXIT_FAILURE);  
    }  
  
    // 监听连接  
    if (listen(server_fd, 3) < 0) {  
        perror("listen");  
        exit(EXIT_FAILURE);  
    }  
  
    // 接受连接  
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {  
        perror("accept");  
        exit(EXIT_FAILURE);  
    }  
  
    // 读取数据  
    read(new_socket, buffer, 1024);  
    printf("%s\n", buffer);  
  
    // 发送数据  
    send(new_socket, hello, strlen(hello), 0);  
    printf("Hello message sent\n");  
  
    // 关闭Socket  
    close(new_socket);  
    close(server_fd);  
    return 0;  
}

客户端

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <string.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
  
#define PORT 8080  
  
int main() {  
    struct sockaddr_in serv_addr;  
    int sock = 0;  
  
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {  
        printf("\n Socket creation error \n");  
        return -1;  
    }  
  
    serv_addr.sin_family = AF_INET;  
    serv_addr.sin_port = htons(PORT);  
  
    // 将IPv4地址从文本转换为二进制形式  
    if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {  
        printf("\nInvalid address/ Address not supported \n");  
        return -1;  
    }  
  
    // 连接到服务器  
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {  
        printf("\nConnection Failed \n");  
        return -1;  
    }  
  
    // 发送数据  
    send(sock, "Hello, server!", strlen("Hello, server!"), 0);  
    printf("Hello message sent\n");  
  
    // 接收数据  
    char buffer[1024] = {0};  
    int valread = read(sock, buffer, 1024);  
    printf("%s\n", buffer);  
  
    // 关闭Socket  
    close(sock);  
    return 0;  
}

3.5Socket接口函数分析下

write

read

send&&recv

sendto&&recvfrom

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值