Socket API编程模型

1. Socket API

1.1 Socket基础知识

socket通讯原理示意图
LWIP应用开发|Socket API编程模型_udp
LWIP应用开发|Socket API编程模型_lan8720_02
socket网络编程接口示意图(下左图示)以及LWIP中的socket的实现框图(下右图示)
LWIP应用开发|Socket API编程模型_lan8720_03
IP地址转换、IP地址结构以及字节序转换的相关介绍可参考之前的网络编程基础

1.2 Socket API接口函数

为了兼容性,LWIP的socket也提供了标准的socket接口函数,在socket.h文件中可以看到下面的宏定义

//sockets.h
#define accept(s,addr,addrlen)                    lwip_accept(s,addr,addrlen)
/** @ingroup socket */
#define bind(s,name,namelen)                      lwip_bind(s,name,namelen)
/** @ingroup socket */
#define shutdown(s,how)                           lwip_shutdown(s,how)
/** @ingroup socket */
#define getpeername(s,name,namelen)               lwip_getpeername(s,name,namelen)
/** @ingroup socket */
#define getsockname(s,name,namelen)               lwip_getsockname(s,name,namelen)
/** @ingroup socket */
#define setsockopt(s,level,optname,opval,optlen)  lwip_setsockopt(s,level,optname,opval,optlen)
/** @ingroup socket */
#define getsockopt(s,level,optname,opval,optlen)  lwip_getsockopt(s,level,optname,opval,optlen)
/** @ingroup socket */
#define closesocket(s)                            lwip_close(s)
/** @ingroup socket */
#define connect(s,name,namelen)                   lwip_connect(s,name,namelen)
/** @ingroup socket */
#define listen(s,backlog)                         lwip_listen(s,backlog)
/** @ingroup socket */
#define socket(domain,type,protocol)              lwip_socket(domain,type,protocol)
/** @ingroup socket */
#define select(maxfdp1,readset,writeset,exceptset,timeout)     lwip_select(maxfdp1,readset,writeset,exceptset,timeout)
/** @ingroup socket */
#define ioctlsocket(s,cmd,argp)                   lwip_ioctl(s,cmd,argp)
/** @ingroup socket */
#define recv(s,mem,len,flags)                     lwip_recv(s,mem,len,flags)
/** @ingroup socket */
#define recvfrom(s,mem,len,flags,from,fromlen)    lwip_recvfrom(s,mem,len,flags,from,fromlen)
/** @ingroup socket */
#define send(s,dataptr,size,flags)                lwip_send(s,dataptr,size,flags)
/** @ingroup socket */
#define sendmsg(s,message,flags)                  lwip_sendmsg(s,message,flags)
/** @ingroup socket */
#define sendto(s,dataptr,size,flags,to,tolen)     lwip_sendto(s,dataptr,size,flags,to,tolen)
//#if LWIP_POSIX_SOCKETS_IO_NAMES
/** @ingroup socket */
#define read(s,mem,len)                           lwip_read(s,mem,len)
/** @ingroup socket */
#define write(s,dataptr,len)                      lwip_write(s,dataptr,len)
/** @ingroup socket */
#define writev(s,iov,iovcnt)                      lwip_writev(s,iov,iovcnt)
/** @ingroup socket */
#define close(s)                                  lwip_close(s)
/** @ingroup socket */
#define fcntl(s,cmd,val)                          lwip_fcntl(s,cmd,val)
/** @ingroup socket */
#define ioctl(s,cmd,argp)                         lwip_ioctl(s,cmd,argp)

常用的几种socket编程基本函数的详细介绍,可参考socket编程一文

2. TCP编程模型

TCP编程CS模型流程图如下图示
LWIP应用开发|Socket API编程模型_socket_04

2.1 TCP Server端实例

要实现的功能为:将从PC客户端输入的小写字母转换为大写字母

PC_Client STM32_Server acbde 小写转大写 ABCDE PC_Client STM32_Server
  • 参考带操作系统移植LWIP将LWIP移植到F4开发板中,并能够ping通开发板

  • 在工程中创建socket_tcp_server.c和socket_tcp_server.h两个文件

