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

2021年11月8日 1点热度 0条评论 来源: embed_huang

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服务器,所以就把socket这块完整的熟悉了一下,以下这些整理的比较随意,还望见谅哦

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()函数用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。选项影响套接口的操作,诸如加急数据是否在普通数据流中接收,广播数据是否可以从套接口发送等等。

 

 

 

    原文作者:embed_huang
    原文地址: https://blog.csdn.net/huangminqiang201209/article/details/8272201
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。