130-网络编程:端口复用

4、端口复用

fgets(sendBuf, sizeof(sendBuf), stdin);
	-默认是阻塞的
#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main() {

    // 创建socket
    int fd = socket(PF_INET, SOCK_STREAM, 0);
    if(fd == -1) {
        perror("socket");
        return -1;
    }

    struct sockaddr_in seraddr;
    inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(9999);

    // 连接服务器
    int ret = connect(fd, (struct sockaddr *)&seraddr, sizeof(seraddr));

    if(ret == -1){
        perror("connect");
        return -1;
    }

    while(1) {
        char sendBuf[1024] = {0};
        fgets(sendBuf, sizeof(sendBuf), stdin);

        write(fd, sendBuf, strlen(sendBuf) + 1);

        // 接收
        int len = read(fd, sendBuf, sizeof(sendBuf));
        if(len == -1) {
            perror("read");
            return -1;
        }else if(len > 0) {
            printf("read buf = %s\n", sendBuf);
        } else {
            printf("服务器已经断开连接...\n");
            break;
        }
    }

    close(fd);

    return 0;
}

#include <stdio.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {

    // 创建socket
    int lfd = socket(PF_INET, SOCK_STREAM, 0);

    if(lfd == -1) {
        perror("socket");
        return -1;
    }

    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = INADDR_ANY;
    saddr.sin_port = htons(9999);
    
    //int optval = 1;
    //setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

    int optval = 1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));

    // 绑定
    int ret = bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
    if(ret == -1) {
        perror("bind");
        return -1;
    }

    // 监听
    ret = listen(lfd, 8);
    if(ret == -1) {
        perror("listen");
        return -1;
    }

    // 接收客户端连接
    struct sockaddr_in cliaddr;
    socklen_t len = sizeof(cliaddr);
    int cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);
    if(cfd == -1) {
        perror("accpet");
        return -1;
    }

    // 获取客户端信息
    char cliIp[16];
    inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, cliIp, sizeof(cliIp));
    unsigned short cliPort = ntohs(cliaddr.sin_port);

    // 输出客户端的信息
    printf("client's ip is %s, and port is %d\n", cliIp, cliPort );

    // 接收客户端发来的数据
    char recvBuf[1024] = {0};
    while(1) {
        int len = recv(cfd, recvBuf, sizeof(recvBuf), 0);
        if(len == -1) {
            perror("recv");
            return -1;
        } else if(len == 0) {
            printf("客户端已经断开连接...\n");
            break;
        } else if(len > 0) {
            printf("read buf = %s\n", recvBuf);
        }

        // 小写转大写
        for(int i = 0; i < len; ++i) {
            recvBuf[i] = toupper(recvBuf[i]);
        }

        printf("after buf = %s\n", recvBuf);

        // 大写字符串发给客户端
        ret = send(cfd, recvBuf, strlen(recvBuf) + 1, 0);
        if(ret == -1) {
            perror("send");
            return -1;
        }
    }
    
    close(cfd);
    close(lfd);

    return 0;
}

先启动服务端:

再启动客户端:

查看网络相关信息的命令:
    netstat
    	参数:
			-a 所有的socket
            -p 显示正在使用socket的程序的名称
			-n 直接使用IP地址,而不通过域名服务器

查看9999端口:

先启动服务端;

服务器的ip地址是0,调用了listen函数,处于监听状态;

再启动客户端:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qm9haulh-1651469422266)(../../../assets/网络编程/image-20220122191856442.png)]

对于服务器端,有两个socket,一个是用于监听的套接字(第1个),还有个是用于通信的套接字(第3个);

客户端和服务端在通信的过程中, 状态是不会发生改变的;

现在,主动断开服务器端:

此时的状态:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LawnbhKI-1651469422267)(../../../assets/网络编程/image-20220122192519683.png)]

此时是服务端主动断开,客户端没有主动发起关闭,此时服务端处于FIN_WAIT_2状态

客户端处于close_wait状态:

过一会后,只剩下客户端了,服务端消失了:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iiwxvWlE-1651469422268)(../../../assets/网络编程/image-20220122192912257.png)]

重点:

先启动服务端,再启动客户端;

状态都是正常的:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BrakIjkX-1651469422269)(../../../assets/网络编程/image-20220122193816498.png)]

断开服务端:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1IXoYrLT-1651469422270)(../../../assets/网络编程/image-20220122193833735.png)]

立即运行服务端,不成功

此时服务端的状态是在FIN_WAIT_2上,客户端处于CLOSED_WAIT状态;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MDxGl5mv-1651469422271)(../../../assets/网络编程/image-20220122194023803.png)]

处于TIME_WAIT状态,会有2MSL时间,在这期间,服务器会一直占用9999端口;过了2MSL时间后,进程会真正的结束掉。

端口复用最常用的用途是:

服务器有一个2MLS的时间,端口没有释放,不能进行使用

  • 防止服务器重启时之前绑定的端口还未释放
  • 程序突然退出而系统没有释放端口
#include <sys/types.h>
#include <sys/socket.h>
// 设置套接字的属性(不仅仅能设置端口复用)		--参考unix网络编程这本书(第7章)
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t
				optlen);
    参数:
    	- sockfd : 要操作的文件描述符
   		- level : 级别 - SOL_SOCKET (端口复用的级别)
    	- optname : 选项的名称
   			- SO_REUSEADDR	//允许重用本地地址
    		- SO_REUSEPORT	//允许重用本地端口
    	- optval : 端口复用的值(整形)
    		- 1 : 可以复用
    		- 0 : 不可以复用
   		- optlen : optval参数的大小
    
//端口复用,设置的时机是在服务器绑定端口之前。
    setsockopt();
    bind();
#include <arpa/inet.h>	--这个会包含setsockopt的头文件

	int optval = 1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

    int optval = 1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));

此时,服务端断开连接后,可以继续连接,端口复用。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liufeng2023

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值