• socket那些事----以linux C实现一个服务器端


    数据发到网络环境通常通过交换机,再由交换机转发至路由器
    但凡提到套接字,必定有两端:数据的发送端和接收端,
    ip地址在网络环境中可以唯一的表示一台主机,ip+端口号可以在网络环境中唯一标识一个进程。
    socket通信原理:
        套接字是linux操作系统中的一种文件类型----伪文件
        linux中有7种文件类型:其中普通文件,目录文件,软链接三种文件类型占用内存,字符设备,块设备,管道,套接字
        管道,fd[0]读端,fd[1]写端,半双工,如同对讲机
        socket乃是双向全双工,一个文件描述符,两个缓冲区(一个读,一个写)
    对于数字8910.67
    低地址存高位----大端法按照内存地址顺序由近及远依次存入8,9,1,0,.6.7
    低地址存低位----小端法(与上边相反)x86就是如此,小端法存储,源于windows基于Intel x86平台
    理论上是这样,不同操作系统可能不尽相同。
    
    网络数据流采用了大端字节序,操作系统采用了小端法存储,这样一来网卡发送的数据字节序发生了变化,这样一来,接收端在解包的过程中无法正确解析IP地址等关键信息,这就需要能转换网络字节序和主机字节序的系列函数
    
    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的惯用手法,但在网络环境中,这种方法显然无法满足要求,这就需要一组函数完成两种(点分十进制和网络字节序)规制的相互转换
    int inet_pton(int domain,char * src, void* des) 第一个参数代表net版本类型 ipv4,或是ipv6
    char * inet_ntop(int domain,const void * src, char* des socklen_t size)
    
    描述ipv4地址协议的结构体(sockaddr_in)
    典故与现实,早期描述ipv4协议的结构体是sockaddr,网络协议绑定函数bind()参数列表中就是这种结构体类型指针,然而定义ipv4协议类型结构体又只能使用 sockaddr_in类型,所以,为了兼容bind()函数要强转类型:
    struct sockaddr_in sockipv4;
    bind(struct sockaddr* &sockipv4)
    linux 结构体查看命令 man 7 ip
    
    
    socket
    int socket(domain,type,protocol)
    domain: ipv4 ipv6
    type:流式协议,报式协议……
    protocol: 默认0
    成功,返回整型文件描述符,失败返回-1
    
    int bind(int sockfd,const struct sockaddr* addr,socklen_t addrlen);往socket上绑定ip和端口号,成功返回0,失败返回-1
    
    int listen()//指定监听上线数,允许同时建立连接(而非保持)的数量(可能允许的数量还未达到支持上限)
    int accept(int fd,struct sockaddr* addr, socklen_t * size)//返回一个新的文件描述符,这一新的文件描述符,作为socket的accept端,
    参数1:指定(我们创建的)套接字(以便基于此后续建立accept套接字)
    参数2:传出参数,与我建立连接的客户端的地址信息。
    参数3:传入传出参数,传入的读一次,传出写一次
    返回:一个全新的socket文件描述符,用来同客户端进行通信
    
    int connect(int sockfd,const struct sockaddr* addr,socklen_t addrlen)
    参数1:客户端新建的socket
    参数2:传入参数,指定服务器的地址信息,含ip地址和端口号。
    参数3:传入参数,传入的addr大小。
    • 一个服务端的实现---功能:把客户端的输入字符转为小写
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <stdlib.h>
    #include <netinet/in.h>
    #define SERV_PORT 6666//1024-65535
    #define BUFSIZE 1024
    int main()
    {
        struct sockaddr_in serv_addr,client_addr;
        int sfd,cfd,n;
        char buf[BUFSIZE];
        sfd = socket(AF_INET,SOCK_STREAM,0);
        serv_addr.sin_family=AF_INET;
        serv_addr.sin_port=htons(SERV_PORT);
        serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
        socklen_t lenofserv_addr = sizeof(serv_addr);
        bind(sfd,(const struct sock_addr*)(&serv_addr),lenofserv_addr);
        listen(sfd,128);
        socklen_t lenofclient_addr = sizeof(client_addr);
        cfd=accept(sfd,(const struct sockaddr*)(&client_addr),&lenofclient_addr);
    
       while(1)//while循环,要写在accept()函数后面,否则除非不断开启客户端,转换完大小写后就会阻塞等待客户端请求
       {
           n = read(cfd,buf,sizeof(buf));
           for(int i=0;i<n;i++)
           {
               buf[i] = tolower(buf[i]);
           }
           write(cfd,buf,n);
       }
        close(sfd);
        close(cfd);
        printf("Hello World!
    ");
        return 0;
    }
    • 编译,启动,warning警示,

     

    • 如果你还没有写好一个客户端,可以开启一个shell终端,输入命令(当然,你的ip和端口号可能与我不同)
    nc 127.0.0.1 6666

    然后尽情使用吧

  • 相关阅读:
    android 休眠唤醒机制分析(三) — suspend
    android 休眠唤醒机制分析(一) — wake_lock
    开机音乐不发声的问题
    Linux的时钟管理
    Android4.2增加新键值
    _IO, _IOR, _IOW, _IOWR 宏的用法与解析
    Mifare 0简介
    Mifare 1卡的存储结构
    Maven 介绍
    DAL 层引用 System.Net.Http ,引发的一阵心慌
  • 原文地址:https://www.cnblogs.com/saintdingspage/p/12267539.html
Copyright © 2020-2023  润新知