您的位置:首页 > 理论基础 > 计算机网络

Linux上TCP,UDP以及广播与多播通信(代码大全)

2013-11-20 15:17 267 查看
Linux下TCP,UDP以及广播与多播通信(代码大全)

TCP、UDP、广播、多播的客户端服务器代码链接地址为(for free):

tcp代码:http://download.csdn.net/detail/huangminqiang201209/4860661

udp代码:http://download.csdn.net/detail/huangminqiang201209/4860665

广播代码:http://download.csdn.net/detail/huangminqiang201209/4860672

多播代码:http://download.csdn.net/detail/huangminqiang201209/4860719



TCP

Transmission Control Protocol 传输控制协议TCP是一种面向连接(连接导向)的、可靠的、基于字节流的运输层(Transport layer)通信协议,由IETF的RFC 793说明(specified)。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,UDP是同一层内另一个重要的传输协议。 

在因特网协议族(Internet protocol suite)四层协议中,TCP层是位于IP层之上,应用层之下的传输层。不同主机的应用层之间经常需要可靠的、像管道一样的连接,但是IP层不提供这样的流机制,而是提供不可靠的包交换。  

 

UDP

UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是 OSI 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC 768是UDP的正式规范。



广播

广播和多播仅应用于UDP,它们对需将报文同时传往多个接收者的应用来说十分重要。TCP是一个面向连接的协议,它意味着分别运行于两主机(由IP地址确定)内的两进程(由端口号确定)间存在一条连接。

考虑包含多个主机的共享信道网络如以太网。每个以太网帧包含源主机和目的主机的以太网地址(48 bit)。通常每个以太网帧仅发往单个目的主机,目的地址指明单个接收接口,因而称为单播(unicast)。在这种方式下,任意两个主机的通信不会干扰网内其他主机(可能引起争夺共享信道的情况除外)。然而,有时一个主机要向网上的所有其他主机发送帧,这就是广播。通过ARP和RARP可以看到这一过程。多播(multicast) 处于单播和广播之间:帧仅传送给属于多播组的多个主机。



多播

多播数据仅由对该数据报感兴趣的接口接收,也就是说,由运行希望参加多播会话应用系统的主机上的接口接收。广播一般局限与局域网,而多播既可用于局域网,也可用于广域网。

IP多播提供两类服务:

1) 向多个目的地址传送数据。有许多向多个接收者传送信息的应用:例如交互式会议系统和向多个接收者分发邮件或新闻。如果不采用多播,目前这些应用大多采用TCP来完成(向每个目的地址传送一个单独的数据复制)。然而,即使使用多播,某些应用可能继续采用TCP来保证它的可靠性。

2) 客户对服务器的请求。例如,无盘工作站需要确定启动引导服务器。目前,这项服务是通过广播来提供的,但是使用多播可降低不提供这项服务主机的负担。



函数

1.socket 函数

指定期望的通信协议类型。

#include <sys/types.h> /* See NOTES */

#include <sys/socket.h>

int socket(int domain, int type, int protocol);

返回:若成功则为非负描述符,出错则为-1。

参数说明:

domain: 指明协议族,也称为协议域,是一个常值。

AF_INET IPv4 协议

AF_INET6 IPv6 协议

AF_LOCAL/AF_UNIX Unix协议域

AF_ROUTE 路由套接字

AF_KEY 密匙套接字



type: 指明套接字的类型。

SOCK_STREAM 字节流套接字(TCP)

SOCK_DGRAM 数据报套接字(UDP)

SOCK_SEQPACKET 有序分组套接字

SOCK_RAW 原始套接字



protocol: 指明协议类型。一般为0,以选择给定的domain和type组合的系统默认值。

IPPROTO_TCP TCP传输协议

IPPROTO_UDP UDP传输协议

IPPROTO_SCTP SCTP传输协议

函数描述:

socket 函数在成功时返回一个小的非负整数值,与文件描述符类似,我们称它为套接字描述符,简称 sockfd。为了得到这个套接字描述符,我们只是指定了协议族(IPv4、IPv6

或Unix)和套接字类型(字节流、数据报或原始套接字)。我们并没有指定本地跟远程的协议地址。



2.bind 函数

将一个本地协议地址赋予一个套接字。对于网际网协议,协议地址是32位的IPv4地址和128位的IPv6地址与16位的TCP或UDP端口号的组合。bind 函数主要用于服务器端,用来指定本地

