• 网络编程、socket编程、多进程并发服务器


    网络编程

    一、TCP编程的API

    socket:

    int socket(int domain, int type, int protocol);
    返回值:> 0 代表函数调用成功,这个值是一个文件描述符
            < 0 代表函数调用失败
    int domain:地址簇 AF_INET:IPv4 AF_INET6: IPv6 
    ​
    int type: 套接字类型 SOCK_STREAM:流式套接字   唯一对应TCP 
                         SOCK_DGRAM:  数据报套接字 唯一对应UDP
                         SOCK_RAW:    原始套接字   针对较低层次协议
    ​
    int protocol: 默认为0  

    bind:

    /*绑定通信IP和端口*/
    struct sockaddr_in {
             sa_family_t sin_family; /* 地址族: AF_INET 2字节 */
             u_int16_t sin_port; /* 按网络字节次序的端口 2字节*/
             struct in_addr sin_addr; /* internet地址 */
             };
    ​
             /* Internet地址. */
             struct in_addr {
             u_int32_t s_addr; /* 按网络字节次序的地址 */
             };
    struct sockaddr_in sddr;
    bzero(&sddr, sizeof(sddr)); //将sddr做清0操作
    ​
    uint16_t htons(uint16_t hostshort);
    把本机字节序转为网络字节序
    uint16_t ntohs(uint16_t netshort);
    把网络字节序转为本机字节序
    ​
    int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
    返回值:成功 0
       失败 -1
    int sockfd:                 代表socket成功返回的文件描述符
    struct sockaddr *my_addr : 要绑定的IP和端口
    socklen_t addrlen :        要绑定的IP和端口的结构体的大小
    

    listen:

    int listen(int s, int backlog);
    返回值:        成功 0
                    失败 -1
    int s:          socket打开的文件描述符
    int backlog :   允许监听的套接字队列元素的最大个数 (数目和性能产生联系)
    

    accept:

    int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
    返回值:                失败 -1
                            成功 非负整数
    int s:                  socket返回的文件描述 
    struct sockaddr *addr: 存储连接进来的客户端的IP和端口 (NULL不关注客户端的信息)
    socklen_t *addrlen: 结构体的大小
    
    

    connect:

    int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
    返回值:                成功 0
                            失败 -1
    int sockfd:         socket返回的文件描述符
    struct sockaddr *addr: 要连接的服务器的IP和PORT
    socklen_t addrlen:      存储要连接服务器IP和PORT的结构体的长度
    ​

    read函数的返回值分析

    返回值为 -1 :read发生错误

    返回值为0 :表示另外一端掉线

    返回值大于0:表示实际读取到的字节数

    注意:如果处于连接状态,且缓冲区没有内容,read会阻塞

    send:

    发送数据,专属网络编程

    ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    int sockfd      : 发送的目标套接字的文件描述符(对象)
    const void *buf : 要发送的内容缓冲区的首地址
    size_t len      : 要发送内容的大小(字节为单位)
    int flags       : 为0效果和write一摸一样(绝大多时刻选择参数)
                      为MSG_DONTWAIT时,它是非阻塞模式
    ssize_t         : >0 表示实际发送的字节数
                    : -1 表示发送失败

    recv:

    发送数据,专属网络编程

    ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    int sockfd      : 接收数据对象的文件描述符
    const void *buf : 接收数据存储的缓冲区首地址
    size_t len      : 期望要接收内容的大小(字节为单位)
    int flags       : 为0效果和read一摸一样(绝大多时刻选择参数)
                      为MSG_DONTWAIT时,它是非阻塞模式
    ssize_t         : >0 表示实际接收的字节数
                    : -1 表示接收失败
                    : 0  表示另外一端退出或者关闭或者掉线

    sendto:

    UDP编程使用的发送函数

    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                          const struct sockaddr *dest_addr, socklen_t addrlen);
    int sockfd                      : 发送的目标套接字的文件描述符(对象)
    const void *buf                 : 要发送的内容缓冲区的首地址
    size_t len                      : 要发送内容的大小(字节为单位)
    int flags                       : 默认为0
    const struct sockaddr *dest_addr: 发送的对象(对象的IP PORT)
    socklen_t addrlen:              : 上一个参数的长度
    ssize_t                         : >0 表示实际发送的字节数
                                    : -1 表示发送失败                     
    

    recvfrom:

    UDP编程使用的发送函数

    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                            struct sockaddr *src_addr, socklen_t *addrlen);
    int sockfd                  : 接收数据对象的文件描述符
    const void *buf             : 接收数据存储的缓冲区首地址
    size_t len                  : 期望要接收内容的大小(字节为单位)
    int flags                   : 默认为0
    struct sockaddr *src_addr   : 发送方的信息(IP PORT)
    socklen_t *addrlen          : 上一个参数的长度
    ssize_t         : >0 表示实际接收的字节数
                    : -1 表示接收失败                     

    二、TCP服务器的模型

    1、循环服务器

    socket(...);
    bind(...);
    listen(...);
    accept(...);
    while(1){
        read(...);
        ...
        write(...);
    }
    TCP循环服务器用的相对较少

    2、并发服务器

    并发服务器实现原理是多次调用accept函数

    多线程实现并发

    socket(...);
    bind(...);
    listen(...);
    while(1){
        accept(...);
        pthread_create(...); //创建线程处理新的链接
    }
    ​

    多进程实现并发

    socket(...);
    bind(...);
    listen(...);
    while(1){
        accept(...);
        fork(...); //创建子进程处理新的链接
    }
    记得回收子进程,否则会出现僵尸进程
    

    服务器代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. #define SIZE 64
    15. int tcp_server_init(const char *ip,int port,int backlog)
    16. {
    17. int fd=socket(AF_INET,SOCK_STREAM,0);
    18. if(-1==fd)
    19. {
    20. return -1;
    21. }
    22. struct sockaddr_in sddr;
    23. sddr.sin_family =AF_INET;
    24. sddr.sin_port=htons(port);
    25. sddr.sin_addr.s_addr=inet_addr(ip);
    26. if(-1==bind(fd,(struct sockaddr *)&sddr,sizeof(sddr)))
    27. {
    28. return -1;
    29. }
    30. if(-1==listen(fd,backlog))
    31. {
    32. return -1;
    33. }
    34. puts("listen...");
    35. return fd;
    36. }
    37. int tcp_server_wait(int fd)
    38. {
    39. struct sockaddr_in cddr;
    40. socklen_t len =sizeof(cddr);
    41. int nfd=accept(fd,(void*)&cddr,&len);
    42. if(-1==nfd)
    43. {
    44. return -1;
    45. }
    46. printf("IP:%s PORT:%hd\n",inet_ntoa(cddr.sin_addr),cddr.sin_port);
    47. return nfd;
    48. }
    49. void signal_handler(int no)
    50. {
    51. pid_t pid=waitpid(-1,NULL,WNOHANG);
    52. printf("%d 被回收!\n",pid);
    53. }
    54. void child_handler(int fd)
    55. {
    56. char buf[SIZE];
    57. int ret;
    58. while(1)
    59. {
    60. ret=read(fd,buf,SIZE);
    61. if(0>=ret)
    62. {
    63. exit(0);
    64. }
    65. if(0==strncmp(buf,"quit",4))
    66. {
    67. exit(0);
    68. }
    69. sprintf(buf,"Recv size:%d",ret);
    70. write(fd,buf,SIZE);
    71. }
    72. }
    73. int main(int argc, char *argv[])
    74. {
    75. int fd=tcp_server_init("0",8881,20);
    76. if(-1==fd)
    77. {
    78. perror("tcp init");
    79. return -1;
    80. }
    81. int nfd;
    82. signal(SIGCHLD,signal_handler);
    83. while(1)
    84. {
    85. nfd=tcp_server_wait(fd);
    86. if(-1==nfd)
    87. {
    88. continue;
    89. }
    90. pid_t pid =fork();
    91. if(-1==pid)
    92. {
    93. close(nfd);
    94. continue;
    95. }else if(0==pid){
    96. close(fd);//子进程值负责通信不负责监听连接,所以关闭监听套接字
    97. //子进程去处理连接的客户端的请求
    98. child_handler(nfd);
    99. }
    100. else{
    101. close(nfd); //父进程只负责监听连接,所以关闭通信套接字
    102. }
    103. }
    104. return 0;
    105. }

     

  • 相关阅读:
    【SpringCloud】设置接口同时支持返回多种数据类型(json、xml)
    设计模式之创建型模式---工厂模式
    YOLOV7训练自己的yolo数据集
    python读取eml文件并用正则匹配邮箱
    【Matplotlib绘制图像大全】(十三):甜甜圈饼图
    【云原生之Docker实战】使用Docker部署OrangeHRM人力资源管理系统
    计算机毕业设计django基于python商品比价平台(源码+系统+mysql数据库+Lw文档)
    数字IC/FPGA面试题目合集解析(一)
    微信支付APIv3
    阿里云轻量应用服务器有月流量限制吗?
  • 原文地址:https://blog.csdn.net/qq_52119661/article/details/132699870