网络程序设计 实验2 简单的人机网络对话

实验目的
通过流式套接字编程,实现简单的人机网络对话程序。服务端预存对话模板,根据模板对客户端发来的会话进行应答。

实验要求
1.使用控制台界面编程。
2.利用流式套接字编程。
3.客户端发送信息,并显示服务端的应答。客户端发送bye结束会话

4.服务端根据对话模板应答客户端发来的信息,对话模板自己定制。
5.若客户端发来的信息不在对话模板内,则复制客户端的信息进行应答。
6.若客户端发来消息“人类的本质是什么?”,则服务端应答"复读机"。

刚看到实验要求的我:蒋老师怎么这么闷骚啊哈哈哈哈
不过一番思考后,我想这么皮的实验要求大概不是蒋老师写出来的
很有可能是我们的英俊帅气的常俊哥哥写出来的!!
常俊哥哥:


真是比嘉然还要可爱捏~❤

进入正题
很快就把PPT上的代码搬完了

PPT上的代码已经是足够实现复读机的部分了
不过我发现进程总是会自己结束,像这样:

看输出的结果应该是触发了这段代码

查阅了一些资料,得出的结论是:
argc是输入main函数的参数个数,argv是输入main函数的参数
所以程序出的问题应该是:我没有在控制台中输入参数

所以,我需要做的是,用控制台打开文件,同时文件后面输入参数
或者在调试的时候输入参数,其实很简单,参考这篇文章:
https://blog.csdn.net/weixin_43450564/article/details/113178840

在属性中这样修改即可

看代码的情况应该是要在后边输入本地ip
结果出现了这个结果

这说明我的代码已经能实现测试信息的往返了
接下来就可以开始实现复读机功能了

接下来加上了复读功能,但是

strcpy函数报错
查阅资料得知是因为c++鼓励大家用string函数而不是strcpy
添加#pragma warning (disable:4996)即可

再后来没有遇到大的问题,主要是调试问题
1)Visual studio的项目,打开一个就会自动关闭一个,想要同时打开两个,需要先打开其中一个,然后右键解决方案管理器添加另一个
2)调试代码的时候需要同时运行客户端和服务器,所以注意调试选项,改成多线程MT
这些网上查查也都能找到办法

实现效果如图:

服务端代码

点击查看代码
//服务器
#include <WinSock2.h>
#include<ws2tcpip.h>
#include<stdio.h>
#include<iostream>
#include<cstring>
#pragma comment(lib, "ws2_32.lib")
#pragma warning (disable:4996)

using namespace std;

#define DEFAULT_PORT 3000
#define DEFAULT_BUFLEN 512
#define IP_ADDRESS "192.168.153.1"
#define echocount 5

