# issue 7 TCP回声服务器和客户端

打印 上一主题 下一主题

主题 871|帖子 871|积分 2613

一、TCP/IP协议栈

        为什么须要理解协议栈?
        学习C/C++就是要懂底层的原理。不然永久是“调包侠”
        根据数据传输方式的差别,基于网络协议(这里是指基于TCP/IP协议)的套接字一般分为TCP 套接字和UDP 套接字。由于TCP 套接字是面向毗连的,因此又称基于流(stream)的套接字。 TCP 是TransmissionControl Protocol(传输控制协议)的简写,意为"对数据传输过程的控制"。

第一层次:数据链路层 

        如图所示:链路层是物理链接领域标准化的结果,也是最基本的领域,专门定义LAN、WAN、MAN 等网络标准。若两台主机通过网络举行数据交换,则须要下图所示的物理毗连,
链路层就负责这些标准。

第二层次:IP 层

        准备好物理毗连后就要传输数据。为了在复杂的网络中传输数据,起首须要思量路径的
选择。向目标传输数据须要颠末哪条路径?办理此题目就是IP 层,该层使用的协议就是IP。
        IP 是面向消息的、不可靠的协议。每次传输数据时会帮我们选择路径,但并不同等。如
果传输中发生路径错误,则选择其他路径;但如果发生数据丢失或错误,则无法办理。换言
之,IP 协议是无法应对数据错误的。因此,又要下放一层。
第三层次:TCP/UDP 层

        IP 层办理数据传输中的路径选择题目,只需照此路径传输数据即可。TCP 和UDP 层以
IP 层提供的路径信息为根本完成实际的数据传输,故该层又称传输层(Transport)。
        IP 层只关注1 个数据包(数据传输的基本单位)的传输过程。因此,即使传输多个数据
包,每个数据包也是由IP 层实际传输的,也就是说传输顺序及传输本身是不可靠的。若只
利用IP 层传输数据,则有可能导致后传输的数据包B 比先传输的数据包A 提早到达。另外,
传输的数据包A、B、C 中有可能只收到A 和C,甚至收到的C 可能已损毁。
        若添加TCP 协议则按照下图的对话方式举行数据交换。

第四层次:应用层

        前面三个层次,套接字通信过程中都是自动处置惩罚的。为了"使步伐员从这些细节中解放
出来"。选择数据传输路径、数据确认过程都被隐蔽到套接字内部。前面三个层次都是为了
给应用层提供服务的。
二、TCP服务端和代码实现

