文章目录

  • 前言
  • 1、网络通信--TCP
    • 1.1、常用概念辨析
    • 1.2、程序分析
  • 2、网络通信--UDP
    • 程序分析
  • 参考资料

前言

韦东山嵌入式Linux应用开发基础知识学习笔记
文章中大多内容来自韦东山老师的文档,还有部分个人根据自己需求补充的内容

视频教程地址: https://www.bilibili.com/video/BV1kk4y117Tu

1、网络通信–TCP

1.1、常用概念辨析

IP和端口
所有的数据传输,都有三个要素 :源、目的、长度
怎么表示源或者目的呢?请看下图:

▲源和目的

所以,在网络传输中需要使用“IP和端口”来表示源或目的

网络传输中的2个对象:server和client

我们经常访问网站,这涉及2个对象:网站服务器,浏览器。网站服务器平时安静地呆着,浏览器主动发起数据请求。网站服务器、浏览器可以抽象成2个软件的概念:server程序、client程序。

▲server和client

两种传输方式:TCP/UDP
参考文章:计算机网络模型(TCP五层模型)
  TCP和UDP 原理上的区别
  TCP向它的应用程序提供了面向连接的服务。这种服务有2个特点:可靠传输、流量控制(即发送方/接收方速率匹配)。它包括了应用层报文划分为短报文,并提供拥塞控制机制。
  UDP协议向它的应用程序提供无连接服务。它没有可靠性,没有流量控制,也没有拥塞控制。
  为何存在UDP协议
  TCP和UDP的区别
  通过报文格式可以很直观的看出TCP相对于UDP为什么传输效率低,安全性高

▲TCP报文格式

▲UDP报文格式

  TCP/UDP网络通信大概交互图

▲面向连接的TCP流模式

▲UDP用户数据包模式

1.2、程序分析

▲面向连接的TCP流模式

server.c:服务器端运行程序

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
/* socket* bind* listen* accept* send/recv*/
#define SERVER_PORT 8888                    //端口号
#define BACKLOG     10                      //最大监听连接数量int main(int argc, char **argv)
{int iSocketServer;                     //服务器socket文件描述符int iSocketClient;                      //客户端socket文件描述符struct sockaddr_in tSocketServerAddr;   //服务器socket addressstruct sockaddr_in tSocketClientAddr;    //客户端socket addressint iRet;int iAddrLen;                           //客户端socket address长度int iRecvLen;                          //从客户端接收数据长度unsigned char ucRecvBuf[1000];          //从客户端接收数据缓冲区int iClientNum = -1;                  //客户端索引 signal(SIGCHLD,SIG_IGN);//AF_INET:IPV4 SOCK_STREAM:TCP传输 0:只存在一个协议来支持给定协议族中的特定套接字类型iSocketServer = socket(AF_INET, SOCK_STREAM, 0);if (-1 == iSocketServer){printf("socket error!\n");return -1;}//设定协议族tSocketServerAddr.sin_family      = AF_INET;//主机字节序转换成网络字节序tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short *///INADDR_ANY:本机上所有的IPtSocketServerAddr.sin_addr.s_addr = INADDR_ANY;memset(tSocketServerAddr.sin_zero, 0, 8);//绑定socket文件描述符和socket addressiRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));if (-1 == iRet){printf("bind error!\n");return -1;}//设定iSocketServer最大监听数量BACKLOGiRet = listen(iSocketServer, BACKLOG);if (-1 == iRet){printf("listen error!\n");return -1;}while (1){iAddrLen = sizeof(struct sockaddr);/*iSocketServer可以接受连接,建立连接的客户端的socket address保存在iSocketClient长度信息保存在iAddrLen,且如果连接成功,系统返回iSocketClient*/iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);if (-1 != iSocketClient){iClientNum++;//inet_ntoa:net to ASCIIprintf("Get connect from client %d : %s\n",  iClientNum, inet_ntoa(tSocketClientAddr.sin_addr));//复制出子进程if (!fork()){/* 子进程fork() == 0 *//* 子进程源码 */while (1){/* 接收客户端发送数据并显示 */iRecvLen = recv(iSocketClient, ucRecvBuf, 999, 0);if (iRecvLen <= 0){close(iSocketClient);return -1;}else{//添加结束符ucRecvBuf[iRecvLen] = '\0';printf("Get Msg From Client %d: %s\n", iClientNum, ucRecvBuf);}}             }//父进程}}close(iSocketServer);return 0;
}

