zoukankan      html  css  js  c++  java
  • linux编程---网络编程1

    主干部分

    服务器端:socket—>bind—>listen—>accept—>close;

    客户端:   socket—>connect—>close;

    image

    按照上面建立连接后,就是进行数据的传输了。。。。

    涉及主干函数如下:

    socket函数用于创建套接字文件标识号;

    bind函数用于套接字和地址的绑定;

    listen函数用于监听该套接字

    accept函数用于等待客户端连接请求;

    connect函数向服务器申请请求。

    具体函数解说

    image

    socket成功返回该套接字的文件标示符;失败返回-1;

    参数:domain表示本机采用的协议族:例如AF_INET表示internet网络协议族;AF_UNIX表示系统unix的进程通信协议族

             type表示具体协议SOCK_STREAM表示TCP协议,SOCK_DGRAM表示UDP协议

             protocal表示也是指定具体协议的,如果type指定了则为0;

    上诉参数的值都是通过宏给出的;

    补充:

    正常下,protocol为0,则具体协议通过type给出;

    若设置原始套接字某个协议时,通过设置type将其说明是原始套接字,用protocol来说明创建具体的协议的套接字;

    image

    bind成功则返回0;失败-1;将本地端口绑定到该socket字上。

    参数:sockfd表示由socket函数产生的套接字标识号;

            my_addr表示本地端口信息;后一个参数就是该类型长度;

    sockaddr结构:linux/socket.h头文件

    image

    as_family表示协议族;

    sa_data 表示具体地址;

    sockaddr_in结构体:linux/in.h头文件

    image

    sin_family表示协议族;sin_port表示要监听的端口;sin_addr表示吧本机可以和哪些主机通信若为INADDR_ANY表示任何

    补充:

    选择sockaddr_in是因为兼容性

    image

    listen函数将套接字变成监听套接字;成功为0;失败-1

    参数:sockfd表示已经绑定的套接字;

            backlog表示多个客户端时用它来表示最多有个多套接字可以被同时监听

    image

    accept函数表示等待客户端请求;成功则放回已经接受到了客户端请求后的套接字标识号;

    参数:sockfd表示已经监听的套接字;

             addr表示请求连接客户端的地址信息;后一个是该信息大小

    image

    connect函数表示客户端向服务端请求连接,成功则为0,失败为-1

    参数:sockfd表示客户端产生的套接字;

            serv_addr表示服务器的地址信息;后一个是该结构的大小;

    image

    write函数用于写信息,写入到fd中;

    image

    read函数用于读取fd信息;

    采用的模式:阻塞模式,后期还会讲解一些其他模式

    程序来源:http://blog.chinaunix.net/uid-23069658-id-3273673.html

    服务器端:

    //TCP示例服务器端 tcpSrv.c
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <unistd.h>
    #include <netdb.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <sys/types.h>
    #include <arpa/inet.h>
    int main(int argc, char *argv[])
    {
        int skfd,cnfd,addr_len;
        struct sockaddr_in srv_addr,clt_addr;
        int portnumber;
        char hello[]="Hello! Long time no see.
    ";
        if(2 != argc || 0 > (portnumber=atoi(argv[1])))
        {
             printf("Usage:%s port
    ",argv[0]);
             exit(1);
        }
        
        /* 创建IPv4的流式套接字描述符 */
        if(-1 == (skfd=socket(AF_INET,SOCK_STREAM,0)))
        {
             perror("Socket Error:");
             exit(1);
        }
    
        /* 填充服务器端sockaddr地址结构 */
        bzero(&srv_addr,sizeof(struct sockaddr_in));
        srv_addr.sin_family=AF_INET;
        srv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
        srv_addr.sin_port=htons(portnumber);
    
        /* 将套接字描述符skfd和地址信息结构体绑定起来 */
        if(-1 == bind(skfd,(struct sockaddr *)(&srv_addr),sizeof(struct sockaddr)))
        {
             perror("Bind error:");
             exit(1);
        }
    
        /* 将skfd转换为被动建通模式 */
        if(-1 == listen(skfd,4))
        {
             perror("Listen error:");
             exit(1);
        }
    
        while(1)
        {
           /* 调用accept,服务器端一直阻塞,直到客户程序与其建立连接成功为止*/
            addr_len=sizeof(struct sockaddr_in);//此处sockaddr也可以通过。。。
            if(-1 == (cnfd=accept(skfd,(struct sockaddr *)(&clt_addr),&addr_len)))
            {
                 perror("Accept error:");
                 exit(1);
            }
            printf("Connect from %s:%u ...!
    ",inet_ntoa(clt_addr.sin_addr),ntohs(clt_addr.sin_port)); 
            if(-1 == write(cnfd,hello,strlen(hello))){
                 perror("Send error:");
                 exit(1);
            }
            close(cnfd);
         }
         close(skfd);
         exit(0);
    }

    客户端:

    //TCP示例客户端 tcpclt.c
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <netdb.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <sys/types.h>
    #include <arpa/inet.h>
    int main(int argc, char *argv[])
    {
        int skfd;
        char buf[1024] = {0};//----此处尤其重要注意,应该先分配这个变量空间,不然不能正常存储数据
        struct sockaddr_in server_addr;
        struct hostent *host;
        int portnumber,nbytes;
        if(3 != argc || 0>(portnumber=atoi(argv[2])))
        {
             printf("Usage:%s hostname portnumber 
    ");
             exit(1);
        }
        if(NULL == (host=gethostbyname(argv[1])))
        {
             perror("Gethostname error:");
             exit(1);
        }
    
        /* 创建socket描述符 */
        if(-1 == (skfd=socket(AF_INET,SOCK_STREAM,0)))
        {
             perror("Socket Error:");
             exit(1);
        }
    
        /* 客户端填充需要连接的服务器的地址信息结构体 */
        bzero(&server_addr,sizeof(server_addr));
        server_addr.sin_family=AF_INET;
        server_addr.sin_port=htons(portnumber);
        server_addr.sin_addr=*((struct in_addr *)host->h_addr);
    
        /* 客户端调用connect主动发起连接请求 */
        if(-1 == connect(skfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr)))
        {
             perror("Connect Error:");
             exit(1);
        }
    
        /*客户端只接收服务器发来的数据,然后就退出*/
        if(-1 == read(skfd,buf,1024)){
             perror("Recv Error:");
        }
        printf("Date arrived:%s",buf);
    
        /* 拆除TCP连接 */
        close(skfd);
        exit(0);
    }

    ------------------------------------------------------------分割线----------------------------------------------------

    信息辅助函数

    image

    此类是字节转换函数:

    函数字母含义:h表示机器host;n表示网络;s表示short;l表示long

    这里需要字节转换主要是不同机器的字节顺序是不一样的,所以通过统一网络的字节顺序,从而能沟通不同机器的通信。

    详细说明

    不同操作系统会有不同的字节顺序,目前有2中字节顺序,一种是小头;一种是大头;网络字节顺序是大头字节顺序。

    小头字节顺序是指随地址增加,高的字节是放在后面的,低字节放在前面;

    大头字节顺序是指地址增加,高字节放在前面,低字节放在后面;

    image

    ----------------------分割线---------------------

    image

    此类函数为IP与域名的转换:

    第一个是将域名转为网络可识别的机器结构指针;第二是将IP形式的转换;

    关键点是返回的结构体hostent---netdb.h头文件

    image

    详细说明:

    结构体在头文件netdb.h中;

    image

    ----------------------分割线---------------------

    image

    此类函数为字符十点制IP转为32位IP--字符串与in_addr结构体互相转换

    详细说明:

    参数中inp参数的结构in_addr其实里面就是一个成员为s_addr类型为无符号的长整型;

    image

    ----------------------分割线---------------------

    image

    此类函数目的是获取服务器信息:

    服务器结构体如下:

    image

    ---------------------------------------------------我是分割线-----------上述为TCP基础上讲解的-----------------------

    UDP中的通信函数:

    服务器端:socket—>bind—>close;

    客户端:   socket—>close;

    image

    image

    这两个函数是UDP中关键函数

    第一个表示接受数据函数:

    参数:sockfd表示接受收数据的套接字;

            buf:表示接受数据的指针;

            len:数据大小;

            flags:如下解释

    image

             from:是来源的地址信息

             fromlen:表示from的大小;

    第二个函数表示发送函数:

             参数很第一个相似;----
    程序来源:http://blog.chinaunix.net/uid-23069658-id-3280895.html

    服务器:

    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <unistd.h>
    #include <netdb.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <sys/types.h>
    #include <arpa/inet.h>
    #define MAX_MSG_SIZE 1024
    
    int main(int argc,char** argv){
        int skfd,addrlen,ret;
        struct sockaddr_in addr,cltaddr;
        char buf[MAX_MSG_SIZE]={0};
        char sndbuf[MAX_MSG_SIZE]={0};
    
        //创建数据报式套接字skfd
        if(0>(skfd=socket(AF_INET,SOCK_DGRAM,0))){
             perror("Create Error");
             exit(1);
        }
    
        bzero(&addr,sizeof(struct sockaddr_in));
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr=htonl(INADDR_ANY);
        addr.sin_port=htons(atoi(argv[1]));
    
        //将socket文件描述符skfd和本地端口和地址绑定起来
        if(0>(bind(skfd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in)))){
             perror("Bind Error");
             exit(1);
        }
    
        //开始收发数据
        while(1){
             ret=
    recvfrom
    (skfd,buf,MAX_MSG_SIZE,0,(struct sockaddr*)&cltaddr,&addrlen);
             if(ret < 0){
                printf("recv data from %s:%d error!",inet_ntoa(cltaddr.sin_addr),ntohs(cltaddr.sin_port));
             }else if(ret == 0){
                perror("client has been closing socket!");
             }else{
                printf("From %s:%d,%s",inet_ntoa(cltaddr.sin_addr),ntohs(cltaddr.sin_port),buf);
                memset(sndbuf,0,MAX_MSG_SIZE);
                switch(buf[0]){
                      case 'a':
                           strcpy(sndbuf,"After u ,lady...");
                      break;
                      case 'b':
                           strcpy(sndbuf,"Before u ,sir...");
                      break;
                      case 'c':
                           strcpy(sndbuf,"Can u?");
                           break;
                      default:
                           strcpy(sndbuf,"I dont't know what u want!");
                }
                sendto(skfd,sndbuf,strlen(sndbuf),0,(struct sockaddr*)&cltaddr,addrlen);
             }
             memset(buf,0,MAX_MSG_SIZE);
        }
        return 0;
    }

    客户端:

    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <unistd.h>
    #include <netdb.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <sys/types.h>
    #include <arpa/inet.h>
    #define MAX_MSG_SIZE 1024
    
    int main(int argc,char** argv){
        int skfd,ret,len;
        struct sockaddr_in srvaddr;
        char buf[MAX_MSG_SIZE]={0};
        char sndbuf[MAX_MSG_SIZE]={0};
        struct in_addr addr;
    
        //创建数据报式套接字skfd
        if(0>(skfd=socket(AF_INET,SOCK_DGRAM,0))){
             perror("Create Error");
             exit(1);
        }
    
        if(0 == inet_aton(argv[1],&addr)){
             perror("server addr invalid!");
             exit(1);
        }
    
        bzero(&srvaddr,sizeof(struct sockaddr_in));
        srvaddr.sin_family = AF_INET;
        srvaddr.sin_addr=addr;
        srvaddr.sin_port=htons(atoi(argv[2]));
    
        //我们的客户端只接收从服务器地址是srvaddr的主机发来的数据
        if(0>(connect(skfd,(struct sockaddr*)&srvaddr,sizeof(struct sockaddr_in)))){
              perror("Connect Error");
              exit(1);
        }
        
        //开始收发数据
        while(1){
            memset(sndbuf,0,MAX_MSG_SIZE);
            len=read(0,sndbuf,MAX_MSG_SIZE);
            ret=
    sendto
    (skfd,sndbuf,strlen(sndbuf),0,(struct sockaddr*)&srvaddr,sizeof(struct sockaddr));
            if(ret == len){
                  memset(buf,0,MAX_MSG_SIZE);
                  //我们已经知道服务器地址信息了,所以最后两个参数为NULL
                  ret=recvfrom(skfd,buf,MAX_MSG_SIZE,0,NULL,NULL);
           
                  if(ret < 0){
                         perror("read error from server!");
                  }else if(ret == 0){
                         perror("server has been closing socket!");
                  }else{
                         buf[ret]='';
                         printf("From Server:%s
    ",buf);
                  }
            }
        }
        return 0;
    }

    image调用形式。。。。

    ---------------------------------------------------我是分割线-----------上述为UDP讲解的-----------------------

    高级函数:

    image

    前3个参数与之前的write和read一样;最后一个参数取值如下:可以组合

    image

    详细说明:

    此处的flags参数很重要;是对发送函数的控制选项。比如设置为MSG_OOB就表示可以允许发送带外数据

    MSG_DONTROUTE就表示不需要查找路由表,一般用于检测上,看是目的主机是否在本网中,若是不查找路由也可以找到。

    MSG_DONTWAIT表示效果不大,只是避免反复性操作的麻烦,只是设置为类似无阻塞操作,也就是不等待发送,若不行则返回错误。----send

    MSG_PEEK表示用于读这个函数,表示只是看,而不读取走;多个进程查看同个数据这个就很有用了。---recv

    MSG_WAITALL表示这个和阻塞模式一样了。

    ----------------------分割线---------------------

    image

    UDP协议中接收地址用的;

    详细说明:

    struct msghdr

    {

    void * msg_name;//指向sockaddr的指针,存储对方的地址信息

    int msg_namelen;

    struct  iovec * msg_iov;//指向iovec的指针---数组一样,多个iovec

    int msg_iovlen;//表示数组结构大小

    void * msg_control;//附加数据用于控制---在结构体cmsghdr

    int msg_controllen;

    int msg_flags;//针对接收,对发送无效果

    }

    struct iovec

    {

    void * iov_base;//缓冲区开始位置

    size_t iov_len;//缓冲区大小

    }

    struct cmsghdr

    {

    int cmsg_len;//结构体大小

    int cmsg_level;//结构体层次

    int cmsg_type;//数据类型

    int cmsg_data[0];//数据开始地址

    }

    ----------------------分割线---------------------

    image

    TCP中关闭通道控制,

    --------------------------------------------------------我是分割线-----------------------------------------------

    IP/TCP协议数据格式:

    IP协议:

    image

    下图来源(http://blog.chinaunix.net/uid-23069658-id-3280895.html

    image

    详细说明:

    ip_hl:IP包头的长度---20B固定长度+4B变动

    ip_v:IP版本号

    ip_tos:服务类型

    ip_len:IP包总长度

    ip_id:标示符+3bit的标志

    ip_off:偏移

    ip_ttl:生存时间

    ip_p:协议号

    ip_sum:校验码

    ip_src:源地址

    ip_dst:目标地址

    struct in_addr//ip 地址结构

    {

    unsigned long s_addr;

    }

    struct ip{}、struct icmp{}是供BSD系统层使用,struct iphdr{}和struct icmphdr{}是在INET层调用;

    见/usr/include/netinet目录

    在用户空间的编写网络应用程序的层次就叫做BSD层

    所以推荐用struct ip{}、struct icmp{}

    ----------------------分割线---------------------

    ICMP协议:

    image

    类型 代码
    校验码 校验码

    type:表示类型

    code:表示代码

    checksum:表示校验码

    是IP包的补充不是IP协议上层协议。。

    ----------------------分割线---------------------

    UDP协议:

    image

    源端口 目的端口
    消息长度 校验和

    source:源端口

    dest:目的端口

    len:消息长度

    check:校验和

    ----------------------分割线---------------------

    TCP协议:

    image

    下图来源(http://blog.chinaunix.net/uid-23069658-id-3280895.html

    image

    详细说明:

    source:源端口

    dest:目标端口

    seq:序号

    ack_seq:确认序号

    res1(4bit),res2(2bit):保留字段

    doff:TCP头部长度

    urg:设置带外数据(紧急标志位),ack:确认,psh,rst,syn:序号标志,fin          :表示标志位

    window:表示窗口大小

    check:表示校验码

    urg_prt表示紧急指针

    ----------------------分割线---------------------

    通过知道协议栈----可以对其编程

    第一:带外数据传输---TCP协议

    能传输的开关是UGR;传输1B;对通过send发,recv收;

    3种接收方法:

    1:信号SIGURG接收

    2:多路复用模型接收----异常和读允许

    3:通过带外标示接收---设置套接字参数SO_OOBINLINE

    第二:原始套接字

    1:创建原始套接字----SOCK_RAM

    2:设置IP头可修改参数---IP_HDRINCL

    3:利用函数接收,发送----sendto recvfrom等

    注意:必须在管理员允许小才能执行

    关于原始套接字的输入和输出处理:

    http://blog.163.com/li_xiang1102/blog/static/607140762011103091547530/

  • 相关阅读:
    【python+selenium自动化】图像识别技术在UI自动化测试中的实际运用
    【python+selenium自动化】使用pytest+allure2完成自动化测试报告的输出
    【python+selenium自动化】设置Chrome启动参数
    迭代器 和 生成器
    Python 第三方包上传至 PyPI 服务器
    python 模块导入全局变量
    装饰器
    set 集合数据类型
    lambda 、 map 、filter 、reduce 及 reversed 常用函数
    AVA + Spectron + JavaScript 对 JS 编写的客户端进行自动化测试
  • 原文地址:https://www.cnblogs.com/miner007/p/3963763.html
Copyright © 2011-2022 走看看