(四)linux网络编程

一、CS架构,BS架构

(1)CS架构介绍(client server,客户端服务器架构),例如:qq、360网盘
(2)BS架构介绍(broswer server,浏览器服务器架构)例如:浏览器

二、TCP协议学习1

1、关于TCP理解的重点
(1)TCP协议工作在传输层,对上服务socket接口,对下调用IP层
(2)TCP协议面向连接,通信前必须先3次握手建立连接关系后才能开始通信。
(3)TCP协议提供可靠传输,不怕丢包、乱序等。
2、TCP如何保证可靠传输
(1)TCP在传输有效信息前要求通信双方必须先握手,建立连接才能通信
(2)TCP的接收方收到数据包后会ack给发送方,若发送方未收到ack会丢包重传
(3)TCP的有效数据内容会附带校验,以防止内容在传递过程中损坏
(4)TCP会根据网络带宽来自动调节适配速率(滑动窗口技术),如果接收方接收压力很小,发送方便会慢慢提高发送速率;相反接收方压力大,发送方会慢慢讲地发送速率
(5)发送方会给各分割报文编号,接收方会校验编号,一旦顺序错误即会重传。

三、TCP协议的学习2

1、TCP的三次握手

(1)建立连接需要三次握手
(2)建立连接的条件:服务器listen时客户端主动发起connect
2、TCP的四次握手
(3)关闭连接需要四次握手
(4)服务器或者客户端都可以主动发起关闭

注:这些握手协议已经封装在TCP协议内部,socket编程接口平时不用管
3、基于TCP通信的服务模式
(1)具有公网IP地址的服务器(或者使用动态IP地址映射技术)
(2)服务器端socket、bind、listen后处于监听状态
(3)客户端socket后,直接connect去发起连接。
(4)服务器收到并同意客户端接入后会建立TCP连接,然后双方开始收发数据,收发时是双向的,而且双方均可发起
(5)双方均可发起关闭连接
4、常见的使用了TCP协议的网络应用
(1)http(也是一个协议,纯传文件,文本信息)、ftp
(2)QQ服务器
(3)mail服务器

四、socket编程接口介绍

表示IP地址数据结构都定义在 /usr/include/netinet/in.h下,我们在linux下输入vim /usr/include/netinet/in.h便可查看in.h下定义的结构体

1、建立连接
(1)socket。socket函数类似于open,用来打开一个网络连接,如果成功则返回一个网络文件描述符(int类型),之后我们操作这个网络连接都通过这个网络文件描述符。

  int socket(int domain,int type,int protocol) //socket函数非常类似于open函数,socket函数是用来发开一个网络的,如果成功就会返回一个int型数据,网络文件描述符。之后我们操作这个网络都通过这个网络文件描述符。

  dimain:域,网络域,网络地址范围(IPV4或IPV6等)

  type:指定类型:SOCK_STREAM(TCP网络)、SOCK_DGRAM(UDP)、SOCK_SEQPACKET

  protocol:指定协议,如果指定0,表示使用默认的协议
(2)bind函数

  int bind(int socket,const struct *address,socklen_t address_len);

  函数功能及作用:把本地的IP地址和我们的socket绑定起来,也就是第一个参数int socket,

  const struct  sockaddr *address:struct sockaddr,这个结构体是网络编程接口中用来表示一个IP地址的,注意这个IP地址是不区分IPv4和IPv6的(或者说是兼容IPv4和IPv6的)

  address_len:表示IP地址的长度  

(3)listen函数

  int listen(int socket,int backlog)

  int socket:socket接口,和上面一样

  int backlog:表示我们可以同时接收几个,设置监听队列长度。

对于服务器来说只要调用soclet、bind、listen函数就可以进入监听状态了,客户端那边调用socket函数打开文件描述符,然后调用connect去连接

(4)connect

  int connect(int socket,const struct sockaddress *address,socklen_t address_len);

  要连那个服务器,就用那个address,调用connect函数去连接起来。自己的IP地址会自动得放到发送报文里去。这个函数是兼容IPV4和IPV6的,因为struct suckaddress它是兼容IPV4和IPV6的。

