文章转自本人公众号:机械猿,本人之前在四川某汽轮机从事结构强度设计,目前在阿里巴巴淘宝事业部担任高级开发工程师,有机械工程同行想转行IT,或者有想入职BAT的可以找我内推~

絮叨

讲解CS通信之前,先大致了解一下我们平时手机通话的流程。语音信号经过脉冲采样变成数字信号,通过手机GSM模块发送无线信号至基站进入无线接入网,根据对方手机号查询数据库后通过骨干路由器转入核心网,一连串中转之后发送到对端所属的小区,找一条空闲线路接通对方。

网络通信类似,但是也有不同,电话信号只能维持一条连接,而一个服务端可以维持多条连接,像双十一淘宝OceanBase就达到了一千万QPS的并发量。

这里实名给手淘打个招聘广告

基础知识

了解APP通信首先要了解socket的含义。Socket是一种进程通信方式,可用于多主机之间的通信,IP地址(对应主机)和端口(对应进程)就确定了一个socket,类似于电话的插座。下面我们来实现一个基础网络示例:客户端从标准输入读取文本,发送给服务器;服务器接收后原文返回给客户端,客户端输出到标准输出。

注:标准输入STDIN位于 /dev/stdin ,一般为键盘输入,fd为0;标准输出STDOUT位于/dev/stdout,一般为终端显示器,fd为1;标准错误 STDERR位于/dev/stderr,fd为2。

TCP客户/服务端程序基本流程如下:

服务端处理流程

服务端程序如下:

#include<sys/socket.h>     /* basic socket definitions */
int main(int argc, char **argv)
{int listenfd,connfd;pid_t childpid;socklen_t clilen;struct sockaddr_incliaddr, servaddr;listenfd = Socket(AF_INET, SOCK_STREAM,0);  //创建套接字,监听端口bzero(&servaddr, sizeof(servaddr));servaddr.sin_family      = AF_INET;servaddr.sin_addr.s_addr =htonl(INADDR_ANY);servaddr.sin_port        = htons(SERV_PORT);Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));  //绑定本机地址Listen(listenfd, LISTENQ);      //监听for ( ; ; ) {clilen = sizeof(cliaddr);connfd = Accept(listenfd, (structsockaddr *) &cliaddr, &clilen);  //阻塞等待客户端SYN报文if ( (childpid = Fork()) == 0) {      /* fork一个子进程专门处理接入的客户端 */Close(listenfd);            /* 子进程关闭监听端口 */str_echo(connfd);       /* 子进程发送请求 */exit(0);}Close(connfd);                    /* 父进程关闭连接端口 */}
}

下面分析一下服务端状态机流程:

服务端创建一个监听套接字并绑定本机知名端口(如80、8080http端口),本机地址设置为INADDR_ANY是为了任何本地接口的连接都接收,一般为多网卡的场景。之后服务端阻塞在accpt调用,使用fork为每个客户端专门分配一个子进程,父进程继续监听接入的客户端。

对于已连接的客户端,使用str_echo读入客户端发送过来的数据,并直接返回回去。

void str_echo(intsockfd)
{ssize_t n;char buf[MAXLINE];again:while ( (n = read(sockfd, buf, MAXLINE))> 0)   //从标准输入读取数据Writen(sockfd, buf, n);        //发送至服务端// while循环退出说明接收到FIN包,客户端完成了数据发送if (n < 0 && errno == EINTR)goto again;                    //被信号打断,继续读取else if (n < 0)err_sys("str_echo: readerror");   //遇到其他错误结束运行
}

客户端处理流程

下面给出客户端处理状态机(省略部分socket异常处理):

str_cli处理逻辑如下:

