Linux系统编程之网络编程

网络编程第一天:

进行网络通信(TCP通信)

客户端:

1.创建socket

2.bind 绑定

3.connect 连接

4.recv send 数据接受发送

5.close 关闭套接字

服务器端:

1.创建套接字 socket

2.bind绑定

3.listen 监听(检测) 最大连接数

4.accept 响应

5.send、recv 接受发送

6.close 关闭

第一个函数 创建socket

  函数原型 int socket(int domain, int type, int protocol);
  头 文 件:#include <sys/socket.h>
  参      数:    domain: 是地址族
        AF_INET
                     PF_INET  // internet 协议
                      PF_UNIX // unix internal协议
                       PF_NS      // Xerox NS协议
                      PF_IMPLINK  // Interface Message协议
                    type  : 套接字类型】】
                         SOCK_STREAM   // 流式套接字 	TCP常用
                        SOCK_DGRAM    // 数据报套接字	UDP常用
                         SOCK_RAW         //  原始套接字

  protocol :参数通常置为0
返回值: 如果成功返回socket描述符 失败返回-1,错误存入errno中
写个代码 创建套接字

第二个函数 bind绑定

     函数原型: int bind(int sockfd, const struct sockaddr *addr,    socklen_t addrlen);
     函数参数:sockfd 是socket描述符  socket函数的返回值
                      addr   struct sockaddr的结构体变量的首地址,指向 sockaddr_in
                      addrlen  是  sizeof (struct sockaddr_in)

                      struct sockaddr //通用结构体 {
                              sa_family_t sa_family; //2字节:地址族, AF_xxx
                               char sa_data[14];	//14字节:协议地址
                        };
                       struct sockaddr_in //专用IPv4结构体{
                   sa_family_t sin_family; /* 2字节:地址族: AF_INET或AF_INET6等 */
                               in_port_t sin_port;	/* 2字节:端口按网络字节顺序排列 */  
                               struct in_addr sin_addr; /* 4字节:ip互联网地址 */
                               char sin_zero[8];  8 bytes unused,作为填充
                         };
                     struct in_addr {
                             uint32_t s_addr; /* 4字节:以网络字节顺序排列的地址 */  	};

第三个函数:字节序的转换

     #include <arpa/inet.h>

   uint32_t htonl(uint32_t hostlong);
   作用:本机字节序转换long类型的网络字节序

   uint16_t htons(uint16_t hostshort);
   作用: 本机字节序转换成short类型的网络字节序

   uint32_t ntohl(uint32_t netlong);
  作用: 网络字节序转换成long类型的本地字节序

   uint16_t ntohs(uint16_t netshort);
  作用: 网络字节序转换成short类型的本地字节序。

    作用: 把ip和端口和当前程序绑定

第四个函数:字符串转换成网络字节序

 #include <sys/socket.h>
   #include <netinet/in.h>
   #include <arpa/inet.h>

   函数原型:in_addr_t   inet_addr(const char *cp);
   函数作用: 将字符串转换成网络字节序
  参数: 需要转换的字符串
   返回值: 网络字节序。

   函数原型:char *inet_ntoa(struct in_addr in);
   函数参数: in_addr 结构体变量
   返回值:  点分十进制字符串

第五个函数:监听

int listen(int sockfd, int backlog);
参数: sockfd 套接字描述符
backlog 最大连接数

第六个函数: 响应

  函数原型:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  函数参数:sockfd  是socket描述符
                   addr    客户端的地址,等价来电显示。保存了客户端的ip和端口,传出参数
                  addrlen  结构体的大小(结构体长度)  
                 堵塞函数
 返回值:返回值是客户端的socket描述符。

第七个函数:接受和发送

    函数原型:ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    函数参数:sockfd 是客户端的socket描述符
                     buf 需要发送的数据buf
                     len  发送的数据长度
                     flags  堵塞和非堵塞的标志 0位堵塞。
    函数返回: 实际发送的数据长度,失败返回 -1

   函数原型: ssize_t recv(int sockfd, void *buf, size_t len, int flags);
   函数参数: sockfd 是客户端的socket描述符
                     buf 需要接受的数据buf
                     len  接受的数据长度
                     flags  堵塞和非堵塞的标志 0位堵塞。
    函数返回: 实际接受的数据长度,失败返回 -1

第八个:关闭套接字

     close(套接字)