服务的创建的基本流程(最简单的)

  1. void run_client()
  2. {
  3.         int client = socket(PF_INET, SOCK_STREAM, 0);
  4.         struct sockaddr_in servaddr;
  5.         memset(&servaddr, 0, sizeof(servaddr)); //清零 防止意外
  6.         servaddr.sin_family = AF_INET;
  7.         servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
  8.         servaddr.sin_port = htons(33005);
  9.         int ret = connect(client, (struct sockaddr*)&servaddr, sizeof(servaddr));
  10.         if (ret == 0) {
  11.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  12.                 char buffer[1024] = "hello , here is client\n";
  13.                 write(client, buffer, strlen(buffer));
  14.                 memset(buffer, 0, sizeof(buffer));
  15.                 read(client, buffer, sizeof(buffer));
  16.                 std::cout << buffer;
  17.         }
  18.         else {
  19.                 printf("%s(%d):%s       %d\r\n", __FILE__, __LINE__, __FUNCTION__, ret);
  20.         }
  21.         close(client);
  22.         std::cout << "client done!" << std::endl;
  23. }
  24. void lession63_() {
  25.         int server, client;
  26.         struct sockaddr_in seraddr, cliaddr; //服务端和客户端的地址结构体
  27.         socklen_t cliaddrlen;//客户端的地址结构体大小
  28.         //const char* message = "hello world!\n";
  29.         //1.创建服务器套接字
  30.         printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  31.         server = socket(PF_INET, SOCK_STREAM, 0);//TCP协议  什么是TCP,就是IPv4中面向了流的套接字
  32.         if (server < 0) {
  33.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  34.                 std::cout << "create socket failed!" << std::endl;
  35.                 return;
  36.         }
  37.         //2.绑定服务器套接字和地址结构体
  38.         // 设置SO_REUSEADDR选项,允许端口复用
  39.         memset(&seraddr, 0, sizeof(seraddr));//清零
  40.         seraddr.sin_family = AF_INET;        //地址协议族
  41.         seraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
  42.         seraddr.sin_port = htons(33005);
  43.         printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  44.         int on = 1;
  45.         if (setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
  46.                 perror("setsockopt");
  47.                 close(server);
  48.                 return;
  49.         }
  50.         printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  51.         int ret = bind(server, (struct sockaddr*)&seraddr, sizeof(seraddr));
  52.         printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  53.         if (ret == -1) {
  54.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  55.                 std::cout << "bind failed!" << std::endl;
  56.                 close(server);
  57.                 return;
  58.         }
  59.         ret = listen(server, 3);
  60.         if (ret == -1) {
  61.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  62.                 std::cout << "listen failed!" << std::endl;
  63.                 close(server);
  64.                 return;
  65.         }
  66.         char buffer[1024];
  67.         while (1) {
  68.                 memset(buffer, 0, sizeof(buffer));
  69.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  70.                 cliaddrlen = sizeof(cliaddr);
  71.                 client = accept(server, (struct sockaddr*)&cliaddr, &cliaddrlen/*&cliaddrlen*/);
  72.                 if (client == -1) {
  73.                         std::cout << "accept failed!" << std::endl;
  74.                         close(server);
  75.                         return;
  76.                 }
  77.                 //printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  78.                 read(client, buffer, sizeof(buffer));
  79.                 ssize_t len = write(client, buffer, strlen(buffer)); //返回值就是实际写入的字节数
  80.                 if (len != (ssize_t)strlen(buffer)) {
  81.                         std::cout << "write failed!" << std::endl;
  82.                         close(server);
  83.                         return;
  84.                 }
  85.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  86.                 close(client);
  87.         }
  88.         close(server);
  89.         printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  90. }
  91. void lession63()
  92. {
  93.         pid_t pid = fork();
  94.         if (pid == 0) {
  95.                 //开启客户端
  96.                 sleep(2);//让服务端先启动       
  97.                 run_client();
  98.                 run_client();
  99.         }
  100.         else if (pid > 0) {
  101.                 //开启服务端
  102.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  103.                 lession63_();
  104.                 int status = 0;
  105.                 wait(&status);//避免子进程成为僵尸进程
  106.         }
  107.         else {
  108.                 std::cout << "fork failed!" << pid << std::endl;
  109.         }
  110. }
复制代码
三、connect函数和TCP客户端

connect函数:

#include<sys/socket.h>
int connect(int sock,struct sockaddr*servaddr, socklen_t addrlen);

→成功时返回0,失败时返回-1。
● sock 客户端套接字文件描述符。
● servaddr 保存目标服务器端地点信息的变量地点值。
●addrlen 以字节为单位通报已通报给第二个结构体参数servaddr 的地点变量长度。
客户端套接字地点信息在哪?
实现服务器端必颠末程之一就是给套接字分配IP 和端标语。但客户端实现过程中并未出现
套接字地点分配,而是创建套接字后立即调用conect 函数。难道客户端套接字无需分配IP
和端口?
答案:当然不是!
网络数据交换必须分配IP 和端口。既然如此,那客户端套接字何时、何地、如何分配
地点呢?
.何时? 调用connect 函数时。
.何地? 操纵系统,更准确地说是在内核中。
.如何? IP 用计算机(主机)的IP,端口随机。
客户端的IP 地点和端口在调用connect 函数时自动分配,无需调用标志的bind 函数进
行分配。
这就是与服务端的差别。
TCP客户端



四、客户端代码实现和联调

基于TCP服务端/客户端的函数调用关系

五、迭代服务器

        前面的普通服务器TCP 的缺点:一次服务。只能给一个客户端服务。
        迭代服务器比力原始,它的原型可以描述成:

        这个改动就是,当处置惩罚完一个客户端后,会再次进入accept,等候毗连。
        也就是说,这个步伐是一个一个处置惩罚各个客户端发来的毗连的,比如一个客户端发来一个