主机的哪个网络接口(IP,可以是INADDR_ANY,表示本地主机的任一网络接口)可以接受客户端的请求,和指定端口号(即开启的等待客户来连接的进程)。



#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);

返回:若成功则为0,出错则为-1。

参数说明:

sockfd: socket 函数返回的套接字描述符。

myaddr、addrlen:指向一个套接字地址结构的指针和该结构的大小。

struct sockaddr结构说明如下:

struct sockaddr_in {

   short int sin_family; /* 地址族 */

   unsigned short int sin_port; /* 端口号 */

   struct in_addr sin_addr; /* IP地址 */

   unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大小 */

   };

函数描述:

对于 TCP ,调用 bind 函数可以指定一个端口号,或指定一个IP地址,也可以两者都指定,还可以两者都不指定。

服务器在启动时捆绑它们众所周知的端口号(如何捆绑?)。如果一个TCP客户或服务器未曾调用bind捆绑一个端口,当调用 connect 或 listen 时,内核就要为相应的套接字选择一个临时端口。

让内核来选择临时端口对于TCP客户来说是正常的,除非应用需要一个预留端口。然而对于TCP服务器来说却极为罕见,因为服务器是通过它们的众所周知的端口号来被大家认识的。



进程可以把一个特定的IP捆绑到它的套接字上,不过这个IP地址必须属于其所在主机的网络接口之一(对于TCP服务器)。对于TCP客户,这就为在该套接字上发送的IP数据报指派了源IP地址(服务器源地址)。对于TCP服务器,这就限定该套接字只接收那些目的地为这个IP地址的客户连接。TCP套接字通常不把IP地址捆绑到它的套接字上。当连接套接字时,内核将根据所用外出网络接口来选择源IP地址,而所用外出端口则取决于到达服务器所需的路径。如果TCP服务器没有把IP地址捆绑到它的套接字上,内核就会把发送的SYN的目的IP地址作为服务器的源IP地址(即服务器IP等于INADDR_ANY的情况)。

实际上客户的源IP地址就是服务器的目的地址,服务器的源IP地址就是客户的目的地址,说到底也就只存在两个IP地址:客户IP跟服务器IP。



3.connec函数

t TCP 客户用 connect 函数来与 TCP 服务器建立连接。



#include <sys/socket.h>

int connect( int sockfd, const struct sockaddr *servaddr, socklen_t addrlen );

返回:若成功则为0,出错则为-1。

参数说明:

sockfd: 由 socket 函数返回的套接字描述符。

servaddr、addrlen:指向一个套接字地址结构的指针和该结构的大小。套接字地址结构必须含有服务器的IP地址和端口号。

函数描述:

客户在调用 connect 函数前并不一定得调用 bind 函数,如果需要的话,内核会确定源P地址,并选择一个临时端口作为源端口。所以在客户进程中的套接字一般只需指明客户所要连接的服务器的IP跟端口号。

如果是 TCP 套接字,调用 connect 函数将激发 TCP 的三路握手。而且仅在连接成功或出错时才返回。其中出错的情况有如下几种:

1-> TCP 客户没有收到 SYN 分节的响应。

2-> TCP 服务器对客户的 SYN 分节的响应是 RST 。

3-> 客户发出的 SYN 分节在某个路由器器上发生了错误。

若 connect 调用失败则该套接字不再可用,必须关闭,我们不能对这样的套接字再次执行 connect 函数。



4.listen 函数

#include <sys/socket.h>

int listen(int sockfd, int backlog);

返回:若成功则为0,出错则为-1。

函数描述:

listen 函数仅由 TCP 服务器调用,它做两件事情。

(1)把一个未连接的套接字(主动)转换成一个被动套接字,指示内核应该接受指向该套接字的连接请求。

(2)backlog 参数规定了内核应该为相应套接字排队的最大连接数。其中内核始终为监听套接字维护两个队列。

(1)未完成连接队列,每个SYN分节对于其中一项:

已由某个客户发出并到达服务器,而服务器正在等待待完成的TCP三路握手过程。这些套接字处于SYN_RCVD状态。

(2)已完成连接队列

每个已完成TCP三路握手过程的客户对应其中一项。这些套接字处于ESTABLISHED状态。

