IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 【手把手教你写服务器】TCP状态转换、TIME_WAIT状态、SO_REUSEADDR选项 -> 正文阅读

[系统运维]【手把手教你写服务器】TCP状态转换、TIME_WAIT状态、SO_REUSEADDR选项

1.TCP状态转换

关于TCP状态转换,自行查阅《UNIX网络编程_卷1_套接字联网API_第3版》第二章第六节。

在这里插入图片描述

2.TIME_WAIT状态

关于TIME_WAIT状态,自行查阅《UNIX网络编程_卷1_套接字联网API_第3版》第二章第七节。

server.c:

#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define SERV_PORT 9000 // 本服务器要监听的端口号,一般1024以下的端口很多都是周知端口,所以这里采用1024之后的数字做端口号

int main(int argc, char* const* argv)
{
    int listenfd = socket(AF_INET, SOCK_STREAM, 0); // 创建服务器的socket套接字(文件描述符)

    struct sockaddr_in serv_addr; // 服务器的地址结构体
    memset(&serv_addr, 0, sizeof(serv_addr));
    
    // 设置本服务器要监听的地址和端口,这样客户端才能连接到该地址和端口并发送数据
    serv_addr.sin_family = AF_INET; // 选择协议族为IPV4
    serv_addr.sin_port = htons(SERV_PORT); // 绑定我们自定义的端口号,客户端程序和服务器程序通讯时,就要往这个端口连接和传送数据
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听本地所有的IP地址,INADDR_ANY表示一个服务器上所有的网卡(服务器可能不止一个网卡),多个本地ip地址都进行绑定端口号,进行侦听

    int result;

    result = bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); // 绑定服务器地址结构体
    
    if (result  == -1)
    {
        char* perrorinfo = strerror(errno); 
        printf("bind返回的值为%d,错误码为:%d,错误信息为:%s;\n", result, errno, perrorinfo);
        return -1;
    }

    result = listen(listenfd, 32); // 第二个参数表示服务器可以积压的未处理完的连入请求总个数,客户端来一个未连入的请求,请求数+1,连入请求完成、c/s之间进入正常通讯后,请求数-1
    
    if (result == -1)
    {
        char* perrorinfo = strerror(errno); 
        printf("listen返回的值为%d,错误码为:%d,错误信息为:%s;\n", result, errno, perrorinfo);
        return -1;
    }

    int connfd;
    
    const char* pcontent = "I sent sth to client!\n"; // 指向常量字符串区的指针
    
    for (;;)
    {
        // 卡在这里,等客户端连接,客户端连入后,该函数走下去
        // 注意这里返回的是一个新的socket(connfd),后续本服务器就用connfd和客户端之间收发数据,而原有的lisenfd依旧用于继续监听其他连接        
        connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);

        // 发送数据包给客户端
        write(connfd, pcontent, strlen(pcontent)); // 注意第一个参数是accept()返回的connfd套接字
        
        printf("本服务器给客户端发送了一串字符~~~~~~~~~~~!\n");
        
        // 只给客户端发送一个信息,然后直接关闭套接字连接
        close(connfd);
    }
    
    close(listenfd); // 实际上本范例走不到这里

    return 0;
}

运行服务器程序,用 netstat -anp | grep -E 'State|9000' 命令观察到监听端口一直处在 LISTEN 状态,我们用两个客户端连接到服务器,服务器给每个客户端发送一串字符 “I sent sth to client!\n” 并关闭客户端,虽然这两个连接被 close 掉了,但是产生了两条 TIME_WAIT 状态的信息。

在这里插入图片描述

此时,我们杀掉服务器程序再重新启动,就会启动失败,bind() 函数返回失败。

在这里插入图片描述

3.SO_REUSEADDR选项

server.c:

#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define SERV_PORT 9000 // 本服务器要监听的端口号,一般1024以下的端口很多都是周知端口,所以这里采用1024之后的数字做端口号

