网络程序设计 实验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;
}
完结撒花!阿门!