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

文章目录

    • 流程伪代码
    • 代码实现
      • 加载库
      • 创建套接字
      • 绑定ip地址和端口号
      • 监听
      • 接受连接
      • 收发数据
      • 关闭套接字、卸载库

【计算机网络】 基于TCP的简单通讯(服务端)_第1张图片

流程伪代码

//1、加载库——WSAStartup()

//2、创建套接字——socket()

//3、绑定ip和端口号——bind()

//4、监听——listen()

while(true){
    //5、接受连接——accept()
    
    while(true){
        //6、接收数据——recv()
        
        //7、发送数据——send()
    }
}

//8、关闭套接字、卸载库——closesocket()、WSACleanup()

代码实现

加载库

加载库和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;
	}

创建套接字

思路和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;
	}

绑定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;
	}

监听

监听我们用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;
	}

接受连接

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

	sockaddr_in addrClient;
	int addrClientSize = sizeof(addrClient);
    //接受连接
    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;
    }

收发数据

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

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

	int nRecvNum = 0;
	char recvBuf[1024] = "";
    nRecvNum = recv(sockTalk, recvBuf, sizeof(recvBuf), 0);
    if (nRecvNum > 0) {
        cout << "Client say:" << recvBuf << endl;
    }
    else {
        cout << "recv error:" << WSAGetLastError() << endl;
        break;
    }

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

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

关闭套接字、卸载库

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

	closesocket(sockTalk);

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

	closesocket(sock);
	WSACleanup();

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

你可能感兴趣的:(计算机网络(网络编程),计算机网络,tcp/ip,网络协议)