一、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
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#include
//主函数
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;
}