TCP与UDP区别
面向TCP连接的socket通信程序:
服务端:创建套接字,指定协议族(sockaddr_in),绑定,监听(listen),接受链接(accept),发送或接收数据;客户端:创建套接字,指定协议族,连接,发送或接收数据
这几个步骤都是必须的。
补
充:在发送和接受数据时:write/send/sendto,read/recv/recvfrom都可以用,通常会用:send,recv;但需要注
意的是:在面向UDP的socket程序中,发送数据时,如果用sendto的话,就不用connect了;但是,在面向TCP的程序中,在发送数据时,
即使sendto,也必须connect,也就是说connect这一步是必不可少的。
面向UDP连接的socket通信程序:
服务端:创建套接字,指定协议族(sockaddr_in),绑定(不需要listen和accept),发送或接收数据;客户端:创建套接字,指定协议族,连接(和TCP的客户端步骤一样),发送或接收数据。
补
充:在发送和接收数据时,和TCP大同小异,write/send/sendto,read/recv/recvfrom都可以用,但UDP通常会用
sendto,recvfrom;需要注意的是:当用sendto发送数据的时候,就不用connect了(用了也没事),其他的
(write,send)必须connect。
补充:无论是TCP还是UDP,默认情况下创建的都是阻塞模式(blocking)的套接字,执行到
accept,connect,write/send/sendto,read/recv/recvfrom等语句时,会一直等待(connect有点例
外,它连接一段时间,如果连接不成功,会以错误形式返回,不会一直等待)。
可以把socket设置成非阻塞模式,linux下用fcntl函
数,windows下用的是ioctlsocket函数。(TCP和UDP设置成非阻塞模式以后,效果是一样的,都不再等待,而是立即返回。只是
sendto和send一次发送的最大数据量可能不同,两种模式下返回的错误代码应该也是相同的)
设置成非阻塞模式以后,这些函数不再等待会立即
返回(这和windows下是相同的),至于错误时返回的值应该也是和windows下相同的(具体没试,send和recv在windows错误时返回
的值,请看2011-4-27的博客:“套接字的同步阻塞(blocking)与异步非阻塞(no blocking)”)。
TCP面向连接,UDP面向无连接(在默认的阻塞模式下):
read/recv/recvfrom:当客户端退出程序或断开连接
时,TCP的这个函数会立即返回不再阻塞(因为服务端自己知道客户端已经退出或断开连接,证明它是面向连接的),而UDP的这个函数将会始终保持阻塞(因
为服务端自己不知道客户端已经退出或断开连接,证明它是面向无连接的)。
TCP无边界,UDP有边界(在默认的阻塞模式下):
read/recv
/recvfrom:TCP,客户端连续发送数据,只要服务端的这个函数的缓冲区足够大,会一次性接收过来(客户端是分好几次发过来,是有边界的,而服务
端却一次性接收过来,所以证明是无边界的);UDP:客户端连续发送数据,即使服务端的这个函数的缓冲区足够大,也只会一次一次的接收,发送多少次接收多
少次(客户端分几次发送过来,服务端就必须按几次接收,从而证明,这种UDP的通讯模式是有边界的)。
补充(来自网络):
1.socket()的参数不同
2.UDP Server不需要调用listen和accept
3.UDP收发数据用sendto/recvfrom函数
4.UDP:shutdown函数无效
5.TCP:地址信息在connect/accept时确定
UDP:在sendto/recvfrom函数中每次均需指定地址信息
Sendto()和recvfrom()用于在无连接的数据报socket方式下进行数据传输。由于本地socket并没有与远端机器建立连接,所以在发送数据时应指明目的地址。
sendto()函数原型为:
int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen);
该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。Sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。
Recvfrom()函数原型为:
int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
from是一个struct sockaddr类型的变量,该变量保存源机的IP地址及端口号。fromlen常置为sizeof (struct
sockaddr)。当recvfrom()返回时,fromlen包含实际存入from中的数据字节数。Recvfrom()函数返回接收到的字节数或
当出现错误时返回-1,并置相应的errno。
如果你对UDP模式的socket调用了connect()函数时,你也可以利用send()和recv()进行数据传输,但该socket仍然是 数据报socket,并且利用传输层的UDP服务。但在发送或接收数据报时,内核会自动为之加上目地和源地址信息。(这一点正说明了我在“面向UDP连接 的socket通信程序”中所补充的内容)
总结:从sendto和recvfrom的后两个参数想到的:当客户端向服务端发送数据时,客户端必须知道服务端的IP地址和端口号,而结构体 sockaddr_in正是完成了这项工作。所以在客户端程序中,一定要指定需要连接的服务端的IP地址和端口号(TCP中必须指定,因为它是面向连接 的,交换数据前必须connect,而connect时就必须用到那个结构体;UDP可以不指定,那就需要让服务端先发送数据到客户端,客户端通过 recvfrom函数接收,从而通过参数from得到了存放服务端IP地址和端口号的那个结构体,然后就可以通过它来交换数据了,后来一想这样不行,因为 服务端向客户端发送数据时,在sendto中要指定客户端的IP地址和端口号,这个就又需要客户端也创建一个套接字打开一个端口并把它们绑定在一起接受连 接。从而,得出的结论是:UDP中:无论服务端还是客户端,开始时,发送数据方一定要在程序中指定接收数据方的IP和端口,接收方通过recvfrom得 到数据以后也就得到了发送方的IP和端口就可以通过这个结构体发送数据了实现了数据的双向传递(好像不对,UDP面向无连接,这个连接有可能过一会就断掉 了,更重要的是,发送方根本就没有和socket绑定,也就是说那个端口是临时分配的,所以我认为即使成功也是偶然,有待证明);TCP不同:它是必须在 客户端指定服务端的IP和端口,建立连接,然后两者可以任意接收和发送数据,实现双向数据传递)。
另外:
TCP在运行客户端connect之前必须先运行服务端,不然的话connect的连接会出错;UDP不一样(客户端发送信息,服务端接收信息,服 务端需要绑定,客户端就需要指定服务端的IP和端口),先运行客户端和后运行客户端一样通信,只是当先运行客户端后运行服务端时,从客户端起来到服务端起 来的这段时间内发送的数据服务端就收不到了(这也从另个方面体现了TCP的面向连接和UDP的面向无连接)。还有一点需要注意:UDP中,客户端先运行, 用connect+write/send/sendto:在服务端起来之前不发送数据过去,起来之后,和后运行客户端收发数据一样,在服务端起来之前发送 数据了,那么服务端起来之后,客户端第一次发送的数据,服务端会收不到,再发就一样了;用sendto(不用connect)的话,则不管服务端起来之前 发没发数据,服务端起来之后都照常收发数据,从客户端起来到服务端起来的这段时间内发送的数据服务端就收不到了(这一点,或许是和上面所说的“如果你对 UDP模式的socket调用了connect()函数时,你也可以利用send()和recv()进行数据传输,但该socket仍然是数据报 socket,并且利用传输层的UDP服务。但在发送或接收数据报时,内核会自动为之加上目地和源地址信息”有关,有待进一步证明)。
最后再补充一个小知识点:sizeof()这个函数,参数是一个变量时根本不用括号(用的话也行),只要用空格隔开即可;但是如果是一个数据类型的话,则必须用括号。
一、TCP与UDP的区别
基于连接与无连接
对系统资源的要求(TCP较多,UDP少)
UDP程序结构较简单
流模式与数据报模式
TCP保证数据正确性,UDP可能丢包
TCP保证数据顺序,UDP不保证
部分满足以下几点要求时,应该采用UDP 面向数据报方式 网络数据大多为短消息
拥有大量Client
对数据安全性无特殊要求
网络负担非常重,但对响应速度要求高
具体编程时的区别 socket()的参数不同
UDP Server不需要调用listen和accept
UDP收发数据用sendto/recvfrom函数
TCP:地址信息在connect/accept时确定
UDP:在sendto/recvfrom函数中每次均 需指定地址信息
UDP:shutdown函数无效
二、man----socket
通过查看socket的man手册可以看到socket函数的第一个参数的值可以为下面这些值:
Name Purpose
PF_UNIX, PF_LOCAL Local
communication
PF_INET IPv4 Internet
protocols
PF_INET6 IPv6 Internet
protocols
PF_IPX IPX - Novell
protocols
PF_NETLINK Kernel user
interface device
PF_X25 ITU-T X.25 /
ISO-8208 protocol
PF_AX25 Amateur radio
AX.25 protocol
PF_ATMPVC Access to raw
ATM PVCs
PF_APPLETALK Appletalk
PF_PACKET Low level
packet interface
三、编程区别
通常我们在说到网络编程时默认是指TCP编程,即用前面提到的socket函数创建一个socket用于TCP通讯,函数参数我们通常填为
SOCK_STREAM。即socket(PF_INET, SOCK_STREAM, 0),这表示建立一个socket用于流式网络通讯。
SOCK_STREAM这种的特点是面向连接的,即每次收发数据之前必须通过connect建立连接,也是双向的,即任何一方都可以收发数据,协议本身提供了一些保障机制保证它是可靠的、有序的,即每个包按照发送的顺序到达接收方。
而SOCK_DGRAM这种是User Datagram
Protocol协议的网络通讯,它是无连接的,不可靠的,因为通讯双方发送数据后不知道对方是否已经收到数据,是否正常收到数据。任何一方建立一个
socket以后就可以用sendto发送数据,也可以用recvfrom接收数据。根本不关心对方是否存在,是否发送了数据。它的特点是通讯速度比较
快。大家都知道TCP是要经过三次握手的,而UDP没有。
基于上述不同,UDP和TCP编程步骤也有些不同,如下:
TCP编程的服务器端一般步骤是:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt(); * 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();
4、开启监听,用函数listen();
5、接收客户端上来的连接,用函数accept();
6、收发数据,用函数send()和recv(),或者read()和write();
7、关闭网络连接;
8、关闭监听;
TCP编程的客户端一般步骤是:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
4、设置要连接的对方的IP地址和端口等属性;
5、连接服务器,用函数connect();
6、收发数据,用函数send()和recv(),或者read()和write();
7、关闭网络连接;
与之对应的UDP编程步骤要简单许多,分别如下:
UDP编程的服务器端一般步骤是:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();
4、循环接收数据,用函数recvfrom();
5、关闭网络连接;
UDP编程的客户端一般步骤是:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
4、设置对方的IP地址和端口等属性;
5、发送数据,用函数sendto();
6、关闭网络连接;

浙公网安备 33010602011771号