void str_cli(FILE* fp, int sockfd)
{charsendline[MAXLINE],recvline[MAXLINE];while (fgets(sendline, MAXLINE, fp) !=NULL) {  //从fp读入数据Writen(sockfd, sendline,strlen(sendline));  //发送给服务器if (Readline(sockfd, recvline,MAXLINE) == 0)   //接收服务器发送过来的数据err_quit("str_cli:server terminated prematurely");  //如果为0,说明服务端已关闭连接fputs(recvline, stdout);  //将接收到的数据输出到终端}   //文件读取结束时fgets返回NULL,while退出
}

运行客户端/服务端程序

服务器启动后,在客户端连接之前,使用netstat -a检查主机监听套接字状态如下:

Proto   Local Address       Foreign Address       State
TCP     *:9877               *:*                LISTEN

来启动客户端并指定服务器地址127.0.0.1(本地环回地址),客户端在connect函数中完成TCP三次握手流程,之后服务端从accept中返回,一条数据通道建立。

服务端这边握手流程较为复杂,用简图表示如下:

连接建立后,客户端阻塞于fgets等待接收键盘输入,服务端进程从accept返回后调用fork创建一个子进程专门负责这条连接,父进程继续阻塞在accept上监听新客户端的到来。此时,三个进程都阻塞:客户端进程、服务器父进程、服务器子进程。

注1:一个程序不等于一个进程,像淘宝,除了主进程进行各种数据处理外,还有push进程作为维持客户端和服务器的长连接通信,用于发送心跳包和推送消息。

注2:建立连接时,客户端阻塞在connect上,收到服务器的SYN/ACK报文即返回,而服务器需要收到ACK报文才返回,两边阻塞时间差了半个RTT。

使用netstat -a观察现在连接情况:​​​​​​​

Proto    Local Address           Foreign Address            StateTCP     localhost:9877           localhost:47512             ESTABLISHED //服务器TCP     localhost:47512          localhost:9877              ESTABLISHED    //客户端TCP     *:9877                   *:*                         LISTEN  //服务器父进程可以看到双方socket已处于ESTABLISHED状态,接下来客户端可以和服务器进行数据收发。当客户端输入EOF字符(按下Control+Z表示终止输入)时,fgets返回空指针,客户端数据处理函数str_cli返回,客户端main函数调用exit终止进程。进程终止会关闭所有打开的文件描述符,因此客户端会发送FIN报文给服务器,服务器子进程回应ACK后也调用exit函数关闭文件描述符,发送FIN报文。

这里除了通过TCP四次挥手正常终止连接,还可以发送信号kill -9 pid终止进程。信号的处理后续剖析~

上述程序对服务器主机崩溃、主机重启、主机关机及客户端主机崩溃等异常情况都做了保护,这也是我们平时写需要注意程序健壮性的地方。

最后厚着脸皮推广一下自己的公众号:机械猿,有机械工程同行想转行IT,或者有想入职BAT的可以找我内推~

手机上的APP是如何与服务器通信的相关推荐

  1. 对安卓手机上的APP做monkey压力测试

    之前对安卓手机上的APP做了monkey测试,今天来总结一下如何使用安卓自带的monkey命令去做测试. 首先,PC端要对安卓手机进行monkey测试的话,必须要有以下条件: 1.电脑中必须配有ADB ...

  2. Android Studio项目打包生成可安装在自己手机上的App安装包文件

    点击上方"码农的后花园",选择"星标" 公众号 精选文章,第一时间送达 Android程序开发完后,如果要发布在互联网上供别人使用,就要将自己的程序打包成And ...

  3. 用JS任意控制手机上的APP

    用JS控制手机上的APP 1. 控制代码 可以用如下简单的JS代码,控制手机上的第三方APP的行为,实现自动测试等功能 再加上流行的UI交互,可以快速做成牛皮的自动化工具 开源文档:RobotJS文档 ...

  4. 凌晨三点,你手机上的APP在自动签到

    点击上方蓝色文字,选择"置顶公众号" 第一时间关注 Python 技术干货! 阅读文本大概需要 5 分钟. 前两篇文章讲到了自动化框架 RF 的搭建和自动化操作你的浏览器.还没上车 ...

  5. 苹果手机利用itune和手机上的app电脑互传数据

    苹果手机可以利用APP和电脑互传数据.

  6. 手机上app测试总结

     手机上的app分为基于HTML5的app(类似于pc上的b/S应用)和本地app(类似于C/S结构). 所以 测试上我们也可以充分吸收 web的b/s和c/s测试经验.但是不同于pc上的应用测试 ...

  7. 华为鸿蒙系统有望搭载手机上吗,华为智选车载智慧屏将12月上市:有望搭载鸿蒙系统...

    日前在华为Mate40系列发布会上正式发布了华为智选车载智慧屏,不过由于时间限制,华为并没有过多的透露该产品信息. 11月5日,华为在深圳举办"2020华为智选品鉴会"上宣布,华为 ...

  8. ios动态库注入把越狱手机上自制的动态库安装到普通手机上

    文章目录 预备条件 导出越狱手机上的app包和自己注入的动态库 导出自己写的tweak动态库文件 查看依赖库 执行命令查看程序依赖的动态库名字 用machoview查看 安装insert_dylib ...

  9. 使用adb命令uninstall卸载不掉手机上的apk时,可以卸载内置app路径

    使用adb命令uninstall卸载不掉手机上的apk时,可以卸载内置app路径 adb uninstall + 包名 Failure [DELETE_FAILED_INTERNAL_ERROR] 直 ...

最新文章

  1. 重磅!2K图像90FPS,中科院开源轻量级通用人脸检测器
  2. Stegsolve(Data Extract):lsb隐写
  3. 2017级面向对象程序设计——团队作业1
  4. 智能家居数据库设计_设计更智能的数据表
  5. spring4.x(10)---依赖注入-构造方法注入
  6. LayoutInflater——inflate方法不同参数的区别
  7. java 中的wait notify
  8. DOM属性用法速查手册
  9. paypal添加香港招商银行指南
  10. eclipse设置Tomcat超级详细
  11. C# 网页自动填表自动登录 .
  12. 索尼手机更新android10,索尼XPERIA 10 II终于收到了ANDROID 11更新
  13. oracle数据库imp命令,数据库imp导入命令
  14. java detach_Java Node.detach方法代码示例
  15. 【游戏王arc-v卡片力量SP改名字ID教程】
  16. Python Pitfall: 时间戳长度- 10位和13位时间戳
  17. Java匿名类习题_输出英文字母表和希腊字母表
  18. 对ARM紧致内存TCM的理解
  19. 「SDOI 2008」山贼集团
  20. 给Revit中的Button添加动画和图片

热门文章

  1. 获取 3D Slicer 的配色/色彩方案
  2. 根据学生分数给学生成绩分等级
  3. GaitGAN: Invariant Gait Feature Extraction Using Generative Adversarial Networks论文翻译以及理解
  4. mysql 增删改查时的错误解决方法大全
  5. Java中的输入scanner
  6. win10系统托盘图标不见了_win10 右下角任务托盘图标全部不见了,怎么办?
  7. JS正则表达式 ,reg.test()时,慎重全局查找/.../g属性
  8. linux搭建简易网站
  9. Git的学习笔记基本使用
  10. 帝国CMS仿《3641图库》模板/图片网站源码/带WAP手机站带数据