Socket编程中关于服务器端监听端口与新连接端口的深入剖析

Socket编程中关于服务器端监听端口与新连接端口的深入剖析

在Socket编程领域,存在一个容易让初学者感到困惑的问题。尽管很多人在网络上进行了相关探讨,但不少解释要么不够清晰明了,要么太过肤浅,未能深入到问题的核心,这使得初学者在理解上存在诸多障碍。

其中一个关键问题是:当Socket的服务端监听一个固定端口(例如8888),客户端前来连接此端口后,服务器会生成一个新的Socket与对应的客户端进行通讯。那么,这个新的Socket的发送和接收端口究竟是怎样的呢?是随机分配的,还是依然为8888?在此,明确地告诉读者,答案是8888。即所有由该监听产生的回应客户端的新Socket,其收发数据所使用的端口都是8888。后续将通过代码实现以及网络命令的展示来进一步验证这一点。

代码实现

服务端代码

#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")

#define DEFAULT_PORT "8888"
#define DEFAULT_BUFLEN 1024

int main() {
    // 1. 初始化 Winsock
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult!= 0) {
        std::cerr << "WSAStartup failed: " << iResult << std::endl;
        return 1;
    }
    // 2. 创建套接字
    SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ListenSocket == INVALID_SOCKET) {
        std::cerr << "Socket creation failed: " << WSAGetLastError() << std::endl;
        WSACleanup();
        return 1;
    }
    // 3. 绑定套接字到本地地址和端口
    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(atoi(DEFAULT_PORT));
    iResult = bind(ListenSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));
    if (iResult == SOCKET_ERROR) {
        std::cerr << "Bind failed: " << WSAGetLastError() << std::endl;
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    // 4. 监听连接请求
    iResult = listen(ListenSocket, SOMAXCONN);
    if (iResult == SOCKET_ERROR) {
        std::cerr << "Listen failed: " << WSAGetLastError() << std::endl;
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    std::cout << "Server is listening on port " << DEFAULT_PORT << "..." << std::endl;
    do {
        // 5. 接受客户端连接
        SOCKET ClientSocket = accept(ListenSocket, NULL, NULL);
        if (ClientSocket == INVALID_SOCKET) {
            std::cerr << "Accept failed: " << WSAGetLastError() << std::endl;
            closesocket(ListenSocket);
            WSACleanup();
            return 1;
        }
        // 6. 接收和发送数据
        char recvbuf[DEFAULT_BUFLEN];
        int recvbuflen = DEFAULT_BUFLEN;
        iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0) {
            recvbuf[iResult] = '\0';
            std::cout << "Received from client: " << recvbuf << std::endl;
            const char* sendbuf = "Hello from server!";
            iResult = send(ClientSocket, sendbuf, strlen(sendbuf), 0);
            if (iResult == SOCKET_ERROR) {
                std::cerr << "Send failed: " << WSAGetLastError() << std::endl;
            }
        } else if (iResult == 0) {
            std::cout << "Connection closing..." << std::endl;
        } else {
            std::cerr << "Recv failed: " << WSAGetLastError() << std::endl;
        }
    } while (1);
    // 7. 关闭套接字
    //closesocket(ClientSocket);
    closesocket(ListenSocket);
    WSACleanup();
    return 0;
}

这段服务端代码首先进行Winsock的初始化,接着创建套接字、绑定到指定端口(8888)、开始监听连接请求。在接受客户端连接后,能够接收客户端发送的数据,并向客户端发送响应信息。

客户端代码

#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")

#define DEFAULT_PORT "8888"
#define DEFAULT_BUFLEN 1024
#define SERVER_ADDR "127.0.0.1"

int main() {
    // 1. 初始化 Winsock
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult!= 0) {
        std::cerr << "WSAStartup failed: " << iResult << std::endl;
        return 1;
    }
    // 2. 创建套接字
    SOCKET ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ConnectSocket == INVALID_SOCKET) {
        std::cerr << "Socket creation failed: " << WSAGetLastError() << std::endl;
        WSACleanup();
        return 1;
    }
    // 3. 配置服务器地址结构体
    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
    serverAddr.sin_port = htons(atoi(DEFAULT_PORT));
    // 4. 连接服务器
    iResult = connect(ConnectSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));
    if (iResult == SOCKET_ERROR) {
        std::cerr << "Connect failed: " << WSAGetLastError() << std::endl;
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }
    std::cout << "Connected to server " << SERVER_ADDR << ":" << DEFAULT_PORT << std::endl;
    // 5. 发送数据
    const char* sendbuf = "Hello from client!";
    iResult = send(ConnectSocket, sendbuf, strlen(sendbuf), 0);
    if (iResult == SOCKET_ERROR) {
        std::cerr << "Send failed: " << WSAGetLastError() << std::endl;
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }
    // 6. 接收服务器响应
    char recvbuf[DEFAULT_BUFLEN];
    int recvbuflen = DEFAULT_BUFLEN;
    iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
    if (iResult > 0) {
        recvbuf[iResult] = '\0';
        std::cout << "Received from server: " << recvbuf << std::endl;
    } else if (iResult == 0) {
        std::cout << "Connection closing..." << std::endl;
    } else {
        std::cerr << "Recv failed: " << WSAGetLastError() << std::endl;
    }
    getchar();
    // 7. 关闭套接字
    closesocket(ConnectSocket);
    WSACleanup();
    return 0;
}