毗连,那么只要它还没有完成本身的任务,那么它就一直会占用服务器的历程直到处置惩罚完毕
后服务器关闭掉这个socket。可以循环服务多个客户端。
六、回声服务器实现

        回声服务器:将从客户端收到的数据原样返回给客户端,即“回声”。
  1. void run_client65()
  2. {
  3.         int client = socket(PF_INET, SOCK_STREAM, 0);
  4.         struct sockaddr_in servaddr;
  5.         memset(&servaddr, 0, sizeof(servaddr)); //清零 防止意外
  6.         servaddr.sin_family = AF_INET;
  7.         servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
  8.         servaddr.sin_port = htons(33005);
  9.         int ret = connect(client, (struct sockaddr*)&servaddr, sizeof(servaddr));
  10.         while (ret == 0) {
  11.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  12.                 char buffer[256] = "";
  13.                 fputs("input message(Q to quit):", stdout);//输出提示符
  14.                 fgets(buffer, sizeof(buffer), stdin/*标准输入流*/);//读入一行
  15.                 if ((strcmp(buffer, "Q\n") == 0) || (strcmp(buffer, "q\n") == 0)) {
  16.                         break;
  17.                 }
  18.                 size_t len = strlen(buffer);
  19.                 size_t send_len = 0;
  20.                 while (send_len < len); {
  21.                         ssize_t ret = write(client, buffer + send_len, len - send_len);//发给服务器
  22.                         if (ret <= 0)
  23.                         {
  24.                                 fputs("write failed!\n", stdout);
  25.                                 close(client);
  26.                                 std::cout << "client done!" << std::endl;
  27.                                 return;
  28.                         }
  29.                         send_len += (size_t)ret;
  30.                 }
  31.                 memset(buffer, 0, sizeof(buffer));
  32.                 size_t read_len = 0;
  33.                 while (read_len < len)
  34.                 {
  35.                         ssize_t ret = read(client, buffer + read_len, len - read_len);
  36.                         if (ret <= 0)
  37.                         {
  38.                                 fputs("read failed!\n", stdout);
  39.                                 close(client);
  40.                                 std::cout << "client done!" << std::endl;
  41.                                 return;
  42.                         }
  43.                         send_len += (size_t)ret;
  44.                 }
  45.                 std::cout << "from server:" << buffer;
  46.         }
  47.         close(client);
  48.         std::cout << "client done!" << std::endl;
  49. }
  50. void server65() {
  51.         int server, client;
  52.         struct sockaddr_in seraddr, cliaddr; //服务端和客户端的地址结构体
  53.         socklen_t cliaddrlen;//客户端的地址结构体大小
  54.         //1.创建服务器套接字
  55.         printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  56.         server = socket(PF_INET, SOCK_STREAM, 0);//TCP协议  什么是TCP,就是IPv4中面向了流的套接字
  57.         if (server < 0) {
  58.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  59.                 std::cout << "create socket failed!" << std::endl;
  60.                 return;
  61.         }
  62.         //2.绑定服务器套接字和地址结构体
  63.         memset(&seraddr, 0, sizeof(seraddr));//清零
  64.         seraddr.sin_family = AF_INET;        //地址协议族
  65.         seraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
  66.         seraddr.sin_port = htons(33005);
  67.         //printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  68.         int on = 1;
  69.         if (setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
  70.                 perror("setsockopt");
  71.                 close(server);
  72.                 return;
  73.         }
  74.         //printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  75.         int ret = bind(server, (struct sockaddr*)&seraddr, sizeof(seraddr));
  76.         printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  77.         if (ret == -1) {
  78.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  79.                 std::cout << "bind failed!" << std::endl;
  80.                 close(server);
  81.                 return;
  82.         }
  83.         ret = listen(server, 3);
  84.         if (ret == -1) {
  85.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  86.                 std::cout << "listen failed!" << std::endl;
  87.                 close(server);
  88.                 return;
  89.         }
  90.         char buffer[1024];
  91.         for (int i = 0; i < 2; i++) {
  92.                 memset(buffer, 0, sizeof(buffer));
  93.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  94.                 cliaddrlen = sizeof(cliaddr);
  95.                 client = accept(server, (struct sockaddr*)&cliaddr, &cliaddrlen/*&cliaddrlen*/);
  96.                 if (client == -1) {
  97.                         std::cout << "accept failed!" << std::endl;
  98.                         close(server);
  99.                         return;
  100.                 }
  101.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  102.                 //ssize_t len=read(client, buffer, sizeof(buffer));
  103.                 ssize_t len = 0;
  104.                 while ((len = read(client, buffer, sizeof(buffer))) > 0) {
  105.                         len = write(client, buffer, len); //返回值就是实际写入的字节数
  106.                         if (len != (ssize_t)strlen(buffer)) {
  107.                                 std::cout << "write failed!    len:" << len << "  buffer:" << buffer << std::endl;
  108.                                 close(server);
  109.                                 return;
  110.                         }
  111.                         memset(buffer, 0, len);
  112.                 }
  113.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  114.                 close(client);
  115.         }
  116.         close(server);
  117.         printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  118. }
  119. void lession65()
  120. {
  121.         pid_t pid = fork();
  122.         if (pid == 0) {   //子进程
  123.                 //开启服务端
  124.                 server65();
  125.         }
  126.         else if (pid > 0) {
  127.                 for (int i = 0; i < 2; i++)
  128.                         run_client65();
  129.                 int status = 0;
  130.                 wait(&status);//避免子进程成为僵尸进程
  131.         }
  132.         else {
  133.                 std::cout << "fork failed!" << pid << std::endl;
  134.         }
  135. }