client.c:客户端运行程序,使用./client <IP>来和服务器建立连接

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>/* socket* connect* send/recv*/#define SERVER_PORT 8888int main(int argc, char **argv)
{int iSocketClient;struct sockaddr_in tSocketServerAddr;    int iRet;unsigned char ucSendBuf[1000];int iSendLen;if (argc != 2){printf("Usage:\n");printf("%s <server_ip>\n", argv[0]);return -1;}iSocketClient = socket(AF_INET, SOCK_STREAM, 0);tSocketServerAddr.sin_family      = AF_INET;tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short *///tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr)){printf("invalid server_ip\n");return -1;}memset(tSocketServerAddr.sin_zero, 0, 8);iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr)); if (-1 == iRet){printf("connect error!\n");return -1;}while (1){if (fgets(ucSendBuf, 999, stdin)){iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);if (iSendLen <= 0){close(iSocketClient);return -1;}}}return 0;
}

fork()函数返回值
  1)在父进程中,fork返回新创建子进程的进程ID;
  2)在子进程中,fork返回0;
  3)如果出现错误,fork返回一个负值;
  操作系统之 fork() 函数详解

什么是僵尸进程?
  僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被init接管,子进程退出后init会回收其占用的相关资源
  在这里如果没有在server.c中添加signal(SIGCHLD,SIG_IGN);则会在客户端建立连接又断开连接后产生僵尸进程server
怎么解决僵尸进程问题?
  signal(SIGCHLD, SIG_IGN)

2、网络通信–UDP

程序分析

▲UDP用户数据包模式

server.c:服务器端运行程序

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
/* socket* bind* sendto/recvfrom*/
#define SERVER_PORT 8888int main(int argc, char **argv)
{int iSocketServer;int iSocketClient;struct sockaddr_in tSocketServerAddr;struct sockaddr_in tSocketClientAddr;int iRet;int iAddrLen;int iRecvLen;unsigned char ucRecvBuf[1000];//AF_INET:IPV4 SOCK_DGRAM:UDP传输 0:只存在一个协议来支持给定协议族中的特定套接字类型iSocketServer = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == iSocketServer){printf("socket error!\n");return -1;}//设定协议族tSocketServerAddr.sin_family      = AF_INET;//主机字节序转换成网络字节序tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short *///INADDR_ANY:本机上所有的IPtSocketServerAddr.sin_addr.s_addr = INADDR_ANY;memset(tSocketServerAddr.sin_zero, 0, 8);//绑定socket文件描述符和socket addressiRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));if (-1 == iRet){printf("bind error!\n");return -1;}while (1){iAddrLen = sizeof(struct sockaddr);//接收客户端发送的数据并显示iRecvLen = recvfrom(iSocketServer, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);if (iRecvLen > 0){ucRecvBuf[iRecvLen] = '\0';printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);}}close(iSocketServer);return 0;
}

client.c:客户端运行程序,使用./client <IP>来和服务器建立连接

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
/* socket* connect* send/recv*/
#define USECONNECT  0
#define SERVER_PORT 8888int main(int argc, char **argv)
{int iSocketClient;struct sockaddr_in tSocketServerAddr;int iRet;unsigned char ucSendBuf[1000];int iSendLen;int iAddrLen;if (argc != 2){printf("Usage:\n");printf("%s <server_ip>\n", argv[0]);return -1;}iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);tSocketServerAddr.sin_family      = AF_INET;tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short *///tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr)){printf("invalid server_ip\n");return -1;}memset(tSocketServerAddr.sin_zero, 0, 8);#if USECONNECTiRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));   if (-1 == iRet){printf("connect error!\n");return -1;}
#endifwhile (1){if (fgets(ucSendBuf, 999, stdin)){#if USECONNECTiSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
#elseiAddrLen = sizeof(struct sockaddr);iSendLen = sendto(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0,(const struct sockaddr *)&tSocketServerAddr, iAddrLen);#endifif (iSendLen <= 0){close(iSocketClient);return -1;}}}return 0;
}

参考资料

操作系统之 fork() 函数详解
signal(SIGCHLD, SIG_IGN)
计算机网络模型(TCP五层模型)

