• 【计算机网络】 基于TCP的简单通讯(服务端)



    在这里插入图片描述

    流程伪代码

    //1、加载库——WSAStartup()
    
    //2、创建套接字——socket()
    
    //3、绑定ip和端口号——bind()
    
    //4、监听——listen()
    
    while(true){
        //5、接受连接——accept()
        
        while(true){
            //6、接收数据——recv()
            
            //7、发送数据——send()
        }
    }
    
    //8、关闭套接字、卸载库——closesocket()、WSACleanup()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    代码实现

    加载库

    加载库和UDP一样,不用过多解释

    	int err = 0;
    	WORD version = MAKEWORD(2, 2);
    	WSADATA wsaData;
    	err = WSAStartup(version, &wsaData);
    	if (0 != err) {
    		cout << "WSAStartup error" << WSAGetLastError() << endl;
    		return 1;
    	}
    	if (2 != HIBYTE(wsaData.wVersion) || 2 != LOBYTE(wsaData.wVersion)) {
    		cout << "WSAStartup version error" << endl;
    		WSACleanup();
    		return 1;
    	}
    	else {
    		cout << "WSAStart success" << endl;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    创建套接字

    思路和UDP也相同,只不过参数有所变化

    	SOCKET sock = socket(AF_INET,SOCK_STREAM , IPPROTO_TCP);
    	if (INVALID_SOCKET == sock) {
    		cout << "socket error" << WSAGetLastError() << endl;
    		WSACleanup();
    		return 1;
    	}
    	else {
    		cout << "socket success" << endl;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    绑定ip地址和端口号

    绑定也和UDP中的一样

    	sockaddr_in addrServer;
    	addrServer.sin_family = AF_INET;
    	addrServer.sin_port = htons(456789);
    	addrServer.sin_addr.S_un.S_addr = INADDR_ANY;
    	err = bind(sock, (sockaddr*)&addrServer, sizeof(addrServer));
    	if (SOCKET_ERROR == err) {
    		cout << "bind error:" << WSAGetLastError() << endl;
    		closesocket(sock);
    		WSACleanup();
    		return 1;
    	}
    	else {
    		cout << "bind success" << endl;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    监听

    监听我们用listen()函数进行,他有两个参数,第一个为socket,意为派哪个socket去进行监听,第二个参数为int值,它是能够等待被连接的队列的最大长度,返回值如果是0就没有问题,为SOCKET_ERROR就报错

    	err = listen(sock, 10);
    	if (SOCKET_ERROR == err) {
    		cout << "listen error:" << WSAGetLastError() << endl;
    		closesocket(sock);
    		WSACleanup();
    		return 1;
    	}
    	else {
    		cout << "listen success" << endl;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    接受连接

    接受连接我们使用accept()函数,它的返回值为SOCKET,当创建连接成功时会返回一个新的socket,因为之前我们创建的socket去进行监听了,所以我们需要再创建一个socket来接这个返回值。它的第一个参数为socket,就是我们用来进行监听的那个socket,进行监听的这个socket就相当于酒店迎宾的,而返回的socket就相当于是服务员。第二个参数为sockaddr*类型的输出参数,它用来装对端的地址信息,第三个参数就是这个输出参数的长度。

    	sockaddr_in addrClient;
    	int addrClientSize = sizeof(addrClient);
    
    • 1
    • 2
        //接受连接
        SOCKET sockTalk = accept(sock, (sockaddr*)&addrClient, &addrClientSize);
        if (INVALID_SOCKET != sockTalk) {
            //打印客户端的IP地址
            cout << "Client ip:" << inet_ntoa(addrClient.sin_addr) << endl;
        }
        else {
            cout << "accept error:" << WSAGetLastError() << endl;
            break;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    收发数据

    当连接成功之后就可以进行收发数据了

    接收数据我们使用recv()函数,这个函数相比recvfrom()少了两个参数,它不需要记录对端的地址信息了,因为已经跟对端连接成功了,已经知道对端的地址信息了,连接成功返回的socket就像是连接在两端的一条绳。只能给连接成功的两端使用,如果别人想使用,那么需要跟另外一端重新建立连接,也就是说想跟谁通信就用跟谁连接产生的socket

    	int nRecvNum = 0;
    	char recvBuf[1024] = "";
    
    • 1
    • 2
        nRecvNum = recv(sockTalk, recvBuf, sizeof(recvBuf), 0);
        if (nRecvNum > 0) {
            cout << "Client say:" << recvBuf << endl;
        }
        else {
            cout << "recv error:" << WSAGetLastError() << endl;
            break;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    发送数据我们使用send()函数,原理和recv()函数相同

    	int nSendNum = 0;
    	char sendBuf[1024] = "";
    
    • 1
    • 2
        gets_s(sendBuf);
        nSendNum = send(sockTalk, sendBuf, sizeof(sendBuf), 0);
        if (SOCKET_ERROR == nSendNum) {
            cout << "send error:" << WSAGetLastError() << endl;
            break;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    关闭套接字、卸载库

    当我们结束与这个客户端聊天时,我们应该关闭与这个客户端连接时产生的套接字(外层循环里面进行)

    	closesocket(sockTalk);
    
    • 1

    最终关闭监听用的sock然后卸载库

    	closesocket(sock);
    	WSACleanup();
    
    • 1
    • 2

    这样我们的服务端就写完了

  • 相关阅读:
    resvr.exe 电脑病毒清理
    FTP响应代码全面解析:逐个代码详解与应用场景
    原来 GitHub 不仅能学代码,还有这些东西
    使用构建缓存优化 Docker 镜像构建
    SSM-SpringBoot D1-掌握boot框架的开发步骤、修改配置服务器信息、整合juint、mybatis完成基于boot的SSM整合项目开发
    CMAK学习
    流程引擎的架构设计
    JVM调优笔记(一)--Nacos GC引发的服务批量下线问题
    通信用多模光纤主要有哪些类型?OM1~OM5有什么区别
    Java 中的线程池
  • 原文地址:https://blog.csdn.net/jia_03/article/details/133365646