zoukankan      html  css  js  c++  java
  • socket编程

    一。socket概念

    Socket本身有“插座”的意思,在Linux环境下,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。

    既然是文件,那么理所当然的,我们可以使用文件描述符引用套接字。与管道类似的,Linux系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传递。

    二。网络字节序和sockaddr

    1.网络字节序

    TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。

    #include <arpa/inet.h>

    uint32_t htonl(uint32_t hostlong);

    uint16_t htons(uint16_t hostshort);

    uint32_t ntohl(uint32_t netlong);

    uint16_t ntohs(uint16_t netshort);

     

    ip转换

    #include <arpa/inet.h>

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

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

     

    2.sockaddr数据结构

    struct sockaddr_in {

        __kernel_sa_family_t sin_family;           /* Address family */    地址结构类型

        __be16 sin_port;                          /* Port number */      端口号

        struct in_addr sin_addr;                   /* Internet address */ IP地址

        /* Pad to size of `struct sockaddr'. */

        unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -

        sizeof(unsigned short int) - sizeof(struct in_addr)];

    };

    struct sin_addr{

      uint32 s_addr;

    }

     

    三。socket通信模型

    四。socket主要函数

     1.socket函数

    创建一个套接字,即打开网络通讯端口如果成功的话,就像open()一样返回一个文件描述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket()调用出错则返回-1。

    2.bind函数

    bind()的作用是将参数sockfd和addr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听addr所描述的地址和端口号

     

    3.listen函数

     listen()声明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接待状态

    返回值:  listen()成功返回0,失败返回-1

    4.accept函数

    三方握手完成后,服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来。addr是一个传出参数,accept()返回时传出客户端的地址和端口号。addrlen参数是一个传入传出参数(value-result argument),传入的是调用者提供的缓冲区addr的长度以避免缓冲区溢出问题,传出的是客户端地址结构体的实际长度

     

    5.connect函数

    客户端需要调用connect()连接服务器,connect和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址。

    6.客服端服务端套接字通信

    服务端

    //server.c
    #include<stdio.h>
    #include<unistd.h>
    #include<sys/socket.h>
    #include<stdlib.h>
    #include<arpa/inet.h>
    #include<ctype.h>
    #include<strings.h>
    #define SERV_PORT 6666
    #define SERV_IP "127.0.0.1"
    int main()
    {
            int lfd,cfd;   
            lfd=socket(AF_INET,SOCK_STREAM,0);  //创建套接字 ipv4 TCP 方式
            socklen_t clie_addr_len;        //客户端长度
    
            struct sockaddr_in serv_addr,clie_addr;   //定义服务端和客户端sockaddr
           bzero(&serv_addr,sizeof(serv_addr));
            serv_addr.sin_family=AF_INET;   //初始化服务端sockaddr 设置ipv4
            serv_addr.sin_port=htons(SERV_PORT);  //设置 端口号
            serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); //设置ip INADDR_ANY为本地可用ip
            bind(lfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr)); //绑定套接字和addr
    
            listen(lfd,100);  //设置客户端同时连接数
    
            clie_addr_len=sizeof(clie_addr);  //获得的客户端sockaddr 长度
      
            cfd=accept(lfd,(struct sockaddr*)&clie_addr,&clie_addr_len); 
    //cfd为与客户端通讯的实际的文件描述符,clie_addr为传出参数,保护客户端地址信息,clie_addr_len为传入传入参数accpet之后会阻塞等待客户端connect 
            printf("client ip:%s,client port:%d
    ",inet_ntop(AF_INET,&clie_addr.sin_addr.s_addr,clie_IP,sizeof(clie_IP)),ntohs(clie_addr.sin_port));
    
            char buf[BUFSIZ];
            int count,i;
            while(1)
            {
                    count=read(cfd,buf,sizeof(buf)); //从套接字另一端读数据
                    for(i=0;i<count;i++)
                    {
                            buf[i]=toupper(buf[i]);  //将小写字母转换成大写字母
                    }
                    write(cfd,buf,count);   //将数据写入
            }
            close(cfd);  //关闭套接字
            close(lfd);  //关闭套接字
    
            return 0;
    }    

    客户端

    #include<stdio.h>
    #include<unistd.h>
    #include<sys/socket.h>
    #include<stdlib.h>
    #include<arpa/inet.h>
    #include<string.h>
    
    
    #define SERV_IP "127.0.0.1"
    #define SERV_PORT 6666
    int main()
    {
            int fd;  //文件描述符
            fd=socket(AF_INET,SOCK_STREAM,0);//创建套接字
            struct sockaddr_in serv_addr; //服务器地址
    
            memset(&serv_addr,0,sizeof(serv_addr)); //将serv_addr清0
            serv_addr.sin_family=AF_INET;  //设置服务器 ipv4
            serv_addr.sin_port=htons(SERV_PORT); //端口
            inet_pton(fd,SERV_IP,&serv_addr.sin_addr.s_addr); //ip
    
            connect(fd,(struct sockaddr*)&serv_addr,sizeof(serv_addr)); //连接服务器
    
            char buf[BUFSIZ];
            int count;
            while(1)
    {
            fgets(buf,sizeof(buf),stdin);  //键盘输入
            write(fd,buf,strlen(buf));  //向套接字写数据
            count=read(fd,buf,sizeof(buf)); //从服务器读返回的数据
            write(STDOUT_FILENO,buf,count); //写到屏幕上
    }
            close(fd);
            return 0;
    }
    

      

     

  • 相关阅读:
    【产品干货】经典营销模型的产品化介绍
    阿里云力夺FewCLUE榜首!知识融入预训练+小样本学习的实战解析
    云上资源编排的思与悟
    储留香:一个智能运维系统就是一个中枢神经系统,我说的!
    企业网管软件之SOLARWINDS实战-制作拓扑图
    企业网管软件之SOLARWINDS实战-基于浏览器的网络流量监控
    企业网管软件实战之看视频学装Cisco Works 2000
    轻松学习Linux之Shell预定义变量
    oracle的监听日志太大,正确的删除步骤
    MVC 使用HandleErrorAttribute统一处理异常
  • 原文地址:https://www.cnblogs.com/sclu/p/11291457.html
Copyright © 2011-2022 走看看