【嵌入式Linux】嵌入式Linux应用开发基础知识之网络通信相关推荐

  1. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之Pinctrl子系统和GPIO子系统的使用

    文章目录 前言 1.Pinctrl子系统 1.1.为什么有Pinctrl子系统 1.2.重要的概念 1.3.代码中怎么引用pinctrl 2.GPIO子系统 2.1.为什么有GPIO子系统 2.2.在 ...

  2. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之按键驱动框架

    文章目录 前言 1.APP怎么读取按键值 1.1.查询方式 1.2.休眠-唤醒方式 1.3.poll方式 1.3.异步通知方式 1.5. 驱动程序提供能力,不提供策略 2.按键驱动程序框架--查询方式 ...

  3. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之LED模板驱动程序的改造:设备树

    文章目录 前言 1.驱动的三种编写方法 2.怎么使用设备树写驱动程序 2.1.设备树节点要与platform_driver能匹配 2.2.修改platform_driver的源码 3.实验和调试技巧 ...

  4. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之设备树模型

    文章目录 前言 1.设备树的作用 2.设备树的语法 2.1.设备树的逻辑图和dts文件.dtb文件 2.1.1.1Devicetree格式 1DTS文件的格式 node的格式 properties的格 ...

  5. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之总线设备驱动模型

    文章目录 前言 1.驱动编写的三种方法 1.1.传统写法 1.2.总线驱动模型 1.3.设备树驱动模型 2.Linux实现分离:Bus/Dev/Drv模型 2.1.Bus/Dev/Drv模型 2.2. ...

  6. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之驱动设计的思想:面向对象/分层/分离

    文章目录 前言 1.分离设计 驱动程序分析---程序分层 通用驱动程序---面向对象 个性化驱动程序---分离 APP 程序分析 前言 韦东山嵌入式Linux驱动开发基础知识学习笔记 文章中大多内容来 ...

  7. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之LED驱动框架--面向对象、分层设计思想

    文章目录 前言 1.LED驱动程序框架 1.1.对于LED驱动,我们想要什么样的接口? 1.2.LED驱动要怎么写,才能支持多个板子?分层写 1.3.程序分析 驱动程序 应用程序 Makefile 1 ...

  8. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之第一个驱动

    文章目录 前言 1.Hello驱动 1.1.APP打开的文件在内核中如何表示? 1.2.打开字符设备节点时,内核中也有对应的struct file 1.3.如何编写驱动程序? 1.4.驱动程序代码 1 ...

  9. 【嵌入式Linux】嵌入式Linux应用开发基础知识之I2C应用编程和SMBus协议及AP3216C应用编程

    文章目录 前言 1.IIC协议和SMBUS协议 1.1.IIC协议 1.1.1.硬件框架 1.1.2.软件框架 1.1.3.读写数据格式 1.1.4.硬件结构--在硬件上是如何实现双向传输 1.2.S ...

最新文章

  1. 理解YOLOv2训练过程中输出参数含义
  2. VR+监狱,分分钟让犯罪分子重新做人
  3. ubuntu装双系统win7和linux,Ubuntu下安装WIn7(双系统)
  4. C语言学习之求S=a+aa+aaa+... +aa.....aa之值,其中a是一个数字,n表示a的位数.
  5. c++中堆、栈内存分配概念示例讲解
  6. javascript 核心概念(1)-数据类型
  7. docx文档怎么排列图片_“胶水语言”办公自动化Word篇——使用Python编辑和读取Word文档
  8. 机器学习入门|线性回归(二)
  9. [Matlab] 二进制蝙蝠算法用于解决背包问题
  10. UltraISO9.3.0.2610中文绿色注册版
  11. JAVA之进制转换(全)
  12. 鄂尔多斯固体废物智慧化管理平台设备和功能概况
  13. Python exe Fatal error detected
  14. TriangleCount三角形计数
  15. idea-2017破解教程
  16. PPT画图(或排版)后保存为高清图片(可自定义分辨率)
  17. 基于PHP开发的云平台网络课堂教学学习互动平台设计
  18. ffmpeg视频转动图gif和Webp
  19. 输入关键字的爬虫方法(运行环境python3)
  20. 算术运算符:取余(取模)%

热门文章

  1. POJ 2240 Arbitrage(SPFA判正环)
  2. python tkinter treeview 高亮_满满的成就感~如何用python让你的想法拥有可操作性?(一)...
  3. java 本年第一天_JAVA获取本周 本月 本年 第一天和最后一天
  4. 按大小排序php,php中按大小进行排序的函数有哪些
  5. 推荐系统学习(一)推荐系统分类与基本流程
  6. matlab错误使用builtin,MATLAB环境下运行MATLAB函数时发生异常
  7. 触发更新机制_王者荣耀1.14更新:11名英雄调整,韩信加强,鲁班大师重做
  8. 第四单元和课程总结:简单的架构设计意识
  9. ArchLinux安装Gnome桌面
  10. Halcon 和 C# 联合编程 - 如何使用开源项目 ViewROI