• 网页LED——基于http


    当终止服务器运行后,再次进行调试时,出现bind error: Address Already in use

    在bind函数调用前添加,即可免去Linux下的TIME_WAIT的将近两分钟的等待时间

    int on=1;
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));

    上面两行代码,把此套接字listenFd设置为允许地址重用(on=1,如果on=0就是不允许重用了)。这样每次bind的时候,如果此端口正在使用的话,bind就会把端口“抢”过来。就不会报错了。完美解决问题。

    问题深入

    既然TIME_WAIT这么讨厌,那它的存在有什么意义呢?毕竟服务器端已经中断掉连接了呀。记得之前在看UNP的时候,上面好像有提到过,继续翻书:

    书上说,TIME_WAIT状态有两个存在的理由:

    1. 可靠地实现TCP全双工连接的终止;

    2. 允许老的重复分节在网络中消逝。

    原来如此,解释一下,上个图:

    image

    1. 如果服务器最后发送的ACK因为某种原因丢失了,那么客户一定会重新发送FIN,这样因为有TIME_WAIT的存在,服务器会重新发送ACK给客户,如果没有TIME_WAIT,那么无论客户有没有收到ACK,服务器都已经关掉连接了,此时客户重新发送FIN,服务器将不会发送ACK,而是RST,从而使客户端报错。也就是说,TIME_WAIT有助于可靠地实现TCP全双工连接的终止。

    2. 如果没有TIME_WAIT,我们可以在最后一个ACK还未到达客户的时候,就建立一个新的连接。那么此时,如果客户收到了这个ACK的话,就乱套了,必须保证这个ACK完全死掉之后,才能建立新的连接。也就是说,TIME_WAIT允许老的重复分节在网络中消逝。

    回到我们的问题,由于我并不是正常地经过四次断开的方式中断连接,所以并不会存在最后一个ACK的问题。所以,这样是安全的。不过,最终的服务器版本,还是不要设置为端口可复用的。切记。

    demo,在ubuntu或开发板中运行程序

    #include <stdio.h>
    #include <netinet/in.h>
    #include <sys/socket.h> // socket
    #include <sys/types.h>  // 基本数据类型
    #include <unistd.h> // read write
    #include <string.h>
    #include <stdlib.h>
    #include <fcntl.h> // open close
    #include <sys/shm.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <signal.h>
    #include <sys/stat.h>
    #define PORT        8888
    #define SERVER      "0.0.0.0"
    #define BACKLOG     20
    #define BUFF_SIZE   (1024 * 500)
    #define LED_ON      "./led_on.html"
    #define LED_OFF     "./led_off.html"
    
    int sockfd;
    char *http_res_tmpl = "HTTP/1.1 200 OK
    "
            "Server: Cleey's Server V1.0
    "
            "Accept-Ranges: bytes
    "
            "Content-Length: %d
    "
            "Connection: close
    "
            "Content-Type: %s
    
    ";
    
    int http_str_nmatch(const char *str1, const char *str2, int n)
    {
        int i = 0;
        int c1, c2;
        do {
            c1 = *str1++;
            c2 = *str2++;
            i++;
        } while(((c1 == c2) && c1) && (i < n));
        return c1 - c2;
    }
    
    void http_send(int sock_client, char *str) 
    {
        char header[BUFF_SIZE], body[BUFF_SIZE];
        int len = strlen(str);
        sprintf(header, http_res_tmpl, len,"text/html");
        len = sprintf(body,"%s%s", header, str);//把 header与str合并产生新字符串body,str指传入的字符数据(即html文件)
    //len为返回写入的字符总个数,不包括结尾自动添加的空字符 send(sock_client, body, len,
    0);   //cock_client为被发送到的套接字符,body为发送数据的缓存区, } void handle_signal(int sign) { fputs(" SIGNAL INTERRUPT Bye Cleey! SAFE EXIT ",stdout); close(sockfd); exit(0); } int read_file(char *filename, int *len, char **data) { int file = open(filename, O_RDONLY); if ( file == -1 ) return -1; int i = 0; while ( 1 ) { *data = realloc(*data, (BUFF_SIZE * (i + 1))); if ( data == NULL ) { close( file ); return -1; } int cur_len = read(file, *data+(BUFF_SIZE * i), BUFF_SIZE);
    //读取BUFF_SIZE个字节到中间参数所指向的内存中去
    if ( cur_len == 0 )//返回值为0,代表数据读完了, break; else *len += cur_len; i++; } close( file ); return 0; } int main(void) { signal(SIGINT,handle_signal);//信号处理,SIGINT代表信号由InterrupptKey产生(按下CTRL+C或DELETE), //handle_signal代表信号处理函数(SIG_DFL指系统默认处理函数,即不做操作),SIG_ING代表忽略SIGINT信号,结束程序按CTRL+
    int len = 0; 
    char *pdata = NULL;
    int count = 0; // 计数
    // 定义 socket
    sockfd
    = socket(AF_INET,SOCK_STREAM,0);
    // 定义 sockaddr_in
    struct sockaddr_in skaddr; //定义存放ip地址的结构体
    skaddr.sin_family
    = AF_INET; // ipv4
    skaddr.sin_port
    = htons(PORT); //设置端口,PORT即为宏定义的8888
    skaddr.sin_addr.s_addr
    = inet_addr(SERVER);//设置地址,0.0.0.0指网络中所有地址
    int on=1;//在绑定前运行端口复用,减少等待 setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); // bind,绑定 socket 和 sockaddr_in if (bind(sockfd,(struct sockaddr *)&skaddr,sizeof(skaddr)) == -1 ) { perror("bind error"); exit(1); } // listen,开始添加端口 if (listen(sockfd, BACKLOG) == -1 ) { perror("listen error"); exit(1); } // 客户端信息 char buff[BUFF_SIZE]; struct sockaddr_in claddr; socklen_t length = sizeof(claddr);//socklen_t为int 类型转换名称 while(1) { int sock_client = accept(sockfd,(struct sockaddr *)&claddr, &length); //accept函数返回新的套接字(即客户端的套接字),参数中的sockfd是服务器的套接字,claddr为客户端的ip地址和端口号,
    //accept会产生阻塞(不往下执行),直到有新的请求
    if (sock_client <0) { perror("accept error"); exit(1); } memset(buff,0,sizeof(buff)); int len = recv(sock_client, buff, sizeof(buff), 0); //不论服务器或客户端,都使用recv从TCP连接的另一端接收数据,sock_client为接收端来源的套接字,
    //buff为接收数据的缓冲区,后面为缓冲区的最大尺寸
    if (http_str_nmatch(buff, "GET /", 5) == 0) { read_file(LED_ON, &len, &pdata); http_send(sock_client, pdata); } else if (http_str_nmatch(buff, "POST /on", 8) == 0) { read_file(LED_ON, &len, &pdata); http_send(sock_client, pdata); printf("led on "); } else if (http_str_nmatch(buff, "POST /off", 8) == 0) { read_file(LED_OFF, &len, &pdata); http_send(sock_client, pdata); printf("led off "); } else { http_send(sock_client,"Hello World!"); } // fputs(buff,stdout); close(sock_client); } fputs("Bye Cleey",stdout); close(sockfd); return 0; }
  • 相关阅读:
    API从网站中解放出来,也许会带来web3.0
    ASP.NET中MD5和SHA1加密的几种方法
    搜狐博客推出开放平台 1月3日举办开发者论坛
    Open Source PDF Libraries in C#
    .Net线程常见问题和误解解答集锦
    降低车辆油耗的十大不变法门
    知己知彼,百战不殆管理软件这个行业
    求职指南:英文求职简历十大忌讳
    .net中实现运行时从字符串动态创建对象
    用 .NET 开发的轻量级 UI 测试自动化.NET教程,.NET Framework
  • 原文地址:https://www.cnblogs.com/wddx5/p/13207209.html
Copyright © 2020-2023  润新知