第九个:客户端连接函数

  函数原型:int connect(int sockfd, const struct sockaddr *addr,     socklen_t addrlen);
  函数参数: sockfd  套接字描述符   客户端自己的
                    addr    服务器的ip和端口的结构体。 
                    addrlen  结构体大小。

网络概念:

 IP地址:逻辑地址,网络通信的唯一标志,使用点分十进制表示 “192.168.1.4”
 IPV4:32位。最多分配40亿ip地址,ip地址已经不足够使用。
 ipv6:128位,非常多。足够分配,

DHCP:动态地址分配协议,当需要上网时,从DHCP服务器租赁一个未使用的ip地址,主机拿ip地址访问网络。使用完则DHCP服务器收回ip地址,以防给其他人使用。

物理地址: 网卡地址,48位,每个网卡有一个固定的编号,编号就是网卡地址,也就是MAC地址。两个部分 前24位–厂商编号 后24个人编号

各种协议解析:(了解)
FTP(文件传输协议):是网络上两台计算机传送文件的协议,运行在 TCP 之上,。FTP协议使用TCP20号和21号端口,20号端口用于数据交换,21号端口用于建立连接,

允许目录和文件访问,上传下载,不能远程执行文件。
TFTP(简单文件传输协议):是用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务,使用UDP的69号端口。

HTTP(超文本传输协议):是用于从 WWW 服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,使网络传输减少。

DNS(域名系统):在 Internet 上域名与 IP 地址之间是一一对应 的,域名虽然便于人们记忆,但机器之间只能互相识别 IP 地址,
它们之间的转换工作称为域名解析,使用53号端口。

SMTP(简单邮件传输协议):建立在 TCP 之上,是一种提供可靠且有效的电子邮件传输的协议。SMTP 是建模在 FTP 文件传输服务上的一种邮件服务,
主要用于传输系统之间的邮件信息,并提供与电子邮件有关的通知,使用 25 端口。

Telnet(远程登录协议):是登录和仿真程序,建立在 TCP 之上,它的基本功能是允许用户登录并进入远程计算机系统。

DHCP(动态主机配置协议):建立在 UDP 之上,是基于客户机/服务器模型(B/S模型)设计的一个局域网的网络协议。

TCP:传输控制协议,为用户进程提供可靠的全双工字节流。
IP:网间协议,提供分组递送服务

ICMP:互联网控制信息协议,处理路由器和主机之间流通的错误和控制消息,控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息.如ping命令。
ARP:地址解析协议,把ipv4地址映射成MAC地址
RARP 地址解析协议,把MAC地址转换成IP地址

阐述下tcp和udp的区别(重点)

 TCP是基于连接的通信,UDP是非连接通信
 TCP是可靠传输,数据不丢失,而UDP是不可靠传输,有可能丢失
 TCP是在传输层,UDP也在传输层
 TCP需要三次握手,而UDP不需要。
 UDP传输速度快,发送小尺寸数据,适合做视频等较大数据的传输
 TCP是一对一通信,而UDP可以一对多通信。
 TCP是基于数据流通信,而UDP是基于数据报通信

子网掩码:用来区分网络地址和主机地址部分。子网掩码也是点分十进制表示,也是32位,和ip地址一一对应关系。子网掩码中的1表示ip地址中的网络部分,0表示主机部分。
例如:我的ip地址是192.168.5.169 子网掩码是255.255.255.0
11000000.10101000.00000101.10101001
11111111.11111111.11111111.00000000
所以网络地址部分是:192.168.5 主机地址部分是169
(了解)
A类:(1.0.0.0-126.0.0.0)(默认子网掩码:255.0.0.0或 0xFF000000)第一个字节为网络号,后三个字节为主机号。该类IP地址的最前面为“0”,
所以地址的网络号取值于1~126之间。一般用于大型网络。(第一位必须是0)

B类:(128.0.0.0-191.255.0.0)(默认子网掩码:255.255.0.0或0xFFFF0000)前两个字节为网络号,后两个字节为主机号。该类IP地址的最前面为“10”,
所以地址的网络号取值于128~191之间。一般用于中等规模网络。

C类:(192.0.0.0-223.255.255.0)(子网掩码:255.255.255.0或 0xFFFFFF00)前三个字节为网络号,最后一个字节为主机号。该类IP地址的最前面为“110”,
所以地址的网络号取值于192~223之间。一般用于小型网络。

D类:是多播地址。该类IP地址的最前面为“1110”,所以地址的网络号取值于224~239之间。一般用于多路广播用户。