客户端代码同样先进行Winsock的初始化,创建套接字后,配置服务器地址并尝试连接到服务器(地址为127.0.0.1,端口8888)。连接成功后,发送数据给服务器并接收服务器的响应。

网络命令验证

使用Windows下的VC++对上述代码进行编译链接,生成socket_server.exesocket_client.exe程序。

  1. 首先运行socket_server.exe程序,此时网络监听情况如下,服务进程PID:18076
C:\Users\hss>netstat -ano | findstr :8888
TCP    0.0.0.0:8888           0.0.0.0:0              LISTENING       18076

可以看到服务端在8888端口处于监听状态,进程ID为18076。

  1. 接着分别运行2个客户端socket_client.exe程序,网络监听和连接情况如下:
C:\Users\hss>netstat -ano | findstr :8888
TCP    0.0.0.0:8888           0.0.0.0:0              LISTENING       18076
TCP    127.0.0.1:8888         127.0.0.1:35582        ESTABLISHED     18076
TCP    127.0.0.1:8888         127.0.0.1:35634        ESTABLISHED     18076
TCP    127.0.0.1:35582        127.0.0.1:8888         ESTABLISHED     5896
TCP    127.0.0.1:35634        127.0.0.1:8888         ESTABLISHED     22044

从上述结果可以清晰地看出,服务器进程(ID为18076)有1个监听的8888端口和2个与客户端建立的TCP连接,且这些连接的本地收发端口都是8888。而客户端有2个进程(ID分别为8896和22044),它们的端口是随机分配的(分别是35582和35634)。

通过以上的代码实现和网络命令验证,足以证明服务器监听返回的新的Socket链接的收发数据端口都是监听端口8888。这对于深入理解Socket编程中的端口使用机制具有重要意义,能够帮助初学者更加准确地把握网络编程中这一关键知识点,从而在后续的编程实践中避免因端口概念不清而产生的错误和困惑,提升编程的准确性和效率,为进一步深入学习网络编程奠定坚实的基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/943293.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

idea 8年使用整理

文章目录 前言idea 8年使用整理1. 覆盖application配置2. 启动的时候设置编辑空间大小&#xff0c;并忽略最大空间3. 查询类的关系4. 查看这个方法的引用关系5. 查看方法的调用关系5.1. 查看被调用关系5.2. 查看调用关系 6. 方法分隔线7. 选择快捷键类型8. 代码预览插件9. JReb…

RAGFLOW使用笔记【更新ing】

0.引言 本文记录使用RAGFLOW的一些问题以及解决办法&#xff0c;它以笔记的形式存在&#xff0c;方便我以后回顾自己的学习工作。 1.RAGFLOW上传文件大小默认是128M,如何修改上传文件大小&#xff1f; 更新ragflow/docker/.env中的MAX_CONTENT_LENGTH 环境变量 然后同步更新…

ubuntu22.04安装PaddleX3

PaddleOCR 安装过程可以参考PaddleX本地安装教程 我的电脑环境配置&#xff1a; ubuntu22.04 cuda11.8&#xff08;之前安装的是12.4没有匹配的paddle-gpu;这里改成11.8&#xff09; 一、安装基础环境 1、 conda create -n ppx1 python3.10 2、 conda activate ppx1 3、…

Android 之 Activity 的启动模式(launchMode)

一、Activity 启动模式 在实际项目中&#xff0c;应该根据项目的实际需要来为每个 Activity 指定恰当的启动模式 launchMode。启动模式一共有四种&#xff0c;分别是 standard、singleTop、singleTask 和 singleInstance。可以在 AndroidManifest.xml 中通过给 <activity&g…

软件老化分析

软件老化 课程&#xff1a;软件质量分析 作业 解答 Python代码如下&#xff1a; n int(input("类别数&#xff1a;")) theta list(map(float, input("各个类别的权重&#xff1a;").split())) m list(map(int, input("各个类别的度量元数量&…

Jenkins 构建流水线

在 Linux 系统上安装 Jenkins 服务&#xff0c;以及配置自动化构建项目 前置准备环境&#xff1a;docker、docker-compose、jdk、maven 一、环境搭建 1. Jenkins 安装 &#xff08;1&#xff09;拉取镜像 # 安装镜像包&#xff0c;默认安装最新版本 docker pull jenkins/jen…

5G学习笔记之Non-Public Network

目录 0. NPN系列 1. 概述 2. SNPN 2.1 SNPN概述 2.2 SNPN架构 2.3 SNPN部署 2.3.1 完全独立 2.3.2 共享PLMN基站 2.3.3 共享PLMN基站和PLMN频谱 3. PNI-NPN 3.1 PNI-NPN概述 3.2 PNI-NPN部署 3.2.1 UPF独立 3.2.2 完全共享 0. NPN系列 1. NPN概述 2. NPN R18 3. 【SNPN系列】S…