int main() {
	WSADATA wsaData;
	int iResult;
	SOCKET listenSocket = INVALID_SOCKET;
	SOCKADDR_IN localAddr;
	SOCKET clientSocket = INVALID_SOCKET;
	char recvbuf[DEFAULT_BUFLEN];
	int recvbuflen = DEFAULT_BUFLEN;
	int iSendResult;
	char special[echocount][DEFAULT_BUFLEN] =
	{
		"人类的本质是什么?",
		"你好",
		"蒋老师给我过吧",
		"作者是谁",
		"帮助",
	};
	char echo[echocount][DEFAULT_BUFLEN] =
	{
		"复读机",
		"好好好",
		"求求了",
		"第一大师哥",
		"发送‘bye'可以结束对话。\n发送其他消息可以实现复读。\n发送特殊消息能够触发彩蛋。\n",
	};
	char bye[DEFAULT_BUFLEN] = "bye";
	
	//初始化套接字
	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0) {
		printf("WSAStartup执行失败,错误码:%d\n", iResult);
		return 1;
	}
	
	//为面向连接的服务器创建套接字
	listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (listenSocket == INVALID_SOCKET) {
		printf("socket执行失败,错误码:%d\n", WSAGetLastError());
		WSACleanup();
		return 1;
	}

	//配置本体地址信息
	localAddr.sin_family = AF_INET;
	localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	localAddr.sin_port = htons(DEFAULT_PORT);

	//为套接字绑定地址和端口号
	iResult = bind(listenSocket, (LPSOCKADDR)&localAddr, sizeof(localAddr));
	if (iResult == SOCKET_ERROR) {
		printf("bind执行失败,错误码:%d\n", WSAGetLastError());
		closesocket(listenSocket);
		WSACleanup();
		return 1;
	}

	printf("服务器已开启,正在等待客户端操作……\n");
	//监听连接请求
	iResult = listen(listenSocket, SOMAXCONN);
	if (iResult == SOCKET_ERROR) {
		printf("listen执行失败,错误码:%d\n", WSAGetLastError());
		closesocket(listenSocket);
		WSACleanup();
		return 1;
	}

	//接受客户端的连接请求,返回连接套接字
	clientSocket = accept(listenSocket, NULL, NULL);
	if (iResult == INVALID_SOCKET) {
		printf("accept执行失败,错误码:%d\n", WSAGetLastError());
		closesocket(listenSocket);
		WSACleanup();
		return 1;
	}
	//已经不需要监听了,释放监听套接字
	closesocket(listenSocket);
	
	printf("已完成连接。正在等待客户端进一步操作……\n");
	for (;;) {
		//持续接收数据,直到对方关闭连接
		do {
			memset(recvbuf, '\0', DEFAULT_BUFLEN);//清空缓冲区
			iResult = recv(clientSocket, recvbuf, recvbuflen, 0);//接收数据
			if (iResult > 0) {//成功收到数据

				cout << "客户端:" << recvbuf << endl;

				if (strcmp(recvbuf, bye) == 0) {
					printf("收到客户端结束命令,结束连接\n");
					closesocket(listenSocket);
					WSACleanup();
					return 1;
				}//客户端发送bye则结束对话

				for (int i = 0; i < echocount; i++) {
					if (strcmp(special[i], recvbuf) == 0) {
						memset(recvbuf, '\0', DEFAULT_BUFLEN);
						strncpy(recvbuf, echo[i], strlen(echo[i]));
						break;
					}
				}//特殊消息处理

				//将缓冲区的内容送回给客户端
				iSendResult = send(clientSocket, recvbuf, (int)strlen(recvbuf), 0);
				if (iSendResult == SOCKET_ERROR) {
					printf("send执行失败,错误码:%d\n", WSAGetLastError());
					closesocket(listenSocket);
					WSACleanup();
					return 1;
				}
	//			printf("接收字节数:%d\n", iResult);
				cout << recvbuf << endl;
				printf("已经对客户端数据进行回应,发送字节数:%d\n", iSendResult);
			}
			else if (iResult == 0) {
				//连接关闭
//				printf("网络中断,连接关闭……\n");
			}
			else {
				//接收发生错误
				printf("recv执行失败,错误码:%d\n", WSAGetLastError());
				closesocket(listenSocket);
				WSACleanup();
				return 1;
			}
		} while (iResult > 0);
	}

	//关闭连接
	iResult = shutdown(clientSocket, SD_SEND);
	if (iResult == SOCKET_ERROR) {
		printf("shutdown执行失败,错误码:%d\n", WSAGetLastError());
		closesocket(clientSocket);
		WSACleanup();
		return 1;
	}
	//关闭套接字,释放资源
	closesocket(clientSocket);
	WSACleanup();
	return 0;
}


客户端代码

点击查看代码
//客户端
#include <WinSock2.h>
#include<ws2tcpip.h>
#include<stdio.h>
#include<iostream>
#include<cstring>
#pragma comment(lib, "ws2_32.lib")

using namespace std;