3、发送和接收

TCP层是不管谁发谁收的,在应用层实现。

(1)send和write   在网络上发送,类似写文件;接收,类似于读文件。

socket和write函数很想,返回值几乎是一模一样的。ssize_t write(int fildes,const void *buf,size_t nbyte),我们调用write函数写就可以把要写的内容发出去。

send函数封装:ssize_t send(int socket,const void *buffer,size_t length,int flags),send比write就多一个flags,不考虑这个讲flags设置为0,那就和write是一模一样的。我们可以设置flags一些值:MSG_EOR(一个记录信息的一个结尾,表示我们一个信息就要完了),MSG_OOB(带外数据,即非正式数据,正常通信时是没有的,在一些特殊协议里会有),因此send在一些特殊协议我们才会用到flags,正常协议用不到的,正常通信设置flags为0,和write就一样
(2)recv和read

recv,read和send,write也是类比一样。不过他们是接收

recv函数封装:ssize_t recv(int socket,void *buffer,size_t length,int flags);
4、辅助性函数

辅助性函数主要是进行一些IP地址转换的,为什么要进行IP地址转换呢,IP地址有两种形式,一种是点分十进制的形式即192.32.44.5,一种是32位二进制的形式也就是int类型,点分十进制比较符合人们的观看习惯;而二进制格式,更适合机器编程进行封装,操作。因此我们需要在这两种IP地址之间进行可靠的转换,而转换是由封装好的函数实现的。如下所示:

(1)inet_aton、inet_ntoa、inet_addr

inet_aton:将一个字符串IP地址转换为一个32位的网络序列IP地址

int inet_aton(const char *cp, struct in_addr *inp);

inet_aton函数接受两个参数。参数描述如下:
1 输入参数string包含ASCII表示的IP地址。
2 输出参数addr是将要用新的IP地址更新的结构。

inet_ntoa:将网络地址转换成“.”点隔的字符串格式

int inet_aton(const char *cp, struct in_addr *inp);

inet_addr():的功能是将一个点分十进制的IP转换成一个长整数型数(u_long类型)

in_addr inet_addr(const char *cp);

这三个尽量都少使用,没下面好,其实他们和下面两个功能都一致,都没什么缺陷,唯一缺陷是他们不支持IPV6,下面支持。
(2)inet_ntop、inet_pton

这两个现在阶段变成最好使用,上面三个尽量少使用,这两个程序既支持IPV4又支持IPV6,现在我们下代码肯定要用inet_ntop和inet_pton函数

inet_ntop:将32为二进制IP地址转换为点分十进制的形式。

inet_pton:将点分十进制的形式转换为32位二进制的形式。

我们记忆这个比较好记。ntop即n(网络)-to-p(字符串),网络层使用的是32位二进制,因此是32位二进制IP地址转换为点分十进制的形式,pton相反

5、表示IP地址相关数据结构
(1)都定义在 netinet/in.h
(2)struct sockaddr,这个结构体是网络编程接口中用来表示一个IP地址的,注意这个IP地址是不区分IPv4和IPv6的(或者说是兼容IPv4和IPv6的)
(3)

typedef uint32_t in_addr_t; 网络内部用来表示IP地址的类型

struct in_addr
{
  in_addr_t s_addr;
};
(5)

struct sockaddr_in
{
  __SOCKADDR_COMMON (sin_);
  in_port_t sin_port; /* Port number. */
  struct in_addr sin_addr; /* Internet address. */

  /* Pad to size of `struct sockaddr'. */
  unsigned char sin_zero[sizeof (struct sockaddr) -
  __SOCKADDR_COMMON_SIZE -
  sizeof (in_port_t) -
  sizeof (struct in_addr)];
};
(6)struct sockaddr 这个结构体是linux的网络编程接口中用来表示IP地址的标准结构体,bind、connect等函数中都需要这个结构体,这个结构体是兼容IPV4和IPV6的。在实际编程中这个结构体会被一个struct sockaddr_in(IPV4)或者一个struct sockaddr_in6(IPV6)所填充。sockaddr这个结构体是兼容struct sockaddr_in和struct sockaddr_in6的