backlog 就是这两个队列和的最大值。

在三路握手完成之后,但在服务器调用 accept 之前到达的数据应由 TCP 服务器排队,最大数据量为相应已连接套接字的接收缓冲区的大小。



5.accept 函数

accept 函数由 TCP 服务器调用,用于从已完成连接队列头返回下一个已完成连接。如果已完成队列为空,那么进程被投入睡眠(假设套接字为默认的阻塞方式)。

#include <sys/socket.h>

int accept(int sockfd ,struct sockaddr *cliaddr, socklen_t *addrlen);

返回:若成功则为非负已连接描述符和对端的IP和端口号,出错则为-1。

参数说明:

cliaddr、addrlen 用来返回已连接的对端进程(客户)的协议地址 。调用前,我们将由 *addrlen 所引用的整数值置为由cliaddr所指的套接字地址结构的长度,返回时,该整数值即为内核存放在该套接字地址机构内的确切字节数。

函数描述:

如果 accept 调用成功,那么其返回值是由内核自动生成的一个全新描述符,代表着与所返回客户的TCP连接。在讨论 accept 函数时,我们称它的第一个参数为监听套接字描述符(由 socket 创建,随后用作bind 和 listen 的第一个参数的描述符),称它的返回值为已连接套接字描述符。区分这两个套接字非常重要。一个服务器通常仅仅创建一个监听套接字,它在服务器的生命期内一直存在。内核为每个服务器进程接受的客户连接创建一个已连接套接字(也就是说对于它的TCP三路握手过程已经完成)。当服务器完成对某个连接客户的服务时,相应的已连接套接字就要被关闭。



6.recv/recvfrom函数

从套接字上接收一个消息。对于recvfrom ,可同时应用于面向连接的和无连接的套接字。recv一般只用在面向连接的套接字,几乎等同于recvfrom,只要将recvfrom的第五个参数设置NULL。如果消息太大,无法完整存放在所提供的缓冲区,根据不同的套接字,多余的字节会丢弃。假如套接字上没有消息可以读取,除了套接字已被设置为非阻塞模式,否则接收调用会等待消息的到来。

#include <sys/types.h>

#include <sys/socket.h>

ssize_t recv(int sock, void *buf, size_t len, int flags);

ssize_t recvfrom(int sock, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);

参数:

sock:索引将要从其接收数据的套接字。

buf:存放消息接收后的缓冲区。

len:buf所指缓冲区的容量。

flags:是以下一个或者多个标志的组合体,可通过or操作连在一起

MSG_DONTWAIT:操作不会被阻塞。

MSG_ERRQUEUE:指示应该从套接字的错误队列上接收错误值,依据不同的协议,错误值以某种辅佐性消息的方式传递进来,使用者应该提供足够大的缓冲区。导致错误的原封包通过msg_iovec作为一般的数据来传递。导致错误的数据报原目标地址作为msg_name被提供。错误以sock_extended_err结构形态被使用,定义如下

#define SO_EE_ORIGIN_NONE 0

#define SO_EE_ORIGIN_LOCAL 1

#define SO_EE_ORIGIN_ICMP 2

#define SO_EE_ORIGIN_ICMP6 3

struct sock_extended_err

{

u_int32_t ee_errno; /* error number */

u_int8_t ee_origin; /* where the error originated */

u_int8_t ee_type; /* type */

u_int8_t ee_code; /* code */

u_int8_t ee_pad;

u_int32_t ee_info; /* additional information */

u_int32_t ee_data; /* other data */

/* More data may follow */

};

MSG_PEEK:指示数据接收后,在接收队列中保留原数据,不将其删除,随后的读操作还可以接收相同的数据。

MSG_TRUNC:返回封包的实际长度,即使它比所提供的缓冲区更长, 只对packet套接字有效。

MSG_WAITALL:要求阻塞操作,直到请求得到完整的满足。然而,如果捕捉到信号,错误或者连接断开发生,或者下次被接收的数据类型不同,仍会返回少于请求量的数据。

MSG_EOR:指示记录的结束,返回的数据完成一个记录。

MSG_TRUNC:指明数据报尾部数据已被丢弃,因为它比所提供的缓冲区需要更多的空间。

MSG_CTRUNC:指明由于缓冲区空间不足,一些控制数据已被丢弃。