复制代码
七、回声服务器存在的题目和办理

  1. write(sock, message,strlen(message));
  2. str_len= read(sock, message,BUF_SIZE-1);
  3. message[str_len]= 0;
  4. printf("Message from server:%s",message);
复制代码

以上代码有个错误假设∶
"每次调用read、write 函数时都会以字符串为单位执行实际的I/O 操纵(读文件是输入,写文件是输出)。"
        当然,每次调用write 函数都会通报1 个字符串,因此这种假设在某种程度上也算合理。
但是我们之前讲过:"TCP 不存在数据边界"的内容吗?
        上述客户端是基于TCP 的,因此,多次调用write 函数通报的字符串有可能一次性通报
到服务器端。此时客户端有可能从服务器端收到多个字符串,这不是我们希望看到的结果。
还需思量服务器端的如下情况∶
"字符串太长,须要分2 个数据包发送!"
        我们的回声服务器端/客户端给出的结果是精确的。但这只是运气好罢了!只是由于收发
的数据小,而且运行情况为同一台计算机或相邻的两台计算机,所以没发生错误,可实际上
仍存在发生错误的可能。
八、回声服务器实战:计算器的网络实现(上)

这里举行客户端的实现:

  1. void run_client66()
  2. {
  3.         int client = socket(PF_INET, SOCK_STREAM, 0);
  4.         struct sockaddr_in servaddr;
  5.         memset(&servaddr, 0, sizeof(servaddr)); //清零 防止意外
  6.         servaddr.sin_family = AF_INET;
  7.         servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
  8.         servaddr.sin_port = htons(33005);
  9.         int ret = connect(client, (struct sockaddr*)&servaddr, sizeof(servaddr));
  10.         char buffer[255 * 4] = "";
  11.         while (ret == 0) {
  12.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  13.                 fputs("Operand count:", stdout);//输出提示符
  14.                 int opnd_cnt = 0;
  15.                 scanf("%d", &opnd_cnt);
  16.                 if (opnd_cnt <= 1 && opnd_cnt >= 256) {
  17.                         fputs("opnd_cnt error,too small or too big!\n", stdout);
  18.                         close(client);
  19.                         std::cout << "client done!" << opnd_cnt << std::endl;
  20.                         return;
  21.                 }
  22.                 buffer[0] = (char)opnd_cnt;         //服务器此处需要解释为无符号
  23.                 for (int i = 0; i < opnd_cnt; i++) {
  24.                         scanf("%d", buffer + 1 + i * 4);
  25.                 }
  26.                 fgetc(stdin);
  27.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  28.                 fputs("Operator:", stdout);
  29.                 buffer[1 + opnd_cnt * 4] = (char)fgetc(stdin);
  30.                 //buffer[1 + opnd_cnt * 4] = (char)fgetc(stdin);
  31.                 //fgetc(stdin); // 清除换行符
  32.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  33.                 size_t len = opnd_cnt * 4 + 2;
  34.                 size_t send_len = 0;
  35.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  36.                 while (send_len < len) {
  37.                         printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  38.                         ssize_t ret = write(client, buffer + send_len, len - send_len);//发给服务器
  39.                         if (ret <= 0)
  40.                         {
  41.                                 fputs("write failed!\n", stdout);
  42.                                 close(client);
  43.                                 std::cout << "client done!" << std::endl;
  44.                                 return;
  45.                         }
  46.                         send_len += (size_t)ret;
  47.                 }
  48.                 memset(buffer, 0, sizeof(buffer));
  49.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  50.                 size_t read_len = 0;
  51.                 while (read_len < 4)
  52.                 {
  53.                         printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  54.                         ssize_t ret = read(client, buffer + read_len, len - read_len);
  55.                         if (ret <= 0)
  56.                         {
  57.                                 fputs("read failed!\n", stdout);
  58.                                 close(client);
  59.                                 std::cout << "client done!" << std::endl;
  60.                                 return;
  61.                         }
  62.                         read_len += (size_t)ret;
  63.                 }
  64.                 printf("from server:%d\n", *(int*)buffer);
  65.         }
  66.         close(client);
  67.         std::cout << "client done!" << std::endl;
  68. }
复制代码
九、回声服务器实战:计算器的网络实现(下)

这里实现服务端:(包含计算函数和主函数)

  1. int calculate(int count, int oprand[], char op) {
  2.         int result = 0;
  3.         switch (op) {
  4.         case '+':
  5.                 for (int i = 0; i < count; i++) {
  6.                         result += oprand[i];
  7.                 }
  8.                 break;
  9.         case '-':
  10.                 result = oprand[0];
  11.                 for (int i = 1; i < count; i++) {
  12.                         result -= oprand[i];
  13.                 }
  14.                 break;
  15.         case '*':
  16.                 result = oprand[0];
  17.                 for (int i = 1; i < count; i++) {
  18.                                 result *= oprand[i];
  19.                 }
  20.                 break;
  21.         default:
  22.                 break;
  23.         }
  24.         return result;
  25. }
  26. void server66() {
  27.         int server, client;
  28.         struct sockaddr_in seraddr, cliaddr; //服务端和客户端的地址结构体
  29.         socklen_t cliaddrlen;//客户端的地址结构体大小
  30.         //1.创建服务器套接字
  31.         printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  32.         server = socket(PF_INET, SOCK_STREAM, 0);//TCP协议  什么是TCP,就是IPv4中面向了流的套接字
  33.         if (server < 0) {
  34.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  35.                 std::cout << "create socket failed!" << std::endl;
  36.                 return;
  37.         }
  38.         //2.绑定服务器套接字和地址结构体
  39.         memset(&seraddr, 0, sizeof(seraddr));//清零
  40.         seraddr.sin_family = AF_INET;        //地址协议族
  41.         seraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
  42.         seraddr.sin_port = htons(33005);
  43.         //printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  44.         int on = 1;
  45.         if (setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
  46.                 perror("setsockopt");
  47.                 close(server);
  48.                 return;
  49.         }
  50.         //printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  51.         int ret = bind(server, (struct sockaddr*)&seraddr, sizeof(seraddr));
  52.         printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  53.         if (ret == -1) {
  54.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  55.                 std::cout << "bind failed!" << std::endl;
  56.                 close(server);
  57.                 return;
  58.         }
  59.         ret = listen(server, 3);
  60.         if (ret == -1) {
  61.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  62.                 std::cout << "listen failed!" << std::endl;
  63.                 close(server);
  64.                 return;
  65.         }
  66.         char buffer[1024];
  67.         for (int i = 0; i < 2; i++) {
  68.                 memset(buffer, 0, sizeof(buffer));
  69.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  70.                 cliaddrlen = sizeof(cliaddr);
  71.                 client = accept(server, (struct sockaddr*)&cliaddr, &cliaddrlen/*&cliaddrlen*/);
  72.                 if (client == -1) {
  73.                         std::cout << "accept failed!" << std::endl;
  74.                         close(server);
  75.                         return;
  76.                 }
  77.                 //printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  78.                 //ssize_t len=read(client, buffer, sizeof(buffer));
  79.                 ssize_t len = 0;
  80.                 len = read(client, buffer, 1);
  81.                 int result;
  82.                 if (len > 0) {
  83.                         for (unsigned int i = 0; i < ((unsigned)buffer[0] & 0xFF); i++) {
  84.                                 read(client, buffer + 1 + i * 4, 4);
  85.                         }
  86.                         read(client, buffer + 1 + ((unsigned)buffer[0] & 0xFF) * 4, 1);
  87.                         result = calculate((int)buffer[0] & 0xFF, (int*)(buffer + 1), buffer[1 + ((unsigned)buffer[0] & 0xFF) * 4]);
  88.                         write(client, &result, 4);
  89.                         std::cout << "result" << result << std::endl;
  90.                 }
  91.                 while ((len = read(client, buffer, sizeof(buffer))) > 0) {
  92.                         len = write(client, buffer, len); //返回值就是实际写入的字节数
  93.                         if (len != (ssize_t)strlen(buffer)) {
  94.                                 std::cout << "write failed!    len:" << len << "  buffer:" << buffer << std::endl;
  95.                                 close(server);
  96.                                 return;
  97.                         }
  98.                         memset(buffer, 0, len);
  99.                 }
  100.                 printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  101.                 read(client, buffer, 1);
  102.                 close(client);
  103.         }
  104.         close(server);
  105.         printf("%s(%d):%s\r\n", __FILE__, __LINE__, __FUNCTION__);
  106. }
  107. void lession66()
  108. {
  109.         pid_t pid = fork();
  110.         if (pid == 0) {   //子进程
  111.                 //开启服务端
  112.                 server66();
  113.         }
  114.         else if (pid > 0) {
  115.                 for (int i = 0; i < 2; i++)
  116.                         run_client66();
  117.                 int status = 0;
  118.                 wait(&status);//避免子进程成为僵尸进程
  119.         }
  120.         else {
  121.                 std::cout << "fork failed!" << pid << std::endl;
  122.         }
  123. }
