TCP/IP网络编程 学习笔记_6 --定义应用层协议
2015-07-14 16:36
549 查看
前言:上一章节写了个回声客服端,回顾一下,客服端是循环读取已知的数据长度,但更多的情况是我们一般无法提前知道数据的长度,那么此时应该如何收发数据?这时需要的就是应用层协议的定义。如:上一节写的回声程序中定义“收到Q就立即终止连接”这么个协议(规则),就是应用层协议。同样,收发数据过程中也需要定好规则以表示数据的边界,或提前告知收发数据的大小。所谓应用层协议就是服务端/客服端实现过程中逐步定义的规则的集合。可以看出,应用层协议并不是高深莫测的存在,只不过是为特定程序的实现而制定的规则。
客服端连接到服务端后以1字节整数形式传递操作数个数。
客服端向服务端传递的每个操作数是占4字节的整数型数据。
传递各操作数后接着传递运算符,运算符占用1字节。
服务端以4字节整数型向客服端传回运算结果。
客服端得到运算结果后终止与服务器的连接。
以上就是为实现特定程序而设计的应用层协议,下面来看看依据这些协议规则实现的具体代码。
服务端
计算器程序应用层协议设计
下面我们来尝试设计一个实现计算器程序的应用层协议(客服端发送操作数个数,操作数,运算符,服务端接收这些数据算出结束给客服端):客服端连接到服务端后以1字节整数形式传递操作数个数。
客服端向服务端传递的每个操作数是占4字节的整数型数据。
传递各操作数后接着传递运算符,运算符占用1字节。
服务端以4字节整数型向客服端传回运算结果。
客服端得到运算结果后终止与服务器的连接。
以上就是为实现特定程序而设计的应用层协议,下面来看看依据这些协议规则实现的具体代码。
计算器程序源码
客服端// // main.cpp // hello_client // // Created by app05 on 15-7-6. // Copyright (c) 2015年 app05. All rights reserved. // #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define BUF_SIZE 1024 #define RLT_SIZE 4 #define OPSZ 4 void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); } int main(int argc, const char * argv[]) { int sock; char opmsg[BUF_SIZE]; int result, opnd_cnt, i; struct sockaddr_in serv_adr; if (argc != 3) { printf("Usage: %s <IP> <port> \n", argv[0]); exit(1); } sock = socket(PF_INET, SOCK_STREAM, 0); if (sock == -1) { error_handling("socket() error"); } memset(&serv_adr, 0, sizeof(serv_adr)); serv_adr.sin_family = AF_INET; serv_adr.sin_addr.s_addr = inet_addr(argv[1]); serv_adr.sin_port = htons(atoi(argv[2])); if(connect(sock, (struct sockaddr *) &serv_adr, sizeof(serv_adr)) == -1) error_handling("connect() error"); else puts("Connected............"); //操作数个数(占1字节) fputs("Operand count: ", stdout); scanf("%d", &opnd_cnt); opmsg[0] = (char)opnd_cnt; //操作数(每个占4字节) for (i = 0; i < opnd_cnt; i++) { printf("Operand %d: ", i + 1); scanf("%d", (int *)&opmsg[i * OPSZ + 1]); } fgetc(stdin); //读掉缓冲中的字符\n //运算符(占1字节) fputs("Operator: ", stdout); scanf("%c", &opmsg[opnd_cnt * OPSZ + 1]); write(sock, opmsg, opnd_cnt * OPSZ + 2); read(sock, &result, RLT_SIZE); //因为服务端返回的结果只有4字节,可以一次性读取,所以就不需要循环读取了 printf("Operation result: %d \n", result); close(sock); return 0; }
服务端
// // main.cpp // hello_server // // Created by app05 on 15-7-6. // Copyright (c) 2015年 app05. All rights reserved. // /* 说明:数据传输是否需要循环读取,主要看传输数据的大小,如果比较长,可能会分多次传输, 而tcp的接收与传输不一定相等,这时,为了正确读取数据就需要根据传输数据的大小循环读取。 这是很多新手常常犯的错误,一定要注意tcp没有数据边界。 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define BUF_SIZE 1024 #define OPSZ 4 void error_handling(char *message); int calculate(int opnum, int opnds[], char oprator); int main(int argc, const char * argv[]) { int serv_sock, clnt_sock; char opinfo[BUF_SIZE]; int result, opnd_cnt, i; int recv_cnt, recv_len; struct sockaddr_in serv_adr, clnt_adr; socklen_t clnt_adr_sz; if (argc != 2) { printf("Usage : %s <port> \n", argv[0]); exit(1); } serv_sock = socket(PF_INET, SOCK_STREAM, 0); if(serv_sock == -1) error_handling("socket() error"); memset(&serv_adr, 0, sizeof(serv_adr)); serv_adr.sin_family = AF_INET; serv_adr.sin_addr.s_addr = htonl(INADDR_ANY); serv_adr.sin_port = htons(atoi(argv[1])); if (bind(serv_sock, (struct sockaddr *) &serv_adr, sizeof(serv_adr)) == -1) error_handling("bind() error"); if(listen(serv_sock, 5) == -1) error_handling("listen() error"); clnt_adr_sz =sizeof(clnt_adr); for (i = 0; i < 5; i++) { opnd_cnt = 0; clnt_sock = accept(serv_sock, (struct sockaddr *) &clnt_adr, &clnt_adr_sz); //操作数个数 read(clnt_sock, &opnd_cnt, 1); //操作数和运输符 recv_len = 0; while ((opnd_cnt * OPSZ + 1) > recv_len) { recv_cnt = read(clnt_sock, &opinfo[recv_len], BUF_SIZE - 1); recv_len += recv_cnt; } //结果 result = calculate(opnd_cnt, (int *)opinfo, opinfo[recv_len - 1]); write(clnt_sock, (char *)&result, sizeof(result)); close(clnt_sock); } close(serv_sock); return 0; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); } int calculate(int opnum, int opnds[], char op) { int result = opnds[0], i; switch (op) { case '+': for (i = 1; i < opnum; i++) result += opnds[i]; break; case '-': for (i = 1; i < opnum; i++) result -= opnds[i]; break; case '*': for (i = 1; i < opnum; i++) result *= opnds[i]; break; } return result; }
相关文章推荐
- TCP/IP网络编程 学习笔记_6 --定义应用层协议
- HP MSA P2000 G3 使用 CLI 端口和电缆设置网络端口 IP 地址
- HTTP1.1初识
- android简单实现从网络下载文件到手机sd卡
- 计算机网络网络层
- 常见网络攻击原理
- linux socket网络编程详解
- ios网络请求 get——post 区别
- http配置如何开启gzip网页压缩
- 网络编程socket基本API详解
- apache2.2 httpd-vhosts.conf文件配置
- windows网卡共享网络时,报Internet连接共享访问被启用时,出现了一个错误。(null)
- angularJS 报错: [ngModel:numfmt] http://errors.angularjs.org/1.4.1/ngModel/numfmt?p0=333
- HTTP请求方式GET和POST的区别详解
- 详细解释CNN卷积神经网络各层的参数和链接个数的计算
- http协议详解
- 使用eclipse中的tcp/ip监听webservice请求和响应数据
- HTTP Header 详解
- HTTP头部详解
- Https传输的简单介绍及Tomcat配置