MSG_OOB:指示接收到out-of-band数据(即需要优先处理的数据)。

MSG_ERRQUEUE:指示除了来自套接字错误队列的错误外,没有接收到其它数据。

from:指向存放对端地址的区域,如果为NULL,不储存对端地址。

fromlen:作为入口参数,指向存放表示from最大容量的内存单元。作为出口参数,指向存放表示from实际长度的内存单元。



7.send/sendto函数

用于发送消息。send只可用于基于连接的套接字,send 和 write唯一的不同点是标志的存在,当标志为0时,send等同于write。sendto 和 sendmsg既可用于无连接的套接字,也可用于基于连接的套接字。除了套接字设置为非阻塞模式,调用将会阻塞直到数据被发送完。



#include <sys/types.h>

#include <sys/socket.h>

ssize_t send(int sock, const void *buf, size_t len, int flags);

ssize_t sendto(int sock, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);

ssize_t sendmsg(int sock, const struct msghdr *msg, int flags);

参数:

sock:索引将要从其发送数据的套接字。

buf:指向将要发送数据的缓冲区。

len:以上缓冲区的长度。len可以大于sizeof(buf),例如send("123",10)-->则实际发送10字节数据

flags:是以下零个或者多个标志的组合体,可通过or操作连在一起

MSG_DONTROUTE:不要使用网关来发送封包,只发送到直接联网的主机。这个标志主要用于诊断或者路由程序。

MSG_DONTWAIT:操作不会被阻塞。

MSG_EOR:终止一个记录。

MSG_MORE:调用者有更多的数据需要发送。

MSG_NOSIGNAL:当另一端终止连接时,请求在基于流的错误套接字上不要发送SIGPIPE信号。

MSG_OOB:发送out-of-band数据(需要优先处理的数据),同时现行协议必须支持此种操作。

to:指向存放接收端地址的区域,可以为NULL。

tolen:以上内存区的长度,可以为0。

msg:指向存放发送消息头的内存缓冲,结构形态如下

struct msghdr {

void *msg_name;

socklen_t msg_namelen;

struct iovec *msg_iov;

size_t msg_iovlen;

void *msg_control;

socklen_t msg_controllen;

int msg_flags;

};

可能用到的数据结构有

struct cmsghdr {

socklen_t cmsg_len;

int cmsg_level;

int cmsg_type;

};



8.getsockname 函数

  获取一个套接口的本地名字。

#include <sys/socket.h>

int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);

返回:成功则不返回,出错则为-1。

参数:

 sockfd:标识一个已捆绑套接口的描述字。   

localaddr:接收套接口的地址(名字)。   

addrlen:名字缓冲区长度。

函数描述:

getsockname()函数用于获取一个套接字的名字。它用于一个已捆绑或已连接套接字sockfd,本地地址将被返回。本调用特别适用于如下情况:未调用bind()就调用了connect(),这时唯有getsockname()调用可以获知系统内定的本地地址。在返回时,namelen参数包含了名字的实际字节数。   

若一个套接字与INADDR_ANY捆绑,也就是说该套接字可以用任意主机的地址,此时除非调用connect()或accept()来连接,否则getsockname()将不会返回主机IP地址的任何信息。除非套接字被连接,WINDOWS套接字应用程序不应假设IP地址会从INADDR_ANY变成其他地址。这是因为对于多个主机环境下,除非套接字被连接,否则该套接字所用的IP地址是不可知的。



9.getpeername 函数函数

 获取与套接口相连的端地址。

int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);

返回:成功则不返回,出错则为-1。

函数描述:

 getpeername()函数用于从端口sockfd中获取与它捆绑的端口名,并把它存放在sockaddr类型的name结构中。它适用于数据报或流类套接口。



10.setsockopt/getsockopt函数

设置/获取套接口选项

#include <winsock.h>

int setsockopt(int sockfd, int level, int optname, void *optval, int optlen);

int getsockopt(int sockfd, int level, int optname, void *optval, int *optlen) ;

参数:

s:标识一个套接口的描述字。

level:选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。

optname:需设置的选项。

optval:指针,指向存放选项值的缓冲区。

optlen:optval缓冲区的长度。

注释:

setsockopt()函数用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。选项影响套接口的操作,诸如加急数据是否在普通数据流中接收,广播数据是否可以从套接口发送等等。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: