Socket学习一

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/buhuiguowang/article/details/81590587

Socket


  • socket 概念:
    什么是socket?
    • socket 可以看成是用户j进程与内核网络协议栈的编程接口。
    • socket 不仅用于本机进程间的通信,还可以用于网络上不同主机的进程间的通信

      IPv4套接口地址结构

  • IPv4套接口地址结构也称为“网际套接字地址结构”, 它以“sockaddr_in”命名,定义在头文件中
  • 结构体如下
      struct  sockaddr_in
      {
          uint8_t               sin_len;   //(这个成员在有的内核中已经不使用了,根据实际情况来定,下面有介绍怎么查 ip 结构的具体内容)sin_len: 整个 sockaddr_in结构体的长度
          sa_family_t           sin_family;  // sin_family指定该地址家族,在这里必须设置为AF_INET(它代表TCP协议)
          in_port_t             sin_port;     //端口
          struct  in_addr       sin_addr;    //IPv4的地址
          char                  sin_zero[8]; //暂不使用,一般将其设置为0
      };
    

通用地址结构

  • 通用地址结构用来指定与套接字关联的地址
  • 结构体如下
    struct sockaddr
    {
      unit_8             sin_len; //sin_len 整个 sockaddr 结构体的长度
      sa_family_t        sin_family; //指定该地址家族
      char               sa_data[14]; //sin_family 决定它的形式
    }
    
  • 为什么会有两个 IP 地址结构?
  • 因为是先具体定义是使用那个协议的结构,然后再将其强制转换为通用结构,再传给socketapi函数
    • 可以用 man 7 ip 来查看这个函数的用法

sa_data[14] 就是 in_port_t sin_port、struct in_addr sin_addr char sin_zero[8]这三成员的大小

  • 可以这样认为,这两种结构体是通用的

端口号的理解

有这样一种情况

  • 一客户端的应用程序和服务器的应用程序通信,如图

    服务器如何判断客户是要和那个应用程序通信
  1. 首先,要确定是哪一个主机的ip,
  2. 然后,再确定主机的哪一个应用程序即端口号。
    • 比如app2-1对应的端口号是8001和app1,app2-2对应的端口号是8002对应app2
    • 所以可以这样理解,应用程序就是通过端口号来区分的,对端口号发信息。

网络字节序

  1. 大端字节序(Big Endian)
    • 最高有效位储存于最低内存地址处,最低有效位储存于最高内存地址处。
  2. 小端字节序(little Endian)
    • 最高有效位储存于最高内存地址处,最低有效位储存于最低内存地址处。
  3. 主机字节序
    • 不同的主机有不同的字节序,如x86为小段字节序, Motorola 6800 为大端字节序,ARM 字节序是可配置的。
  4. 网络字节序
    • 网络字节序规定为大端字节序
    • 用程序判别主机是大端字节序、还是小端字节序
    • 代码如下
#include <stdio.h>

void main()
{
    unsigned int data = 0x12345678;
    char           *p = &data;

    printf("%x  %x  %x \n,   p[0], p[1], p[2], p[3]");

    if(p[0] ==  0x78)
    {
        printf("The System is Little  Endian");
    }
    else
    {
        printf("The System is Big  Endian");
    }
}

字节序转换函数

  1. uint32_t htonl(uint32_t hostlong);
  2. uint16_t htons(uint16_t hostshort);
  3. uint32_t ntohl(uint32_t netlong);
  4. uint16_t ntohs(uint16_t netshort);
    说明:这里h代表host;n代表networks;s代表short;L代表long
    将上面代码字节序转换为网络字节序
#include <stdio.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);
    *  /
#include <arpa/inet.h>   ///Byte order  conversion header file

void main()
{
    unsigned int data = 0x12345678;
    char           *p = &data;

    printf("%x  %x  %x \n",   p[0], p[1], p[2], p[3]);

    if(p[0] ==  0x78)
    {
        printf("The System is Little  Endian");
    }
    else
    {
        printf("The System is Big  Endian");
    }

    printf("Converting local byte order into network byte order");
    uint32_t     mydata =  htonl (data);
                     p  = &mydata;
     printf("%x  %x  %x \n,   q[0], q[1], q[2], q[3]");

    if(p[0] ==  0x78)
    {
        printf("The network byte order is Little  Endian");
    }
    else
    {
        printf("The network byte order is Big  Endian");
    }

}

为什么有地址转换函数

因为在tcp、ip协议里面 ipv4 是32bit,需要将其转换成类似与192.168.xxx.xxx的点分ip,或者将点分ip转换成32bit的ip

1. #include <netinet/in.h>
2. #include <arpa/inet.h>
3. int inet_aton(const char *cp, struct in_addr *inp);   //把stuct转换成char *即把点分十进制转换成一个结构(32位的数)
4. int_addr_t inet_addr(const char *cp);  //把点分十进制转换成一个32位的数
5. char *inet_ntoa(struct in_addr in); //把结转换成一个char *即把一个结构(32位的数)转换成点分十进制
#include <sys/socket.h>   //Byte order  conversion header file
#include <netinet/in.h>    //adress conversion function
#include <arpa/inet.h>

void main()
{
      /* int inet_aton(const char *cp, struct in_addr *inp); //1
       in_addr_t inet_addr(const char *cp);   //2
       in_addr_t inet_network(const char *cp);
       char *inet_ntoa(struct in_addr in);   //4  分配一个元素,这里就是一个传递结构体成员值的过程,所以这里不用传地址
       struct in_addr inet_makeaddr(int net, int host);
       in_addr_t inet_lnaof(struct in_addr in);    
      */    
}
test 4

#include <sys/socket.h>   //Byte order  conversion header file
#include <netinet/in.h> //adress conversion function
#include <arpa/inet.h>

void main()
{
    /*  int inet_aton(const char *cp, struct in_addr *inp); //1
    *   in_addr_t inet_addr(const char *cp);   //2
    *   in_addr_t inet_network(const char *cp);
    *   char *inet_ntoa(struct in_addr in);    //3
    *   struct in_addr inet_makeaddr(int net, int host);
    *   in_addr_t inet_lnaof(struct in_addr in);  
    */


    in_addr_t myinet = inet_addr("192.168.23.1");   //2 
    printf("%u\n", myinet);

    /* Internet address.
     * struct in_addr
     * {
     *      uint32_t       s_addr; //address in network byte order
     * };
     */
    struct in_addr myaddr;


    inet_aton("192.168.23.1", &myaddr);  //1
    printf("%u\n", myaddr.s_addr);


    char *inet_ntoa(myaddr);    //4
    printf("%s\n", inet_ntoa(myaddr));

套接字类型

  • 流式套接字(SOCK_STREAM)
    • 提供面向连接的、可靠的数据传输服务,数据无差错,无重复的发送,且按发送顺序接收。
  • 数据报式套接字(SOCK_DGRAM)
    • 提供无连接服务。不提供无错保证,数据可能丢失或重复,并且接收顺序混乱
  • 原始套接字(SOCK_RAW)

SocketApi 基本编程思想


Tcp客户/服务器模型


服务器

  • socket的简单实现:服务器接收到客户端的数据,再回馈给客户端

    • socket函数:
      1.1 包含头文件
      1.2功能:创建一个套接字用于通信
      1.3原型:int socket(int domain, int type, int protocol);
      1.4参数:

      • domain:指定通信协议族(protocol family)
      • type:指定socket类型,流式套接字SOCK_STREAM,数据报套接字SOCK_DGRAM,原始套接字SOCK_RAM
      • protocol:协议类型

      1.5 返回值:成功则返回非负整数,失败则返回-1.
      1.6 man socket查询帮助


  • bind函数:
    2.1 包含头文件
    2.2功能:绑定一个本地地址到套接字
    2.3原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    2.4参数:

    • sockfd:socket函数返回的套接字
    • addr:要绑定的地址
    • addrlen:地址长度

    2.5返回值:成功返回0;失败返回-1
    2.6man bind来查询用法
    2.7几个特殊绑定的IP地址

    • INADDR_LOOPBACK (127.0.0.1)
    • INADDR_ANY (0.0.0.0)
    • INADDR_BROADCAST (255.255.255.255)

  • listen函数
    3.1 一般来说,listen函数应该在调用socket和bind函数之后,调用accept函数之前。
    3.2对于给定的监听端口,内核要维护两个队列
    • 已由客户端发出并到达服务器,服务器正在等待完成相应的TCP三路握手过程
    • 已完成连接的队列
    • TCP为监听套接口维护的两个队列
    • TCP三次握手和监听套口的两个队列

  • accept函数
    4.1包含头文件
    4.2功能:从已完成连接队列返回第一个连接,如果已完成连接队列为空,则阻塞
    4.2原型:int accept(int sockfd, struct sockaddr addr, socklen_t addrlen)
    4.3参数:

    • sockfd:未连接套接字
    • addr:要连接套接字的地址
    • addrlen:第二个参数addr长度

    4.4返回值:成功返回0;失败返回-1
    服务器端实现代码

/*********************************************************************************
 *      Copyright:  (C) 2018 anzhihong<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  socket_server.c
 *    Description:  This file is socket_server 
 *                 
 *        Version:  1.0.0(07/23/2018)
 *         Author:  anzhihong <[email protected]>
 *      ChangeLog:  1, Release initial version on "07/23/2018 08:37:11 PM"
 *                 
 ********************************************************************************/
#include <stdio.h>

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

int main()
{
    /*
    man socket
    int socket(int domain, int type, int protocol);
    */
    int sockfd = 0;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        perror("fun socket\n");
        exit(0);
    }
    /*
    man 7 ip
    struct sockaddr_in
    {
        sa_family_t    sin_family;  //address family: AF_INET
        in_port_t      sin_port;   //port in network byte order
        struct in_addr sin_addr;  //internet address
    };
    //Internet address.
    struct in_addr
    {
        uint32_t       s_addr;     //address in network byte order
    };

    struct sockaddr
    {
        sa_family_t sa_family;
        char        sa_data[14];
    };
    */
    struct sockaddr_in srvaddr;
    srvaddr.sin_family = AF_INET;
    srvaddr.sin_port = htons(8001);
    srvaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    /*
    man 2 bind
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

    struct sockaddr
    {
        sa_family_t sa_family;
        char        sa_data[14];
    };
    */
    if(bind(sockfd, (struct sockaddr *)&srvaddr, sizeof(srvaddr)) < 0)
    {
        perror("fun socket\n");
        exit(0);
    }

    /*
     man listen
     int listen(int sockfd, int backlog);
    */
    //Once you call listen function,this socket will become passive,it means that can only recive and doesn't send actively
    if(listen(sockfd, SOMAXCONN) < 0)
    {
        perror("fun socket\n");
        exit(0);
    }

    //struct sockaddr peeraddr;  //Universal ip
    //socklen_t peerlen;
    struct sockaddr_in
    {
        sa_family_t    sin_family;  //address family: AF_INET
        in_port_t      sin_port;   //port in network byte order
        struct in_addr sin_addr;  //internet address
    };
    //man 2 accept
    //int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    struct sockaddr_in peeraddr;  //tcpip address structure
    socklen_t peerlen = sizeof(peerlen);
    unsigned int conn = 0;

    //Accept will return a new connection, it is 
    conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen);
    if(conn == -1)
    {
        perror("fun socket\n");
        exit(0);
    }
    printf("peeraddr:%s\n peerport:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));

    char rvbuf[1024] ;
    while(1)
    {
        /*
        man 2 read
        size_t read(int fd, void *buf, size_t count);
        */
    memset(rvbuf, 0, sizeof(rvbuf));
        int ret = read(conn, rvbuf, sizeof(rvbuf));
        if(ret == 0)
        {
            //If the other party is already closed during the reading process,tcpip will return a zero packet
            printf("peer is already closed\n");
            exit(0);
        }
        fputs(rvbuf, stdout);  // server receives the data and prints the screen.
        /*
        man 2 write
        size_t write(int fd, const void *buf, size_t count);
        */
        write(conn, rvbuf,  strlen(rvbuf));  //server sends pocket back 
        memset(rvbuf, 0, sizeof(rvbuf));
    }

    return 0;

}

客户端

  • connect函数
    5.1包含头文件#include 
    5.2功能:建立一个连接至addr 所指定的套接字
    5.3原型:int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    5.4参数:

    • sockfd未连接套接字
    • addr:要连接的套接字地址
    • addrlen:第二个参数addr长度

    5.5返回值:成功返回0,失败返回-1
    客户端实现代码:

/*********************************************************************************
 *      Copyright:  (C) 2018 anzhihong<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  socket_server.c
 *    Description:  This file is socket_client 
 *                 
 *        Version:  1.0.0(07/23/2018)
 *         Author:  anzhihong <[email protected]>
 *      ChangeLog:  1, Release initial version on "07/23/2018 08:37:11 PM"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <string.h>

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

int main()
{
    /*
    man socket
    int socket(int domain, int type, int protocol);
    */
    int sockfd = 0;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        perror("fun socket\n");
        exit(0);
    }
    /*
    man 7 ip
    struct sockaddr_in
    {
        sa_family_t    sin_family;  //address family: AF_INET
        in_port_t      sin_port;   //port in network byte order
        struct in_addr sin_addr;  //internet address
    };
    //Internet address.
    struct in_addr
    {
        uint32_t       s_addr;     //address in network byte order
    };

    struct sockaddr
    {
        sa_family_t sa_family;
        char        sa_data[14];
    };
    */
    struct sockaddr_in srvaddr;
    srvaddr.sin_family = AF_INET;
    srvaddr.sin_port = htons(8001);
    srvaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    //man connect
    //int connect(int sockfd, const struct sockaddr *addr, socklen_t 
    if(connect(sockfd, (struct sockaddr *)(&srvaddr), sizeof(srvaddr)) < 0)
    { 
        perror("fun socket\n");
        exit(0);
    }
    //printf("peeraddr:%s\n peerport:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));

    char revbuf[1024] = {0};
    char sendbuf[10244]  = {0};

    //man fgets
    //char *fgets(char *s, int size, FILE *stream);
    while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
    {
        //Write data to the server
        write(sockfd, sendbuf, strlen(sendbuf)); //server send message back
        //Read data to the server
        read(sockfd, revbuf, sizeof(revbuf));
        //Print
        fputs(revbuf, stdout);

        memset(revbuf, 0, sizeof(revbuf));
        memset(sendbuf, 0, sizeof(sendbuf));

    }
    close(sockfd);
    return 0;

}

可以自己做个测试:

  • 在客户端与服务器建立起连接后;
  • 用命令 netstat -na | grep 8001 来查看监听的端口状态
  • 然后先断开服务器。再用netstat -na | grep 8001查看
  • 然后再用客户端发送消息,再查看8001的状态

到这里,简单的socket服务通信的大体框架搭建完毕。


问题1:在发送两个以上的字符后,再发送空字符,服务器仍然会将最后一个字符发送客户端,发送三个字符,会将最后两个发送给客户端?
这里是因为在使用完rvbuf后,没有清除,发送的 \n 覆盖了第一个字符,所以出现这样的情况。
问题2:端口,ip地址固定,更改不便。(通过传参解决,后面介绍)
问题3:先将服务器关闭,客户端不管,再启动服务器,无法启动

  • 解决这个问题需要用到Socket API中的地址复用

    • 服务器端尽可能使用SO_REUSEADDR
    • 在绑定之前尽可能调用setsockopt来设置SO_REUSEADDR套接字选项。使用SO_REUSEADDR选项可以使得不必等待TIME_WAIT状态消失就可以重启服务器
    • 地址复用函数 man setsockopt
    • 头文件:#include
    • 功能:可以不必等待客户机再关闭,就可以重启服务器。
    • 原型:int getsockopt(int sockfd, int level, int optname, void optval, socklen_t optlen); int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
    • 参数:

      • sockfd 未连接套接字
      • level 设置级别(指定选项代码类型)
      • optname(选项名): 选项名称
      • optval(选项值): 是一个指向变量的指针 类型:整形,套接口结构, 其他结构类型:linger{}, timeval{ }
      • optlen(选项长度) :optval 的大小
    • 返回值:成功返回0,不成功返回-1.

将函数放到bind之前

int optval = 1;
if(setsockopt(sockfd, SOL_SOCKET,  SO_REUSEADDR, &optval,sizeof(optlen)) < 0 )    // int optname  在man 7 ip里面查找
{
    perror("fun socket\n");
        exit(0);
}

if(bind(sockfd, (struct sockaddr *)&srvaddr, sizeof(srvaddr)) < 0)
 {
       perror("fun socket\n");
       exit(0);
}
  • 这样问题就解决了
  • 问题4:服务器一对多的实现????(多并发)
    • 创建多进程:
    • 代码实现

利用fork函数,来实现服务器一对多的功能

服务器
*********************************************************************************
 *      Copyright:  (C) 2018 anzhihong<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  socket_server.c
 *    Description:  This file is socket_server 
 *                 
 *        Version:  1.0.0(07/23/2018)
 *         Author:  anzhihong <[email protected]>
 *      ChangeLog:  1, Release initial version on "07/23/2018 08:37:11 PM"
 *                 
 ********************************************************************************/
#include <stdio.h>

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

int main()
{
    /*
    man socket
    int socket(int domain, int type, int protocol);
    */
    int sockfd = 0;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        perror("fun socket\n");
        exit(0);
    }
    /*
    man 7 ip
    struct sockaddr_in
    {
        sa_family_t    sin_family;  //address family: AF_INET
        in_port_t      sin_port;   //port in network byte order
        struct in_addr sin_addr;  //internet address
    };
    //Internet address.
    struct in_addr
    {
        uint32_t       s_addr;     //address in network byte order
    };

    struct sockaddr
    {
        sa_family_t sa_family;
        char        sa_data[14];
    };
    */
    struct sockaddr_in srvaddr;
    srvaddr.sin_family = AF_INET;
    srvaddr.sin_port = htons(8001);
    srvaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    /*
    man 2 bind
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

    struct sockaddr
    {
        sa_family_t sa_family;
        char        sa_data[14];
    };
    */
    if(bind(sockfd, (struct sockaddr *)&srvaddr, sizeof(srvaddr)) < 0)
    {
        perror("fun socket\n");
        exit(0);
    }

    /*
     man listen
     int listen(int sockfd, int backlog);
    */
    //Once you call listen function,this socket will become passive,it means that can only recive and doesn't send actively
    if(listen(sockfd, SOMAXCONN) < 0)
    {
        perror("fun listen\n");
        exit(0);
    }

    //struct sockaddr peeraddr;  //Universal ip
    //socklen_t peerlen;
    struct sockaddr_in
    {
        sa_family_t    sin_family;  //address family: AF_INET
        in_port_t      sin_port;   //port in network byte order
        struct in_addr sin_addr;  //internet address
    };
    //man 2 accept
    //int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    struct sockaddr_in peeraddr;  //tcpip address structure
    socklen_t peerlen = sizeof(peerlen);
    unsigned int conn = 0;

    while(1)
    {
        //Accept will return a new connection, it is 
    //If there is no client connection, it will block here
        conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen);
        if(conn == -1)
        {
            perror("fun conn\n");
            exit(0);
        }
        printf("peeraddr:%s\n peerport:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));

        int pid = fork();
        if(pid == 0)  //this process is child process
        {
            close(sockfd);    // child process doesn't need to listen
            char rvbuf[1024] = {0};
            while(1)
            {
                /*
                man 2 read
                size_t read(int fd, void *buf, size_t count);
                */
                int ret = read(conn, rvbuf, sizeof(rvbuf));
                if(ret == 0)
                {
                    //If the other party is already closed during the reading process,tcpip will return a zero packet
                    printf("peer is already closed\n");
                    exit(0);
                }
                else if(ret < 0)
                {
                    printf("read data failure\n");
                    exit(0);
                }
                fputs(rvbuf, stdout);  // server receives the data and prints the screen.
                /*
                man 2 write
                size_t write(int fd, const void *buf, size_t count);
                */
                write(conn, rvbuf, sizeof(ret));  //server sends pocket back       
            }
        }

        else if(pid > 0)   //parent process
        {
            close(conn);   //parent doesn't need connection
        }

        else
        {
            printf("create fork failure");
            exit(0);
        }
    }
        close(sockfd);
        close(conn);

    return 0;

}

  • 实现服务器给客户端发送信息
  • 代码如下:
客户端
/*********************************************************************************
 *      Copyright:  (C) 2018 anzhihong<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  socket_client.c
 *    Description:  This file is socket_client 
 *                 
 *        Version:  1.0.0(07/23/2018)
 *         Author:  anzhihong <[email protected]>
 *      ChangeLog:  1, Release initial version on "07/23/2018 08:37:11 PM"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <string.h>

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

int main()
{
    /*
    man socket
    int socket(int domain, int type, int protocol);
    */
    int sockfd = 0;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        perror("fun socket\n");
        exit(0);
    }
    /*
    man 7 ip
    struct sockaddr_in
    {
        sa_family_t    sin_family;  //address family: AF_INET
        in_port_t      sin_port;   //port in network byte order
        struct in_addr sin_addr;  //internet address
    };
    //Internet address.
    struct in_addr
    {
        uint32_t       s_addr;     //address in network byte order
    };

    struct sockaddr
    {
        sa_family_t sa_family;
        char        sa_data[14];
    };
    */
    struct sockaddr_in srvaddr;
    srvaddr.sin_family = AF_INET;
    srvaddr.sin_port = htons(8002);
    srvaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    //man connect
    //int connect(int sockfd, const struct sockaddr *addr, socklen_t 
    if(connect(sockfd, (struct sockaddr *)(&srvaddr), sizeof(srvaddr)) < 0)
    { 
        perror("fun socket\n");
        exit(0);
    }
    //printf("peeraddr:%s\n peerport:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));

    char revbuf[1024];
    char sendbuf[1024];
    //man fgets
    //char *fgets(char *s, int size, FILE *stream);
    while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL);
    {
        memset(revbuf, 0, sizeof(revbuf));
        memset(sendbuf, 0, sizeof(sendbuf));
        printf("sendfbuf:%s ", sendbuf);
        //Write data to the server
        write(sockfd, sendbuf, strlen(sendbuf)); //server send message back
        //Read data to the server
        read(sockfd, revbuf, sizeof(revbuf));
        //Print
        fputs(revbuf, stdout);
        memset(revbuf, 0, sizeof(revbuf));
        memset(sendbuf, 0, sizeof(sendbuf));

    }
    close(sockfd);
    return 0;

}
服务端
/*********************************************************************************
 *      Copyright:  (C) 2018 anzhihong<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  socket_server.c
 *    Description:  This file is socket_server 
 *                 
 *        Version:  1.0.0(07/23/2018)
 *         Author:  anzhihong <[email protected]>
 *      ChangeLog:  1, Release initial version on "07/23/2018 08:37:11 PM"
 *                 
 ********************************************************************************/
#include <stdio.h>

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

int main()
{
    /*
    man socket
    int socket(int domain, int type, int protocol);
    */
    int sockfd = 0;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd == -1)
    {
        perror("fun socket\n");
        exit(0);
    }
    /*
    man 7 ip
    struct sockaddr_in
    {
        sa_family_t    sin_family;  //address family: AF_INET
        in_port_t      sin_port;   //port in network byte order
        struct in_addr sin_addr;  //internet address
    };
    //Internet address.
    struct in_addr
    {
        uint32_t       s_addr;     //address in network byte order
    };

    struct sockaddr
    {
        sa_family_t sa_family;
        char        sa_data[14];
    };
    */
    struct sockaddr_in srvaddr;
    srvaddr.sin_family = AF_INET;
    srvaddr.sin_port = htons(8002);
    srvaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    /*
    man 2 bind
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

    struct sockaddr
    {
        sa_family_t sa_family;
        char        sa_data[14];
    };
    */
    if(bind(sockfd, (struct sockaddr *)&srvaddr, sizeof(srvaddr)) < 0)
    {
        perror("fun socket\n");
        exit(0);
    }

    /*
     man listen
     int listen(int sockfd, int backlog);
    */
    //Once you call listen function,this socket will become passive,it means that can only recive and doesn't send actively
    if(listen(sockfd, SOMAXCONN) < 0)
    {
        perror("fun listen\n");
        exit(0);
    }

    //struct sockaddr peeraddr;  //Universal ip
    //socklen_t peerlen;
    struct sockaddr_in
    {
        sa_family_t    sin_family;  //address family: AF_INET
        in_port_t      sin_port;   //port in network byte order
        struct in_addr sin_addr;  //internet address
    };
    //man 2 accept
    //int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    struct sockaddr_in peeraddr;  //tcpip address structure
    socklen_t peerlen = sizeof(peerlen);
    unsigned int conn = 0;

    while(1)
    {
        //Accept will return a new connection, it is 
        conn = accept(sockfd, (struct sockaddr *)&peeraddr, &peerlen);
        if(conn == -1)
        {
            perror("fun conn\n");
            exit(0);
        }
        printf("peeraddr:%s\n peerport:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));

        int pid = fork();
        if(pid == 0)  //this process is child process
        {
            close(sockfd);    //close sockfd
            char rvbuf[1024];
            memset(rvbuf,0, sizeof(rvbuf));
            while(1)
            {
                /*
                man 2 read
                size_t read(int fd, void *buf, size_t count);
                */
                int ret = read(conn, rvbuf, sizeof(rvbuf));
                printf("rvbuf is:%s\n", rvbuf);
                if(ret == 0)
                {
                    //If the other party is already closed during the reading process,tcpip will return a zero packet
                    printf("peer is already closed\n");
                    exit(0);
                }
                else if(ret < 0)
                {
                    printf("read data failure\n");
                    exit(0);
                }
                fputs(rvbuf, stdout);  // server receives the data and prints the screen.
                /*
                man 2 write
                size_t write(int fd, const void *buf, size_t count);
                */
                //write(conn, rvbuf, ret);  //server sends pocket back
                memset(rvbuf, 0, sizeof(rvbuf));
                fgets(rvbuf, sizeof(rvbuf), stdin);
                write(conn, rvbuf, ret);  //server sends pocket back
                //printf("rvbuf is:%s\n", &rvbuf);

            }
        }

        else if(pid > 0)   //parent process
        {
            close(conn);   //parent doesn't need connection
        }

        else
        {
            printf("create fork failure");
            exit(0);
        }
    }
        close(sockfd);
        close(conn);

    return 0;
}
  • 建立连接后,想知道到客户端自己的IP地址??
  • 函数原型:int getsockname(int sockfd, struct sockaddr addr, socklen_t addrlen);
/*********************************************************************************
 *      Copyright:  (C) 2018 anzhihong<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  socket_client.c
 *    Description:  This file is socket_client 
 *                 
 *        Version:  1.0.0(07/23/2018)
 *         Author:  anzhihong <[email protected]>
 *      ChangeLog:  1, Release initial version on "07/23/2018 08:37:11 PM"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <string.h>

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

#define ERR_EXIT(m) \
    do \
    {\
        perror(m);\
        exit(EXIT_FAILURE); \
    } while(0)
int main()
{
    /*
    man socket
    int socket(int domain, int type, int protocol);
    */
    int sockfd = 0;
    //sockfd = socket(AF_INET, SOCK_STREAM, 0);
    sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sockfd == -1)
    {
       //perror("fun socket\n");
       //exit(0);
       ERR_EXIT("socket"); 
    }
    /*
    man 7 ip
    struct sockaddr_in
    {
        sa_family_t    sin_family;  //address family: AF_INET
        in_port_t      sin_port;   //port in network byte order
        struct in_addr sin_addr;  //internet address
    };
    //Internet address.
    struct in_addr
    {
        uint32_t       s_addr;     //address in network byte order
    };

    struct sockaddr
    {
        sa_family_t sa_family;
        char        sa_data[14];
    };
    */
    struct sockaddr_in srvaddr;
    srvaddr.sin_family = AF_INET;
    srvaddr.sin_port = htons(8001);
    srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    /*srvaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
    /*intet_aton("127.0.0.1", &server.sin_addr);*/

    //man connect
    //int connect(int sockfd, const struct sockaddr *addr, socklen_t 
    if(connect(sockfd, (struct sockaddr *)(&srvaddr), sizeof(srvaddr)) < 0)
    { 
        //perror("fun socket\n");
        //exit(0);
        ERR_EXIT("fun socket");
    }

    //获取本地的地址,即端口号的地址(必须是已经建立好的套接口连接才能调用这个函数)
    struct sockaddr_in localaddr;
    socklen_t addrlen = sizeof(localaddr);
    if(getsockname(sockfd, (struct sockaddr*)&localaddr, &addrlen) < 0)
        ERR_EXIT("getsockname");

    printf("ip = %s\n port = %d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port));

    char revbuf[1024] = {0};
    char sendbuf[10244]  = {0};

    int ret;
    //man fgets
    //char *fgets(char *s, int size, FILE *stream);
    while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
    {
        //Write data to the server
        write(sockfd, sendbuf, strlen(sendbuf)); //server send message back
        //Read data to the server
        ret = read(sockfd, revbuf, sizeof(revbuf));
        if(ret == -1)
        {
            ERR_EXIT("read");
            break;
        }
        else if(ret == 0)
        {
            ERR_EXIT("server is closed");
            break;
        }
        //Print
        fputs(revbuf, stdout);

        memset(revbuf, 0, sizeof(revbuf));
        memset(sendbuf, 0, sizeof(sendbuf));

    }
    close(sockfd);
    return 0;

}

  • 获取主机名
  • 函数原型:int gethostname(char *name, size_t len);
  • 返回对应于给定主机名的主机信息:
  • 函数原型:struct hostent gethostbyname(const char name);
/*********************************************************************************
 *      Copyright:  (C) 2018 anzhihong<[email protected]>
 *                  All rights reserved.
 *
 *       Filename:  getip.c
 *    Description:  This file is get ip
 *                 
 *        Version:  1.0.0(07/23/2018)
 *         Author:  anzhihong <[email protected]>
 *      ChangeLog:  1, Release initial version on "07/23/2018 08:37:11 PM"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <string.h>

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

#define ERR_EXIT(m) \
    do \
    { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

int getlocalip(char *ip)
{
    char host[100] = {0};
    if(gethostname(host, sizeof(host)) < 0)
        return -1;
    struct hostent *hp;
    if((hp = gethostbyname(host)) == NULL)
        return -1;
    //strcpy(ip, inet_ntoa(*(struct in_addr*)hp->h_addr_list[0]));
    strcpy(ip, inet_ntoa(*(struct in_addr*)hp->h_addr));

    printf("%s\n", inet_ntoa(*(struct in_addr*)hp->h_addr_list[0]));  //这里虽然是char **h_addr_list类型,但内部保存的是IP地址的结构,所以要强制转换成 *(struct int_addr*) 结构体
       return 0;
}
int main(void)
{
    char host[100] = {0};
    if(gethostname(host, sizeof(host)) < 0)
        ERR_EXIT("gethostname");

    struct hostent *hp;
    if((hp = gethostbyname(host)) == NULL)
        ERR_EXIT("gethostbyname");

    int i = 0;
    while(hp->h_addr_list[i] != NULL)   //遍历所有地址。这里的char **h_addr_list;保存的地址并不是点分十进制
    {
       printf("%s\n", inet_ntoa(*(struct in_addr*)hp->h_addr_list[i]));  //这里虽然是char **h_addr_list类型,但内部保存的是IP地址的结构,所以要强制转换成 *(struct int_addr*) 结构体。
       i++;
    }

    char ip[16] = {0};
    getlocalip(ip);
    printf("localip = %s\n", ip);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/buhuiguowang/article/details/81590587