E类:是保留地址。该类IP地址的最前面为“1111”,所以地址的网络号取值于240~255之间。IP网络中通常用最小的IP地址标识网络本身,
将最大的IP地址作为该网络的广播地址,其余所有IP地址都分配给网络中的主机。然而,局域网中的主机并不能直接访问Internet,
说需要通过一个作为代理的网关或网络地址转换服务(NAT)才能访问Internet。通常将IP地址的第一个或最后一个留给该网络的Internet网关。

三次握手 四次挥手(看图片,重点中的重点)

作业: 客户端可以不断的发送数据给服务器,服务器收到每个消息都回复一个ok,如果客户端发送bye,则服务器和客户端都挂关闭。

网络编程第二天:

UDP通信

服务器                                 客户端
  socket                               socket
  bind                                 bind
  recvfrom                             sento
  close                                close


UDP 通信数据发送:

函数原型:ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

 函数作用: 在使用udp时候发送数据。(也可以用于tcp)
 函数参数:sockfd   是socket的返回值 描述符
                 buf  需要发送数据的缓存  一般是malloc或者数组
                  len  需要发送的数据长度
                   flags  是否堵塞  0为堵塞
                  dest_addr  发送服务器的地址结构体
                   addlen 结构体地址长度
   返回值:成功返回实际发送的数据字节数 失败返回-1

UDP通信 数据接受

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

 函数参数:sockfd   是socket的返回值 描述符
                  buf  需要接受数据的缓存  一般是malloc或者数组
                  len  需要接受的buf的数据长度
                   flags  是否堵塞  0为堵塞
                  src_addr  发送服务器的地址结构体
                   addlen 结构体的长度的地址
 返回值:成功返回实际接受到的数据,失败返回-1
 函数作用:在UDP通信是接受数据(也可以用于TCP)

练习:客户端发送文件到服务器端。客户端发送的时候,需要先发一个结构体和数据包一起。
结构体是struct file{
int size
char buf【128】;
int flags
}

广播通信:

一个发送端,多个接收端,所有当前网络的用户都可以接受到消息,一对多的关系。
单播: 一对一  ,广播是一对所有。 
多播:  也叫组播,某一部分可以收到,另外一个部分收不到。(中间)
广播地址:最大的主机地址就是广播地址,192.168.5.124的广播地址192.168.5.255
广播是属于UDP通信方式。

     客户端(发送)                                 服务器(接受)
       socket                                        socket
       bind                                          bind
       设置允许广播                                   设置允许广播
  设置广播地址为接收端地址                             数据接受
       数据发送                                      接续接受
       关闭                                          关闭

设置广播:

   int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
   函数作用:设置广播(只是其中一个功能)
   函数参数: sockfd   是socket描述符
                      level     选项定义的层次,SOL_SOCKET (设置socket属性)
                                                             IPPROTO_TCP(设置tcp属性)
                     optname 需要设置或者获取的套接字的选项。 SO_BROADCAST
                     optval    指针,指向存放所获得选项的缓冲区。 如果是1则设置广播,如果是0则取消广播
                     optlen   指针 ,上一个参数的长度
 返回值:如果成功返回0,失败返回-1

     int getsockopt(int sockfd, int level, int optname,    void *optval, socklen_t *optlen);
    函数作用:获取socket的状态信息(获取接受发送缓存区等)

多播通信:也叫作组播通信,给某一部分发送消息
多播地址:D类地址,起始是224.0.0.0~239.255.255.255 。
                 在进行组播数据发送过程中,可以先自定义一个多播地址。把自己的ip地址加入到多播地址后,给多播地址发送数据,当前组中所有成员都可以收得到 。
  客户端 (发送端)               服务器端(接收端)
    socket	               socket
    bind(选择)		  bind							
    设置允许多播                   设置允许多播
     加入到多播                     加入到多播
      给多播发数据                  收数据
       关闭                                   退出多播
                                               关闭

setsockopt设置多播:

IP 多播通信必须依赖于 IP 多播地址, 在 IPv4 中它是一个 D 类 IP 地址, 范围从 224.0.0.1 到
239.255.255.255,并被划分为局部链接多播地址、预留多播地址和管理权限多播地址三类:
1)局部链接多播地址范围在 224.0.0.0~224.0.0.255,这是为路由协议和其它用途保留的地址,路由 器并不转发属于此范围的IP包;

