文章目录
前言1、网络通信--TCP1.1、常用概念辨析1.2、程序分析2、网络通信--UDP程序分析参考资料前言
韦东山嵌入式Linux应用开发基础知识学习笔记
文章中大多内容来自韦东山老师的文档,还有部分个人根据自己需求补充的内容
视频教程地址:/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 BACKLOG10//最大监听连接数量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应用开发基础知识之网络通信》对你有帮助,请点赞、收藏,并留下你的观点哦!