计算机网络实验:Socket 通信 (c++)

实验目的:
1.了解 socket 通信过程
2.掌握 c++ 编写 socket

实验原理:
Socket 保证了不同计算机之间的通信,通信模型是客户端服务器之间的通信。客户 端和服务器都建立一个 socket 对象,然后通过 socket 对象对数据进行传输。通常服务器处 于一个无限循环状态,等待客户端的连接。过程如下图所示:
在这里插入图片描述

实验内容:
1、c++ 开发 Socket TCP 通信
2、c++ 编写 socket TCP 通信
3、利用 Socket 连接进行端口扫描

实验设备与软件环境
1、Windows10系统
2、软件:visual studio 2015

服务端代码:

//服务端代码:
#include "winsock2.h"  
#pragma comment(lib, "ws2_32.lib")  
#include <iostream>  
using namespace std;
//主函数
int main(int argc, char* argv[])
{
	const int BUF_SIZE = 64;
	WSADATA         wsd;            //WSADATA变量  
	SOCKET          sServer;        //服务器套接字  
	SOCKET          sClient;        //客户端套接字  
	SOCKADDR_IN     addrServ;;      //服务器地址  
	char            buf[BUF_SIZE];  //接收数据缓冲区  
	char            sendBuf[BUF_SIZE];//返回给客户端得数据  
	int             retVal;         //返回值  
									//初始化套结字动态库  
	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
	{
		cout << "WSAStartup failed!" << endl;
		return 1;
	}
	//创建套接字  
	sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET == sServer)
	{
		cout << "socket failed!" << endl;
		WSACleanup();//释放套接字资源;  
		return  -1;
	}
	//服务器套接字地址   
	addrServ.sin_family = AF_INET;
	addrServ.sin_port = htons(4999);
	addrServ.sin_addr.s_addr = INADDR_ANY;
	//绑定套接字  
	retVal = bind(sServer, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));
	if (SOCKET_ERROR == retVal)
	{
		cout << "bind failed!" << endl;
		closesocket(sServer);   //关闭套接字  
		WSACleanup();           //释放套接字资源;  
		return -1;
	}
	//开始监听   
	retVal = listen(sServer, 1);
	if (SOCKET_ERROR == retVal)
	{
		cout << "listen failed!" << endl;
		closesocket(sServer);   //关闭套接字  
		WSACleanup();           //释放套接字资源;  
		return -1;
	}
	//接受客户端请求  
	sockaddr_in addrClient;
	int addrClientlen = sizeof(addrClient);
	sClient = accept(sServer, (sockaddr FAR*)&addrClient, &addrClientlen);
	if (INVALID_SOCKET == sClient)
	{
		cout << "accept failed!" << endl;
		closesocket(sServer);   //关闭套接字  
		WSACleanup();           //释放套接字资源;  
		return -1;
	}
	while (true)
	{
		//接收客户端数据  
		ZeroMemory(buf, BUF_SIZE);
		retVal = recv(sClient, buf, BUF_SIZE, 0);
		if (SOCKET_ERROR == retVal)
		{
			cout << "recv failed!" << endl;
			closesocket(sServer);   //关闭套接字  
			closesocket(sClient);   //关闭套接字       
			WSACleanup();           //释放套接字资源;  
			return -1;
		}
		if (buf[0] == '0')
			break;
		cout << "客户端发送的数据: " << buf << endl;
		cout << "向客户端发送数据: ";
		cin >> sendBuf;
		send(sClient, sendBuf, strlen(sendBuf), 0);
	}
	//退出  
	closesocket(sServer);   //关闭套接字  
	closesocket(sClient);   //关闭套接字  
	WSACleanup();           //释放套接字资源;  
	return 0;
}

客户端代码:

//头文件
#include "winsock2.h"  
#include <iostream>  
#pragma comment(lib, "ws2_32.lib")  
using namespace std;
BOOL RecvLine(SOCKET s, char* buf); //读取一行数据  
									//主函数
