1 UDP的特点
UDP提供不可靠的交付,但也有优点:
1) 发送数据前不需要建立连接,减少开销与发送数据的延迟。
2) UDP不使用拥塞控制,不保证交付,减少了复杂的连接状态表,网络的拥塞不会使源主机的发送速率降低。
3) 数据报首部字节比TCP少,节约开销。
所以UDP适用于实时应用,网络拥塞时允许丢失一点数据。
2 TCP的特点
TCP是面向连接的运输层协议,它提供双工和可靠交付的服务。
TCP与UDP的最大区别是:TCP是面向连接的,而UDP是无连接的。
TCP的的可靠性基于:
1) 基于确认请求机制的网络协议,经过确认的报文,可保证可靠性,最新发出的报文无法保证。
2) 保证数据按序到达。
3) 丢包重传。
4) 经历面向连接保证可靠性。
5) 进行流量控制。
6) 提供定时器(超时重传定时器)。
7) 逻辑上实现网络拥塞。
3 对以上几点的解释
1) TCP的确认是对接收到的数据的最高序号(即收到的数据流中的最后一个序号)表示确认。但接收端返回的确认号是己收到的数据的最高序号加1也就是说,确认号表示接收端期望下次收到的数据中的第一个数据字节的序号。
TCP传输的可靠是由于使用了序号和确认。当TCP发送一报文段时,它同时也在自己的重传队列中存放一个副本。若收到确认,则删除此副本。若在计时器时间到之前没有收到确认,则重传此报文段的副本。TCP的确认并不保证数据己由应用层交付给了端用户,而只是表明在接收端的TCP收到了对方所发送的报文段。
2) 流量控制(点对点通信的控制),TCP采用大小可变的滑动窗口进行流量控制。流量控制就是控制发送端的数据,使得来得及接受。发送端发送大小大于接收端接收时,丢失。也就是我,接受端向告诉发送端自己所能接收的字节大小,发送端可以分批发送,但不可超过允许发送端发送数据的大小。
3) 拥塞控制,防止过多的数据注入到网络中,使网络中的路由器或链路不致过载。拥塞的产生:
当网络的吞吐量明显地小于理想的吞吐量时,网络就进入了轻度拥塞的状态。当提供的负载达到某一数值时,网络的吞吐量反而随提供的负载的增大而下降,这时网络就进入了拥塞状态。当提供的负载继续增大到某一数值时,网络的吞吐量就下降到零,网络已无法工作。这就是所谓的死锁。
拥塞控制方法:1> 慢开始:从小增大拥塞窗口控制(倍数增加)。 2> 拥塞避免:从小增大拥塞窗口控制(常数增加)
3> 快重传:发送端连续收到三个重复的ACK,断定分组丢失,此时重传丢失的报文,不等待定时器的超时。 4> 快恢复:与快重传配合使用。
4) TCP每发送一个报文段,就对这个报文段设置一次定时器,如果重传时间到但没收到确认,重传这一报文。
4 TCP三次握手,四次挥手
TCP的flags的五中状态:SYN表示建立连接,FIN表示关闭连接,ACK表示响应,PSH表示有 DATA数据传输,RST表示连接重置。
设客户进运行在主机A中。它先向其TCP发出主动打开命令,表明要向某个IP地址的某个端囗建立运输连接。
主机A的TCP向主机B的TCP发出连接请求报文段,其首部中的同步比特SYN应置为l,同时选择一个序号x,表明在后面传送数据时的第一个数据字节的序号是x+1。
主机B的TCP收到连接请求报文段后,如同意,则发回确认。在确认报文段中应将SYN和ACK置为1,确认号应为x+1,同时也为自己选抒一个序号y。
主机A的TCP收到此报文段后,还要向B给出确认,其ACK置1,确认号为y+1。
运行客户进程的主机A的TCP通知上层应用进程,连接已经建立(或打开)。
当运行服务器进程的主机B的TCP收到主机A的确认后.也通知其上层应用进程,连接己经建立。
挥手与此类似。
5 TCP与UDP通信协议程序
单进程代码:
tcp_server
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
static void useage(const char* proc)
{
printf("Useage: %s [local_ip] [local_port]\n",proc);
}
int startup(const char* ip, int port)
{
int sock = socket(AF_INET, SOCK_STREAM,0);
if(sock < 0)
{
perror("socket");
exit(1);
}
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = inet_addr(ip);
if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
{
perror("bind");
exit(2);
}
if(listen(sock, 10) < 0)
{
perror("listen");
exit(3);
}
return sock;
}
int main(int argc, char *argv[])
{
if(argc != 3)
{
useage(argv[0]);
return 4;
}
int sock = startup(argv[1], atoi(argv[2]));
while(1)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int new_fd = accept(sock,(struct sockaddr*)&client, &len);
if(new_fd < 0)
{
perror("accept");
continue;
}
char buf[1024];
while(1)
{
ssize_t s = read(new_fd, buf,sizeof(buf)-1);
if(s > 0)
{
buf[s] = 0;
printf("client: %s\n",buf);
write(new_fd, buf, strlen(buf));
}
else if(s == 0)
{
printf("client is done");
break;
}
else
{
perror("read");
return 5;
}
}
}
return 0;
}
tcp_client
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
static void useage(const char* proc)
{
printf("Useage: %s [local_ip] [local_port]\n",proc);
}
int main(int argc, char *argv[])
{
if(argc != 3)
{
useage(argv[0]);
return 1;
}
int sock = socket(AF_INET, SOCK_STREAM,0);
if(sock < 0)
{
perror("socket");
return 2;
}
struct sockaddr_in peer;
peer.sin_family = AF_INET;
peer.sin_port = htons(atoi(argv[2]));
peer.sin_addr.s_addr = inet_addr(argv[1]);
if(connect(sock, (struct sockaddr*)&peer, sizeof(peer)) < 0)
{
perror("connect");
return 3;
}
char buf[1024];
while(1)
{
printf("please enter# ");
fflush(stdout);
ssize_t s = read(0, buf,sizeof(buf)-1);
if(s > 0)
{
buf[s-1] = 0;
write(sock, buf, strlen(buf));
ssize_t _s = read(sock, buf, strlen(buf)-1);
if(_s > 0)
{
buf[_s] = 0;
}
else if(_s == 0)
{
printf("server quit\n");
break;
}
else
{
perror("read");
break;
}
}
}
close(sock);
return 0;
}