#define DEFAULT_PORT 3000
#define DEFAULT_BUFLEN 512
#define IP_ADDRESS "192.168.153.1"

int main(int argc, char** argv) {
	WSADATA wsaData;
	int iResult;
	SOCKET connectSocket = INVALID_SOCKET;
	SOCKADDR_IN serverAddr;
	SOCKET clientSocket = INVALID_SOCKET;
	char sendbuf[DEFAULT_BUFLEN] = "\0";
	char recvbuf[DEFAULT_BUFLEN] = "\0";
	int recvbuflen = DEFAULT_BUFLEN;
	char bye[DEFAULT_BUFLEN] = "bye";

	//验证参数合法性
	if (argc != 2) {
		printf("命令用法:%s 服务器域名或ip\n", argv[0]);
		return 1;
	}

	//初始化套接字
	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (iResult != 0) {
		printf("WSAStartup执行失败,错误码:%d\n", iResult);
		return 1;
		return 1;
	}

	//配置服务器地址信息
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(DEFAULT_PORT);
	iResult = inet_pton(AF_INET, argv[1], &(serverAddr.sin_addr.s_addr));
	if (iResult == SOCKET_ERROR) {
		printf("bind执行失败,错误码:%d\n", WSAGetLastError());
		closesocket(connectSocket);
		WSACleanup();
		return 1;
	}

	//创建套接字
	connectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (connectSocket == INVALID_SOCKET) {
		printf("socket执行失败,错误码:%d\n", WSAGetLastError());
		WSACleanup();
		return 1;
	}

	//向服务器请求连接
	iResult = connect(connectSocket, (LPSOCKADDR)&serverAddr, sizeof(serverAddr));
	if (iResult == SOCKET_ERROR) {
		closesocket(connectSocket);
		printf("无法连接到服务器\n");
		WSACleanup();
		return 1;
	}

	printf("请输入消息:\n");
	for (int i=0;;i++) {
		//发送数据
		memset(sendbuf, '\0', DEFAULT_BUFLEN);//清空缓冲区字符串
		cin.get(sendbuf, DEFAULT_BUFLEN);//输入消息
		cin.ignore();

//		printf("运行到这里了\n");

		memset(recvbuf, '\0', DEFAULT_BUFLEN);
		iResult = send(connectSocket, sendbuf, (int)strlen(sendbuf), 0);
		if (iResult == SOCKET_ERROR) {
			printf("发送失败,错误码:%d\n", WSAGetLastError());
			closesocket(connectSocket);
			WSACleanup();
			return 1;
		}//错误处理
		
		if (strcmp(sendbuf, bye) == 0) {
			printf("正在为您关闭会话……\n");
			closesocket(connectSocket);
			WSACleanup();
			return 1;
		}

		//printf("发送成功!发送字节数:%ld\n", iResult);
		//数据发送结束,调用shutdown()函数声明不再发送数据,此时客户端仍然可以接收数据
/*		iResult = shutdown(connectSocket, SD_SEND);
		if (iResult == SOCKET_ERROR) {
			printf("shutdown执行失败,错误码:%d\n", WSAGetLastError());
			closesocket(clientSocket);
			WSACleanup();
			return 1;
		}*/
		//持续接收数据,直到服务器关闭连接

		iResult = recv(connectSocket, recvbuf, recvbuflen, 0);
		if (iResult > 0) {
	//		printf("接收字节数:%d\n", iResult);
			cout << "服务器回复: " << recvbuf << endl;
		}
		else if (iResult == 0)
			printf("连接关闭……\n");
		else
			printf("接收数据失败,错误码:%d\n", WSAGetLastError());

	//	printf("(这是第%d次对话)\n", i+1);
	}


	//关闭套接字,释放资源
	closesocket(connectSocket);
	WSACleanup();
	return 0;


}

完结撒花!阿门!

posted @ 2022-12-23 12:23  weitinghaoshuai  阅读(166)  评论(0编辑  收藏  举报