int main(int argc, char* argv[])
{
	const int BUF_SIZE = 64;
	WSADATA wsd; //WSADATA变量  
	SOCKET sHost; //服务器套接字  
	SOCKADDR_IN servAddr; //服务器地址  
	char buf[BUF_SIZE]; //接收数据缓冲区  
	char bufRecv[BUF_SIZE];
	int retVal; //返回值  
				//初始化套结字动态库  
	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
	{
		cout << "WSAStartup failed!" << endl;
		return -1;
	}
	//创建套接字  
	sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET == sHost)
	{
		cout << "socket failed!" << endl;
		WSACleanup();//释放套接字资源  
		return  -1;
	}
	//设置服务器地址  
	servAddr.sin_family = AF_INET;
	servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	servAddr.sin_port = htons((short)4999);
	int nServAddlen = sizeof(servAddr);
	//连接服务器  
	retVal = connect(sHost, (LPSOCKADDR)&servAddr, sizeof(servAddr));
	if (SOCKET_ERROR == retVal)
	{
		cout << "connect failed!" << endl;
		closesocket(sHost); //关闭套接字  
		WSACleanup(); //释放套接字资源  
		return -1;
	}
	while (true)
	{
		//向服务器发送数据  
		ZeroMemory(buf, BUF_SIZE);
		cout << " 向服务器发送数据:  ";
		cin >> buf;
		retVal = send(sHost, buf, strlen(buf), 0);
		if (SOCKET_ERROR == retVal)
		{
			cout << "send failed!" << endl;
			closesocket(sHost); //关闭套接字  
			WSACleanup(); //释放套接字资源  
			return -1;
		}
		//RecvLine(sHost, bufRecv);  
		ZeroMemory(bufRecv, BUF_SIZE);
		recv(sHost, bufRecv, BUF_SIZE, 0); // 接收服务器端的数据, 只接收5个字符  
		cout << endl << "从服务器接收数据:" << bufRecv;
		cout << "\n";
	}
	//退出  
	closesocket(sHost); //关闭套接字  
	WSACleanup(); //释放套接字资源  
	return 0;
}

运行结果
在这里插入图片描述

端口扫描

//头文件
//#include "stdafx.h"
#include <stdio.h>
#include <Winsock2.h>
#pragma comment(lib,"Ws2_32.lib")

//目标地址
#define IP "127.0.0.1"
//线程个数
#define THREADCOUNT 60
DWORD WINAPI ThreadProc(LPVOID lpParameter);
//端口号
int PortNum = 0;
//临界区变量
CRITICAL_SECTION cs;
//线程函数
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	//创建套接字
	SOCKET TryConnect;
	while (1)
	{
		if (PortNum>65535)
		{
			break;
		}
		//进入临界区
		EnterCriticalSection(&cs);
		//领取任务
		int tmpport = PortNum;
		PortNum++;
		//离开临界区
		LeaveCriticalSection(&cs);

		TryConnect = socket(AF_INET, SOCK_STREAM, 0);
		if (INVALID_SOCKET == TryConnect)
		{
			printf("socket");
		}
		//尝试连接
		sockaddr_in addrMe = { 0 };
		addrMe.sin_family = AF_INET;
		DWORD threadID = GetCurrentThreadId();
		//printf("线程%d正在检测端口%d\n",threadID,PortNum);
		addrMe.sin_port = htons(tmpport);
		addrMe.sin_addr.S_un.S_addr = inet_addr(IP);
		int retCon = connect(TryConnect, (sockaddr*)&addrMe, sizeof(sockaddr_in));
		if (SOCKET_ERROR != retCon)
		{
			printf("----------------检测到目标主机开放%d端口\n", tmpport);
		}
	}
	return 0;
}
int main(int argc, char* argv[])
{
	//初始化套接字
	WSADATA ws;
	::WSAStartup(MAKEWORD(2, 0), &ws);

	//初始化临界区
	InitializeCriticalSection(&cs);

	//多线程扫描
	HANDLE hThread[THREADCOUNT];
	for (int i = 0; i<THREADCOUNT; i++)
	{
		hThread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, (LPVOID)0, 0, NULL);
	}
	WaitForMultipleObjects(THREADCOUNT, hThread, true, INFINITE);

	//删除临界区
	DeleteCriticalSection(&cs);

	system("pause");
	return 0;
}

注意:当出现“inet_addr”未声明的情况的时候,请参考以下博客的2.1即可解决问题
https://blog.csdn.net/hou09tian/article/details/74359036

特别鸣谢@彬佬的大力支持

  • 8
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值