2)预留多播地址为 224.0.1.0~238.255.255.255,可用于全球范围(如Internet)或网络协议;
3)管理权限多播地址为 239.0.0.0~239.255.255.255,可供组织内部使用,类似于私有 IP 地址,不能 用于 Internet,可限制多播范围
No such device的解决办法,把广播地址加入路有表
sudo route add -net 224.0.0.88 netmask 255.255.255.255 eth0

1>建立一个socket;

2>设置多播的参数,例如超时时间TTL,本地回环许可LOOP等

3>加入多播组

4>发送和接收数据

5>从多播组离开

2.多播程序设计使用setsockopt()函数和getsockopt()函数来实现,组播的选项是IP层的。

3.setsockopt()的选项

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

成功执行返回0,否则返回-1
选项 IP_ADD_MEMBERSHIP 和 IP_DROP_MEMBERSHIP
加入或者退出一个多播组,通过选项 IP_ADD_MEMBERSHIP 和 IP_DROP_MEMBERSHIP,对一个结 构 struct ip_mreq 类型的变量进行控制,struct ip_mreq 原型如下:

struct in_addr{in_addr_t s_addr;}
struct ip_mreq{
// 多播组 IP,类似于 QQ 群号
struct in_addr imn_multiaddr;
struct in_addr imr_interface; // 将要添加到多播组的 IP,类似于QQ 成员号}

网络编程第三天:

一、多播编程流程

 1>建立一个socket;
 2>设置多播的参数,例如超时时间TTL,本地回环许可LOOP等(略)
 3>加入多播组  setsocketopt
 4>发送和接收数据 (给多播组地址发)
 5>从多播组离开  setsockopt

2.多播程序设计使用setsockopt()函数和getsockopt()函数来实现,组播的选项是IP层的。

3.setsockopt()的选项
int setsockopt( int sockfd, int level,int optname, const void *optval, socklen_t optlen 	);

成功执行返回0,否则返回-1
选项 IP_ADD_MEMBERSHIP 和 IP_DROP_MEMBERSHIP
加入或者退出一个多播组,通过选项 IP_ADD_MEMBERSHIP 和 IP_DROP_MEMBERSHIP,
对一个结 构 struct ip_mreq 类型的变量进行控制,struct ip_mreq 原型如下:

struct in_addr{in_addr_t s_addr;}
struct ip_mreq{
// 多播组 IP,类似于 QQ 群号
struct in_addr imn_multiaddr; 
struct in_addr imr_interface; // 将要添加到多播组的 IP,类似于QQ 成员号}


strubt hp mrdq(
strubt  hn _ccr  hmr multh_ccr;
strubt  hn _ccr  hmr hntdre_bd;
);
strubt hp mrdq mrdq;
azdro(&mrdq, shzdoe(mrdq));
mrdq.hmr multh_ccr.s _ccr = ine_addr(“224.10.10.1”);
mrdq.hmr hntdre_bd.s _ccr = htonl(INADDR ANY);
setsockopt(sobkec, IPPR0T0_IP, IP_ADD_MEMBER, &mrdq, shzdoe(mrdq));

二、io模型

1>堵塞IO模型 
    最常用,超简单,效果最低。
    (read write  recv send recvfrom sendto accpet )
      (bind listen close  socket)
 2>非堵塞IO模型
      函数不堵塞,直接返回,需要轮询。占用cpu
      第一种方式: 直接在recv或者send 或者recvfrom 或者sendto的参数设置非堵塞
               例如: recv(fd,buf,sizeof(buf),MSG_DONTWAIT);
       第二种方式:fcntl函数设置
              int flag = fcntl(fd,F_GETFL,0);
              flag |= O_NONBLOCK 接下来的read write等函数都是非堵塞的。
 3> 多路复用IO模型
              #include <sys/select.h>



   函数原型: int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
   头  文 件: #include <sys/time.h>       #include <sys/types.h>       #include <unistd.h>
   函数作用:做多路复用函数(等待内核通知,然后再通知给read 或者write函数)
   函数参数: nfds         最大描述符值+1
                     readfds    需要读取的描述符的集合(需要监控的是读缓存)   
                     writefds    需要写的描述符集合(需要监控的是写缓存)
                     exceptfds 需要异常的描述符集合(监控异常信息)
                     timeout    超时检测。如果不设置NULL默认是堵塞
   返回值:  返回负数  select错误
                  返回正数: 某些描述符有信号。
                  返回0则表示等待超时。
 以下函数是辅助select函数的使用。       
   void FD_CLR(int fd, fd_set *set);
   函数作用:把所有描述符清空,把fd的描述符在set中清空。
   int  FD_ISSET(int fd, fd_set *set);
   函数作用: 判断fd是否在set描述符集合中。
   void FD_SET(int fd, fd_set  *set);
   函数作用:将fd设置到set描述符集中
   void FD_ZERO(fd_set *set);
   函数作用: 把描述符集全部清空,编程0;