复制代码
十、TCP套接字的IO缓冲

        我们知道,TCP 套接字的数据收发无边界。服务器端即使调用1 次write 函数传输40 字
节的数据,客户端也有可能通过4 次read 函数调用每次读取10 字节。但此处也有一些疑问,
服务器端一次性传输了40 字节,而客户端居然可以缓慢地分批接收。客户端接收10 字节后,
剩下的30 字节在那边等待呢?是不是像飞机为等候着陆而在空中回旋一样,剩下30 字节也
在网络中倘佯并等候接收呢?
        实际上,write 函数调用后并非立即传输数据,read 函数调用后也并非立刻接收数据。
更准确地说,如下图所示,write 函数调用瞬间,数据将移至输出缓冲;read 函数调用瞬间,
从输人缓冲读取数据。

        调用write 函数时,数据将移到输出缓冲,在适当的时候(不管是分别传送还是一次性
传送)传向对方的输入缓冲。这时对方将调用read 函数从输入缓冲读取数据。这些I/O 缓
冲特性可整理如下。
A: I/O 缓冲在每个TCP 套接字中单独存在。
B: I/O 缓冲在创建套接字时自动天生。
C: 即使关闭套接字也会继续通报输出缓冲中遗留的数据。
D: 关闭套接字将丢失输入缓冲中的数据。
那么,下面这种情况会引发什么事情?理解了I/O 缓冲后,其流程∶
"客户端输入缓冲为50 字节,而服务器端传输了100 字节。"
这的确是个题目。输入缓冲只有50 字节,却收到了100 字节的数据。可以提出如下办理方
案∶
填满输入缓冲前迅速调用read 函数读取数据,这样会腾出一部门空间,题目就办理了。
实在根本不会发生这类题目,由于TCP 会控制数据流。
TCP 中有滑动窗口(Sliding Window)协议,用对话方式呈现如下。
套接字A∶"你好,最多可以向我通报50 字节。"
套接字B∶"OK!"
套接字A∶"我腾出了20 字节的空间,最多可以收70 字节。
套接字B∶"OK!" 
数据收发也是如此,因此TCP 中不会由于缓冲溢出而丢失数据。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

西河刘卡车医

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表