int main(int argc, char* const* argv)
{
    int listenfd = socket(AF_INET, SOCK_STREAM, 0); // 创建服务器的socket套接字(文件描述符)

    struct sockaddr_in serv_addr; // 服务器的地址结构体

    memset(&serv_addr, 0, sizeof(serv_addr));
    
    // 设置本服务器要监听的地址和端口,这样客户端才能连接到该地址和端口并发送数据
    serv_addr.sin_family = AF_INET; // 选择协议族为IPV4
    serv_addr.sin_port = htons(SERV_PORT); // 绑定我们自定义的端口号,客户端程序和服务器程序通讯时,就要往这个端口连接和传送数据
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听本地所有的IP地址,INADDR_ANY表示一个服务器上所有的网卡(服务器可能不止一个网卡),多个本地ip地址都进行绑定端口号,进行侦听

    // setsockopt():设置一些套接字参数选项
    // 第二个参数:表示级别,和第三个参数配套使用,也就是说,如果第三个参数确定了,第二个参数也就确定了
    // 第三个参数:允许重用本地地址
    int reuseaddr = 1; // 开启
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (const void *) &reuseaddr, sizeof(reuseaddr)) == -1)
    {
        char* perrorinfo = strerror(errno);
        printf("setsockopt(SO_REUSEADDR)返回值为%d,错误码为:%d,错误信息为:%s;\n", -1, errno, perrorinfo);
    }

    int result;

    result = bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); // 绑定服务器地址结构体
    
    if (result  == -1)
    {
        char* perrorinfo = strerror(errno); 
        printf("bind返回的值为%d,错误码为:%d,错误信息为:%s;\n", result, errno, perrorinfo);
        return -1;
    }
    
    result = listen(listenfd, 32); // 第二个参数表示服务器可以积压的未处理完的连入请求总个数,客户端来一个未连入的请求,请求数+1,连入请求完成、c/s之间进入正常通讯后,请求数-1
    
    if (result == -1)
    {
        char* perrorinfo = strerror(errno); 
        printf("listen返回的值为%d,错误码为:%d,错误信息为:%s;\n", result, errno, perrorinfo);
        return -1;
    }

    int connfd;

    const char* pcontent = "I sent sth to client!\n"; // 指向常量字符串区的指针
    
    for (;;)
    {
        // 卡在这里,等客户端连接,客户端连入后,该函数走下去
        // 注意这里返回的是一个新的socket(connfd),后续本服务器就用connfd和客户端之间收发数据,而原有的lisenfd依旧用于继续监听其他连接        
        connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);

        // 发送数据包给客户端
        write(connfd, pcontent, strlen(pcontent)); // 注意第一个参数是accept()返回的connfd套接字

        printf("本服务器给客户端发送了一串字符~~~~~~~~~~~!\n");
        
        // 只给客户端发送一个信息,然后直接关闭套接字连接
        close(connfd);
    }

    close(listenfd); // 实际上本范例走不到这里
    
    return 0;
}

setsockopt(SO_REUSEADDR):用在服务器端,socket() 创建之后,bind() 之前。

SO_REUSEADDR 的能力:

  1. SO_REUSEADDR 允许启动一个监听服务器并捆绑其端口,即使以前建立的将端口用作它们的本地端口的连接仍旧存在。也就是说,即便 TIME_WAIT 状态存在,服务器 bind() 也能成功。
  2. SO_REUSEADDR 允许同一个端口上启动同一个服务器的多个实例,只要每个实例捆绑一个不同的本地 IP 地址即可。
  3. SO_REUSEADDR 允许单个进程捆绑同一个端口到多个套接字,只要每次捆绑指定不同的本地 IP 地址即可。
  4. SO_REUSEADDR 允许完全重复的绑定:当一个 IP 地址和端口已经绑定到某个套接字上时,如果传输协议支持,同样的 IP 地址和端口还可以绑定到另一个套接字上,一般来说本特性仅支持 UDP 套接字。
  5. 所有 TCP 服务器都应该指定本套接字选项,以防止当套接字处于 TIME_WAIT 时 bind() 失败的情形出现。
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 12:39:36  更:2022-10-31 12:44:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 3:50:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计