实验目的:
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.youkuaiyun.com/hou09tian/article/details/74359036
特别鸣谢@彬佬的大力支持