6、IP地址格式转换函数实践

(1)inet_addr函数:

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

in_addr_t inet_addr(const char *cp); 表示将一个十进制的字符串IP地址,转换为机器可是别的in_addr_t(uint32_t)格式。

示例代码如下:

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

#define  IPADDR        "192.168.1.103"

int main(void)
{
    in_addr_t addr = 0;
    
    addr = inet_addr(IPADDR);
    printf("addr = 0x%x.\n",addr); //0x6701a8c0 0x67:103、0x01:1、0xa8:168、0xc0:192
    
    return 0;
}

网络字节序默认是大端模式,如果是大端模式直接带进去参数就好了,如果是小端模式,要先转换为大端模式,然后再往进传参数。在网络上只允许使用大端模式,不允许小端模式。inet_addr这个函数可以可以检测当前cpu是大端模式还是小端模式,函数内部做了转换了。所以我们不用管大小端,只管他是大端模式就好了。

不过inet_addr这个函数现在一般不用,因为他只适合IPV4网络,不适合IPV6网络。

(2)inet_pton

 #include <arpa/inet.h>

int inet_pton(int af, const char *src, void *dst);

 这里我们要知道:

int af:表示是选择是IPV4还是IPV6,IPV4参数:AF_INET  IPV6参数:AF_INET6;

const char *src:表示源十进制字符串IP地址,例如"192.168.1.1"

void *dst:表示转出的,32位二进制IP地址,他的传参类型根据IPV4和IPV6有所不同,IPV4传的是:struct  in_addr 类型参数,IPV6传的是:struct in6_addr类型参数。我们还要注意in_addr和in6_addr这两个结构体的组成,如下所示:

typedef uint32_t in_addr_t;
struct in_addr
{
  in_addr_t s_addr;
};

可以看出in_addr这个结构体里面只有一个成员为s_addr,这个成员的类型为in_addr_t即uint32_t类型。

struct in6_addr
{
    union
    {
        uint8_t __u6_addr8[16];
        #if defined __USE_MISC || defined __USE_GNU
        uint16_t __u6_addr16[8];
        uint32_t __u6_addr32[4];
        #endif
    } __in6_u;
    #define s6_addr __in6_u.__u6_addr8
    #if defined __USE_MISC || defined __USE_GNU
    # define s6_addr16 __in6_u.__u6_addr16
    # define s6_addr32 __in6_u.__u6_addr32
    #endif
};                

inet_pton函数返回值为一个int类型数据,表示转换是否完成,1表示转换成功,0表示转换不成功。

下面写一个IPV4的调用inet_pton函数转换IP地址函数:

 #include <stdio.h>
 #include <arpa/inet.h>
 
 #define  IPADDRESS        "192.168.100.160"
 int main(void)
 {
     struct in_addr sAddr = {0};
     int  ulText;
     if(ulText)
     {
        ulText = inet_pton(AF_INET,IPADDRESS, &sAddr);
     }
     printf("ulText = %d\nsAddr = 0x%x\n",ulText,sAddr.s_addr);
     
     return 0;
 }
 

(3)inet_ntop函数

#include <arpa/inet.h>

const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);

int af:表示IPV4或者IPV6网络;

const void *src:表示待转换二进制IP地址;

char *dst:表示转换后的十进制字符串IP地址;

socklen_t size:表示转换后的十进制字符串IP地址的长度。

示例代码如下:

#include<stdio.h>
#include <arpa/inet.h>

int main(void)
{
    struct in_addr addr = {0};
    char ucBuff[50] = {0};
    
    addr.s_addr = 0x6601a8c0;
    inet_ntop(AF_INET,&addr,ucBuff,sizeof(ucBuff));
    
    printf("IP:%s\n",ucBuff);
    
    return 0;
}    

 

posted @ 2016-11-01 08:19  xtusir  阅读(552)  评论(0编辑  收藏  举报