   4>信号驱动模型
           发送信号

案例:写一个UDP的接受端,设置成非堵塞(fcntl),然后再while1中不断的读取数据。读取到数据后打印出来。
案例:写一个发送端,需要监控接受和发送复用。(select后需要监控发送结合和接受集合)。

gethostname() 获得主机名

   函数原型:  int gethostname(char *name, size_t len);  
    头文件:  #include <unistd.h>
    函数作用: 获得主机名
    函数参数: name 得到主机名,buf存储
                      len    主机名buf长度

  函数原型:  int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  函数作用:  获得与套接口相连的远程协议地址
  头  文 件:     #include <sys/socket.h>
  函数参数: sockfd 是socket描述符
                   addr  用于保存地址的buf
                   addrlen  保存地址buf的长度

 函数原型 struct hostent *gethostbyname(const char *name);
 头 文 件: #include <netdb.h>
 函数作用:根据主机名取得主机信息
 函数参数: *name  主机名
 结构体如下: struct hostent {
           char  *h_name;            /* 主机规范名 */
           char **h_aliases;         /* 别名列表 */
           int    h_addrtype;        /*主机地址类型 */
           int    h_length;          /* 主机地址长度 */
           char **h_addr_list;       /* 主机地址列表 */
       }
   案例:
  int main()    {
   struct hostent* addr = gethostbyname("www.baidu.com"); 
   printf("%s\n",(addr->h_addrtype == PF_INET)?"IPV4":"IPV6");
   for(int i = 0;addr->h_addr_list[i];++i)    {
         printf("%s\n" ,inet_ntoa( *(struct in_addr*)addr->h_addr_list[i]));
    }
     return 0;
  }
 

//自学
getsockname() 获得本地套接口协议地址
gethostbyaddr() 根据主机地址取得主机信息
getprotobyname() 根据协议名取得主机协议信息
getservbyname() 根据服务名取得相关服务信息
getservbyport() 根据端口号取得相关服务信息

重点: 设置网络属性。
setsockopt() 获取/设置一个套接口选项

   函数原型:int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
                          sockfd:一个标识套接口的描述字
                          level:选项定义的层次。支持的层次仅有SOL_SOCKET(socket属性)和
                                     IPPROTO_TCP(tcp属性)IPPROTO_IP
                         optname:需设置的套接口选项。
                         optval:指针,指向存放所设置选项值的缓冲区。
                         optlen:指针,指向optval缓冲区的长度值。  

预习:unix通信和延迟处理。

网络编程第四天:

1.unix域socket

 不属于网络通信内容,只在多进程之间通信。(ipc的一种通信方式)
 两种方式:tcp和udp
 通过普通文件的路径名来识别socket,不需要提供ip地址。

unlix的TCP域套接字

 服务器                      客户端
  socket                     socket
  bind                       bind
  listen                     connect
  accept                      ....
  read                       read
  write                      write
  close                      close

  流程:
  首先创建socket。int fd  =socket(PF_UNIX,SOCK_STREAM.0);
  其次绑定结构体:
 struct sockaddr_un  {
     sa_family_t sa_prefix         协议簇
    char sun_path[108];	 路径名
 };

再次:绑定 bind函数
接下来listen ,accpet ,read ,write close
注意:unix需要注意 再bind前需要保证文件不存在。可以提前用remove删除bind的文件。防止出错。

网络延迟检测:
setsockeopt 函数,第三个参数设置 SO_RECVTIMEO—设置接收延迟时间
SO_SENDTIMEO—设置发送的延迟时间
第四个参数: 结构体 simeval的结构体
注意:需要加上头文件 #include<sys/time.h>
struct timeval
{
__time_t tv_sec; /* 秒数. /
__suseconds_t tv_usec; /
微秒数 */
};

猜你喜欢

转载自blog.csdn.net/qq_52531759/article/details/127182343