定义应用层协议
收发数据过程中需要定好规则(协议)以表示数据的边界,或提前告知收发数据的大小。所以应用层协议就是为特定程序的实现而制定的规则。
计算器服务器端/客户端的程序示例
客户端:
#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); int main(int argc,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......"); fputs("Operand count:",stdout); scanf("%d",&opnd_cnt); opmsg[0]=(char)opnd_cnt; for(i=0;i<opnd_cnt;i++) { printf("Operand %d:",i+1); scanf("%d",(int*)&opmsg[i*OPSZ+1]); } fgetc(stdin); fputs("Operator:",stdout); scanf("%c",&opmsg[opnd_cnt*OPSZ+1]); write(sock,opmsg,opnd_cnt*OPSZ+2); read(sock,&result,RLT_SIZE); printf("Operation result:%d\n",result); close(sock); return 0; } void error_handling(char *message) { fputs(message,stderr); fputc('\n',stderr); exit(1); }
服务器端:
#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 operator); int main(int argc,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; } 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; } void error_handling(char *message) { fputs(message,stderr); fputc('\n',stderr); exit(1); }
TCP原理:
TCP套接字中的I/O缓冲
I/O缓冲特性:
1.I/O缓冲在每个TCP套接字中单独存在。
2.I/O缓冲在创建套接字时自动生成。
3.即使关闭套接字也会继续传递输出缓冲中遗留的数据。
4.关闭套接字会丢失输入缓冲中的数据。
5.不会发生超过输入缓冲大小的数据传输,因为TCP会控制数据流(滑动窗口协议),所以不会因为缓冲溢出而丢失数据。
TCP内部工作原理1:与对方套接字的连接
通过三次握手,主机A和主机B确认了彼此均就绪。
收发数据前向数据包分配序号,并向对方通报此序号,这都是为防止数据丢失所做的准备。
TCP内部工作原理2:与对方主机的数据交换
为了完成数据包重传,TCP套接字启动计时器以等待ACK应答。若相应计时器发生超时则重传。
CP内部工作原理3:断开与套接字的连接
四次握手:先由套接字A向套接字B传递断开连接的消息,套接字B发出确认收到的消息,准备就绪后向向套接字A传递可以断开连接的消息,套接字A同样发出确认消息。