若依plus apifox导入接口显示为空

项目已经正常启动 访问接口有些没问题&#xff0c;有些有问题 其他模块都可以正常导入 解决&#xff1a;

elementPlus消息组件多按钮案例

let customClass zsl-el-message-box efb.messageBox({title: 操作提示,showConfirmButton: false,customClass,message: efb.VNode(div, null, [efb.VNode(style, null, .${customClass} .el-message-box__message {width: 100%;}),efb.VNode(div, null, hello world),efb.VN…

计算机网络:应用层 —— 网络应用模式

文章目录 客户—服务器方式和对等方式客户/服务器方式 (C/S方式)工作流程特点 对等方式 (P2P方式)工作流程P2P 应用特点 客户—服务器方式和对等方式 网络应用程序运行在处于网络边缘的不同的端系统上&#xff0c;通过彼此间的通信来共同完成某项任务。 开发一种新的网络应用…

科技创新 数智未来|清科·沙丘投研院走进竹云

12月20日&#xff0c;清科沙丘投研院带领企投家团队走进竹云交流分享&#xff0c;聚焦技术创新、企业数字化管理、行业前沿应用案例等热点议题&#xff0c;深入探讨数字技术如何点燃企业高质量发展的澎湃动力&#xff0c;共话企业数字化、智能化发展之道。 达晨财智股权管理部…

外连接转AntiJoin的应用场景与限制条件 | OceanBase SQL 查询改写系列

在《SQL 改写系列&#xff1a;外连接转内连接的常见场景与错误》一文中&#xff0c;我们了解到谓词条件可以过滤掉连接结果中的 null 情形的&#xff0c;将外连接转化为内连接的做法是可行的&#xff0c;正如图1中路径(a)所示。此时&#xff0c;敏锐的你或许会进一步思考&#…

Java - 日志体系_Apache Commons Logging(JCL)日志接口库

文章目录 官网1. 什么是JCL&#xff1f;2. JCL的主要特点3. JCL的核心组件4. JCL的实现机制5. SimpleLog 简介6. CodeExample 1 &#xff1a; 默认日志实现 (JCL 1.3.2版本)Example 2 &#xff1a; JCL (1.2版本&#xff09; Log4J 【安全风险高&#xff0c;请勿使用】 7. 使用…

mavlink移植到单片机stm32f103c8t6,实现接收和发送数据

前言&#xff1a; 好久没更新博客了&#xff0c;这两个月真的是异常的忙&#xff0c;白天要忙着公司里的事&#xff0c;晚上还要忙着修改小论文&#xff0c;一点自己的时间都没有了&#xff0c;不过确确实实是学到了很多东西&#xff0c;对无人机的技术研究也更深了一些。不过好…

GitLab的安装与卸载

目录 GitLab安装 GitLab使用 使用前可选操作 修改web端口 修改Prometheus端口 使用方法 GitLab的卸载 环境说明 系统版本 CentOS 7.2 x86_64 软件版本 gitlab-ce-10.8.4 GitLab安装 Gitlab的rpm包集成了它需要的软件&#xff0c;简化了安装步骤&#xff0c;所以直接…

子网掩码计算route命令

子网掩码 - 站长工具 1.子网掩码 子网掩码就是用来遮掩IP地址并划分网段的工具&#xff0c;根据遮掩的位数不同来划分不同的网段。 2.网关 网关(Gateway)又称网间连接器、协议转换器。默认网关在网络层上以实现网络互连&#xff0c;是最复杂的网络互连设备&#xff0c;仅用…

基本操作:iframe、alert

背景 如果你的目标元素出现在一个iframe标签下&#xff0c;则不能直接定位&#xff0c;必须先完成切换才能进行定位操作&#xff0c;如下图 整个理解为一个大的房间&#xff0c;里面是客厅&#xff0c;driver进到客厅后&#xff0c;如果想操作iframe A里的数据&#xff0c;需…

【再学javascript算法之美】前端面试频率比较高的基础算法题

基础算法题练习代码&#xff0c;看看能做出几道题 代码实现 找出字符串中出现次数最多的字符 const array "cncnansdnajsadnjasndjnasjdnjj";// 找出出现次数最多的字符 let obj {}; for (let index 0; index < array.length; index) {const element array[…

flask后端开发(8):Flask连接MySQL数据库+ORM增删改查

目录 数据库初始化数据库连接创建数据库表添加用户查询用户更新用户删除 在Flask中&#xff0c;很少会使用pymysql直接写原生SQL语句去操作数据库&#xff0c;更多的是通过SQLAichemy提供的ORM技术&#xff0c;类似于操作普通Python对象一样实现数据库的增删改查操作&#xff0…

视频会议是如何实现屏幕标注功能的?

现在主流的视频会议软件都有屏幕标注功能&#xff0c;屏幕标注功能给屏幕分享者讲解分享内容时提供了极大的方便。那我们以傲瑞视频会议&#xff08;OrayMeeting&#xff09;为例&#xff0c;来讲解屏幕标注是如何实现的。 傲瑞会议的PC端&#xff08;Windows、信创Linux、银河…