一、TCP与UDP的区别
1 浅分析
TCP | UDP |
可靠传输 | 不可靠传输 |
有连接 | 无连接 |
字节流 | 数据报 |
关于TCP协议与UDP协议的区别:首先我们应该弄清楚,TCP和UDP协议与tcp/ip协议的联系,很多人表示不明白。一直都是说TCP/IP协议与UDP协议的区别,我觉得这是没有从本质上弄清楚网络通信!
TCP/IP协议是一个协议族。里面包括很多的协议。UDP只是其中的一个。之所以命名TCP,IP协议,因为TCP,IP协议是俩个很重要的协议,就用他俩命名了。
TCP/IP协议集包括应用层、传输层、网络层和网络访问层。
其中应用层包括:
超文本传输协议(HTTP):万维网的基本协议;
文件传输(TFTP简单文件传输协议);
远程登陆(Telnet),提供远程访问其他主机功能,她允许用户登陆internet主机,并在这台主机上执行命令;
网络管理(SNMP简单网络管理协议),该协议提供了监视网络设备的方法,以及设备管理,统计信息收集,性能管理等;
域名系统(DNS):该系统用于在internet中将域名及其公共广播的网络节点转换成IP地址。
其次网络层包括:
internet协议(IP);
internet控制信息协议(ICMP);
地址解析协议(ARP);
反向地址解析协议(RARP);
最后说网络访问层:
网络访问层又称为主机到网络层(hton),网络访问层的功能包括IP地址与物理地址硬件大的映射,以及将IP封装成帧,基于不同硬件的网络接口,网络层定义了和物理介质的连接。
TCP:
TCP(传输控制协议)是面向连接的协议,也就是说,在收发数据前,必须建立可靠的连接。一个TCP的连接必须要经过三次“对话”才能建立起来,其中的过程特别的复杂。
TCP三次握手过程:
1.主机A通过向主机B发送一个喊有同步序列号的标志位的数据段给主机B,向主机B请求建立连接,通过这个数据段,主机A告诉主机B俩件事:我想要和你通信;你可以用那个序列号作为起始数据段来响应我。
2.主机B收到主机A的请求后,用一个带有确定应答(ASK)和同步序列号(SYN)标志位的数据段响应主机A,也告诉主机A俩件事,我已经收到你的请求了,你可以传输数据了;你用于那个序列号作为序列号作为起始数据段来回应我。
3.主机A收到这个数据段后,再发送一个确认应答,确认已经收到主机B的数据段;“我已经回复,我现在要开始传输实际的数据了,“这样3次握手就完成了,主机A和主机B就可以传输数据了。
三次握手的特点:
没有应用层的数据;
SYN这个标志位只有在TCP建立连接时才会被置1,握手完成后SYN标志位置0;
TCp建立连接需要三次握手,而断开连接需要进行4次:
1.当主机A完成数据传输后,将FIN置1,提出停止TCP连接的请求;
2.主机B收到FIN后对其做出响应,确认这一方向上的TCP连接将关闭,将ACK置1;
3.由B端再提出反方向的关闭请求,将FIN置1;
4.主机A对主机B的请求进行确认,将ACK置1 ,双方向的关闭结束。
由TCP的三次握手和四次断开可以看出,TCP使用面向连接的通信方式,大大提高了数据通信的可靠性,使发送数据端和接收数据端在数据正式传输前就有了交互,为数据正式传输打下可靠的基础。
TCP的包头结构:
远端口:16位;
目的端口:16位;
序列号:32位;
回应序列号:32位;
TCP头长度:4位;
reserved:6位;
控制代码:6位;
窗口大小:16位;
偏移量:16位;
校验和:16位;
选项:16位;
这样我们得出了TCP包头的最小长度为20字节。
UDP(用户数据包协议):
1.UDP是一个非连接的协议,传输数据之前源端和终端不建立连接,当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、计算机的能力和传输宽带的限制;在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息端。
2.由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务机可以向多个客户机传输相同的消息
3.UDP信息包括的标题很短,只有8个字节,相对于TCP的20个字节信息包的额外开销很小。
4.吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输宽带、源端和终端主机性能的限制。
5.UDP使用尽最大努力交付。即不保证可靠交付,因此主机不需要维护复杂的链接状态表。
6.UDP是面向报文的。发送方UDP对应程序交下来的报文,在添加首部后就向下交付给IP层。即不拆分,也不合并,而是保留这些报文的边界,因此,应用程序需要选择合适的报文大小。
注:我们经常使用”ping “命令来测试倆台主机间TCP/IP通信是否正常,其实”ping“命令的原理就是向对方主机发送UDP数据包,然后对方主机确认收到数据包,如果数据包是否抵达的消息及时反馈回来,那么网络就是通了。
UDP的包头结构:
源端口:16位;
目的端口:16位;
长度:16位;
校验和:16位。
二、UDP与TCP的相关代码
1.UDP的相关代码
服务器代码:
#include<stdio.h> #include<sys/types.h> #include<sys/socket.h> #include<arpa/inet.h> #include<netinet/in.h> #include<string.h> int main(int argc,char* argv[]){ if(argc!=3){ printf("Usage:%s [ip][port]\n",argv[0]); return 3; } //创建套接字 int sock = socket(AF_INET,SOCK_DGRAM,0); if(sock<0){ perror("socket"); return 2; } //创建struct sockaddr_in结构体 struct sockaddr_in local; local.sin_family = AF_INET; local.sin_port = htons(atoi(argv[2])); //将ip地址转换成四字节 local.sin_addr.s_addr = inet_addr(argv[1]); //绑定 if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0){ perror("bind"); return 3; } char buf[1024]; struct sockaddr_in client; while(1){ socklen_t len =sizeof(client); size_t s = recvfrom(sock,buf,sizeof(buf)-1,0,(struct sockaddr*)&client,&len); if(s>0){ buf[s] = 0; printf("[%s:%d]:%s\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port),buf); sendto(sock,buf,strlen(buf),0,(struct sockaddr*)&client,sizeof(client)); } } }
客户端代码:
#include<stdio.h> #include<sys/types.h> #include<sys/socket.h> #include<arpa/inet.h> #include<netinet/in.h> #include<string.h> int main(int argc,char* argv[]){ if(argc!=3){ printf("usage:%s [ip] [port]\n",argv[0]); return 2; } //创建套接字 int sock = socket(AF_INET,SOCK_DGRAM,0); if(sock<0){ perror("socket"); return 2; } //创建struct sockaddr结构体 struct sockaddr_in server; server.sin_family = AF_INET; server.sin_addr.s_addr = inet_addr(argv[1]); server.sin_port = htons(atoi(argv[2])); char buf[1024]; struct sockaddr_in peer; while(1){ socklen_t len = sizeof(peer); printf("Please input#"); fflush(stdout); size_t s = read(0,buf,sizeof(buf)-1); if(s>0){ buf[s-1] = 0; sendto(sock,buf,strlen(buf),0,(struct sockaddr*)&server,sizeof(server)); size_t s = recvfrom(sock,buf,sizeof(buf)-1,0,(struct sockaddr*)&peer,&len); buf[s]=0; printf("server echo#%s\n",buf); } } }
2.TCP相关代码——多线程实现
服务器代码:
#include<stdio.h> #include<stdlib.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include<sys/types.h> #include<string.h> #include<errno.h> #include<pthread.h> typedef struct Arg{ int client_sock; struct sockaddr_in client_socket; char* buf_ip; }Arg; void ProcessRequest(int client_sock,struct sockaddr_in* client_socket,char* buf_ip){ while(1){ char buf[1024]; buf[0] =0; ssize_t s = read(client_sock,buf,sizeof(buf)-1); //未读取到数字 if(s>0){ buf[s] = 0; printf("[%s:%d]say#%s\n",buf_ip,client_socket->sin_port,buf); buf[0] = 0; printf("server:#%s\n",buf); fflush(stdout); s = read(0,buf,sizeof(buf)-1); if(s>0){ buf[s] = 0; } if(strncmp(buf,"quit",4)==0){ printf("quit!\n"); break; } write(client_sock,buf,sizeof(buf)); printf("please wait!\n"); } else if(s==0){ printf("client[%s:%d]quit\n",buf_ip,client_socket->sin_port); break; }else{ continue; } } } void* CreateWorker(void* ptr){ Arg* arg = (Arg*)ptr; ProcessRequest(arg->client_sock,&arg->client_socket,arg->buf_ip); } //主函数 int main(int argc,char* argv[]){ if(argc != 3){ printf("usage:%s [ip] [port]\n",argv[0]); } //创建套接字 int sock = socket(AF_INET,SOCK_STREAM,0); if(sock<0){ printf("socket error!errno is %d,errstring is %s\n",errno,strerror(errno)); exit(1); } //创建struct sockaddr结构体 struct sockaddr_in server_socket; struct sockaddr_in client_socket; //清空结构体server_socket,并进行赋值 //bzero(&server_socket,sizeof(server_socket)); server_socket.sin_family = AF_INET; server_socket.sin_port = htons(atoi(argv[2])); server_socket.sin_addr.s_addr = inet_addr(argv[1]);//注意此处的inet_addr的使用 if(bind(sock,(struct sockaddr*)&server_socket,sizeof(server_socket))<0){ printf("bind error!errno is %d,errstring is %s\n",errno,strerror(errno)); close(sock); return 2; } //监视 if(listen(sock,5)<0){ printf("listen error! errno is %d,errstring is %s\n",errno,strerror(errno)); return 3; } printf("bind and listen success,wait accept!\n"); for(;;){ //链接 int len = sizeof(client_socket); int client_sock = accept(sock,(struct sockaddr*)&client_socket,&len);//此处的len位输入输出参数 if(client_sock<0){ printf("accept error!"); continue; } char buf_ip[24]; inet_ntop(AF_INET,&client_socket.sin_addr,buf_ip,sizeof(buf_ip)); printf("get connect!ip is %s,port is %d!\n",buf_ip,ntohs(client_socket.sin_port)); pthread_t tid; Arg* arg = (Arg*)malloc(sizeof(Arg)); arg->client_sock = client_sock; arg->client_socket = client_socket; arg->buf_ip = buf_ip; pthread_create(&tid,NULL,CreateWorker,(void*)arg); pthread_detach(tid); } return 0; }
客户端代码:
#include<stdio.h> #include<sys/types.h> #include<sys/socket.h> #include<arpa/inet.h> #include<netinet/in.h> #include<string.h> //主函数 int main(int argc,char* argv[]){ if(argc != 3){ printf("usage:%s [ip] [port]\n",argv[0]); } //创建套接字 int sock = socket(AF_INET,SOCK_STREAM,0); if(sock<0){ printf("socket error!\n"); return 1; } //创建struct sockaddr_in 结构体 struct sockaddr_in client_socket; client_socket.sin_family = AF_INET; client_socket.sin_port = htons(atoi(argv[2])); client_socket.sin_addr.s_addr = inet_addr(argv[1]); //链接 int ret = connect(sock,(struct sockaddr*)&client_socket,sizeof(client_socket)); if(ret <0){ printf("connet error!\n"); return 2; } printf("connect success!\n"); char buf[1024]; buf[0] = 0; while(1){ printf("client:#"); fflush(stdout); ssize_t s = read(0,buf,sizeof(buf)-1); if(s>0){ buf[s-1] = 0; } write(sock,buf,sizeof(buf)); if(strncmp(buf,"quit",4)==0){ printf("quit!\n"); break; } printf("please wait!\n"); read(sock,buf,sizeof(buf)); printf("server:$%s",buf); } close(sock); return 0; }