/******socket_tcp_server.c******/
#include "socket_tcp_server.h"
#include "lwip/sockets.h"
#include "ctype.h"
char ReadBuff[BUFF_SIZE];
//TCP服务器任务
void vTcpServerTask(void){
  int sfd, cfd, n, i;
  struct sockaddr_in server_addr, client_addr;
  socklen_t	client_addr_len;
  //创建socket
  sfd = socket(AF_INET, SOCK_STREAM, 0);
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(SERVER_PORT);
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  //绑定socket
  bind(sfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
  //监听socket
  listen(sfd, 5);
  //等待客户端连接
  client_addr_len = sizeof(client_addr);
  cfd = accept(sfd, (struct sockaddr *)&client_addr, &client_addr_len);
  printf("client is connect cfd = %d\r\n",cfd);
  while(1){
	//等待客户端发送数据
	n = read(cfd, ReadBuff, BUFF_SIZE);
	//进行大小写转换
	for(i = 0; i < n; i++){
	  ReadBuff[i] = toupper(ReadBuff[i]);		
	}
	//写回客户端
	write(cfd, ReadBuff, n);
  }
}

/******socket_tcp_server.h******/
#ifndef _SOCKET_TCP_SERVER_H
#define _SOCKET_TCP_SERVER_H
#define SERVER_IP	"192.168.1.20" //客户端编程时需要用到该服务器地址
#define SERVER_PORT	  6666
#define BUFF_SIZE	  1024

void vTcpServerTask(void);

#endif 
  • 在freertos.c文件中的默认任务里面添加代码
void StartDefaultTask(void const * argument){
  /* init code for LWIP */
  MX_LWIP_Init();
  /* USER CODE BEGIN StartDefaultTask */
  printf("TCP server started!\r\n",cfd);
  /* Infinite loop */
  for(;;){
    vTcpServerTask();
	osDelay(100);
  }
  /* USER CODE END StartDefaultTask */
}
  • 编译无误下载到开发板后,打开串口助手可以看到相关调试信息,使用网络调试工具创建一个PC客户端,连接相应IP和端口后,输入任意小写字母,Server将返回对应的大写字母

LWIP应用开发|Socket API编程模型_socket_05

LWIP应用开发|Socket API编程模型_tcp_06

2.2 TCP Client端实例

要实现的功能为:将从PC服务端输入的小写字母转换为大写字母

PC_Server STM32_Client acbde 小写转大写 ABCDE PC_Server STM32_Client
  • 参考带操作系统移植LWIP将LWIP移植到F4开发板中,并能够ping通开发板

  • 在工程中创建socket_tcp_client.c和socket_tcp_client.h两个文件

/******socket_tcp_client.c******/
#include "socket_tcp_server.h"
#include "socket_tcp_client.h"
#include "lwip/sockets.h"
#include "ctype.h"

static char ReadBuff[BUFF_SIZE];

void vTcpClientTask(void){
  int cfd, n, i;
  struct sockaddr_in server_addr;	
  //创建socket
  cfd = socket(AF_INET, SOCK_STREAM, 0);
  //填充地址信息
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(SERVER_PORT);
  server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);//即PC端地址
  //连接到服务器
  connect(cfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
  printf("server is connect ok\r\n");
  while(1){
	//等待服务器发送数据
	n = read(cfd, ReadBuff, BUFF_SIZE);
	//进行大小写转换
	for(i = 0; i < n; i++){		
	  ReadBuff[i] = toupper(ReadBuff[i]);		
	}
	//写回服务器
	write(cfd, ReadBuff, n);
  }
}

/******socket_tcp_client.h******/
#ifndef _SOCKET_TCP_CLIENT_H
#define _SOCKET_TCP_CLIENT_H

void vTcpClientTask(void);

#endif
  • 在freertos.c文件中的默认任务里面添加代码
void StartDefaultTask(void const * argument){
  /* init code for LWIP */
  MX_LWIP_Init();
  /* USER CODE BEGIN StartDefaultTask */
  printf("TCP server started!\r\n",cfd);
  /* Infinite loop */
  for(;;){
    vTcpClientTask();
	osDelay(100);
  }
  /* USER CODE END StartDefaultTask */
}
  • 编译无误下载到开发板后,打开串口助手可以看到相关调试信息,使用网络调试工具创建一个PC服务端,开始监听服务端IP和端口后,输入任意小写字母,Client端将返回对应的大写字母

LWIP应用开发|Socket API编程模型_tcp_07

LWIP应用开发|Socket API编程模型_lwip_08

3. UDP编程模型

UDP编程CS模型流程图如下图示
LWIP应用开发|Socket API编程模型_lan8720_09
要实现的功能为:将从PC客户端输入的小写字母转换为大写字母

  • 参考带操作系统移植LWIP将LWIP移植到F4开发板中,并能够ping通开发板

  • 在工程中创建socket_udp_server.c和socket_udp_server.h两个文件

/******socket_udp_server.c******/
#include "socket_udp_server.h"
#include "lwip/sockets.h"
#include "ctype.h"

static char ReadBuff[BUFF_SIZE];

void vUdpServerTask(void){
  int sfd, n, i;
  struct sockaddr_in server_addr, client_addr;
  socklen_t	client_addr_len;
  //创建数据包套接字(UDP)
  sfd = socket(AF_INET, SOCK_DGRAM, 0);
  //填充地址信息
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(SERVER_PORT);
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  //绑定socket
  bind(sfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
  client_addr_len = sizeof(client_addr);
  while(1){
	//等待客户端发送数据
	n = recvfrom(sfd,ReadBuff,BUFF_SIZE,0,(struct sockaddr *)&client_addr, &client_addr_len);
	ReadBuff[n] = '\0';
	printf("recv data:%s\r\n",ReadBuff);
	//进行大小写转换
	for(i = 0; i < n; i++){
      ReadBuff[i] = toupper(ReadBuff[i]);		
	}
	//写回客户端
	sendto(sfd,ReadBuff,n,0,(struct sockaddr *)&client_addr,client_addr_len);
  }
	
}

/******socket_udp_client.h******/
#ifndef _SOCKET_UDP_SERVER_H
#define _SOCKET_UDP_SERVER_H

#define SERVER_PORT		6666
#define BUFF_SIZE		1024

void vUdpServerTask(void);

#endif
  • 在freertos.c文件中的默认任务里面添加代码
void StartDefaultTask(void const * argument){
  /* init code for LWIP */
  MX_LWIP_Init();
  /* USER CODE BEGIN StartDefaultTask */
  printf("TCP server started!\r\n",cfd);
  /* Infinite loop */
  for(;;){
    vUdpServerTask();
	osDelay(100);
  }
  /* USER CODE END StartDefaultTask */
}
  • 编译无误下载到开发板后,打开串口助手可以看到相关调试信息,使用网络调试工具创建一个PC客户端,连接相应IP和端口后,输入任意小写字母,Server将返回对应的大写字母

LWIP应用开发|Socket API编程模型_tcp_10
LWIP应用开发|Socket API编程模型_socket_11

LWIP应用开发|Socket API编程模型_socket_12