Linux-----TCP与UDP

一、TCP与UDP的区别

 1 浅分析

                                                TCP                                              UDP
可靠传输 不可靠传输
有连接 无连接
字节流 数据报
 2.深分析

       关于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;
}

你可能感兴趣的:(网络)