- 第一次是基本C++多线程socket通信+IO复用,我记得要把ws2_32.lib下载
代码如下:
多线程socket通信
服务器:
// multhreadServer.cpp : Defines the entry point for the console application. //
#include <winsock2.h>
#include <iostream>
#include <Windows.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
#include <time.h>
#define PORT 8000
#define IP_ADDRESS "127.0.0.1"
DWORD WINAPI ClientThread(LPVOID lpParameter)
{
// 接收每个客户端连接的处理函数
SOCKET CientSocket = (SOCKET)lpParameter;
int Ret = 0;
char RecvBuffer[MAX_PATH]; // 在 VC++6.0 中, MAX_PATH的值为 260 。
time_t t = time( 0 );
char tmp[64];
strftime(tmp, sizeof(tmp), "%Y/%m/%d %X %A 本年第 %j 天 %z",localtime(&t) );
Ret=send(CientSocket, tmp, (int)strlen(tmp), 0);
if ( Ret == SOCKET_ERROR )
{
cout << "Send Info Error" << endl;
}
while (true)
{
memset(RecvBuffer, 0x00, sizeof(RecvBuffer));
// 为新申请的内存做初始化工作,返回值为指向RecvBuffer 的指针。
Ret = recv(CientSocket, RecvBuffer, MAX_PATH, 0);
if (Ret == 0 || Ret == SOCKET_ERROR)
{
cout << " 客户端退出 !" << endl;
closesocket(CientSocket);// 关闭客户端套接字
break;
}
cout << " 接收到客户信息为:" << RecvBuffer << endl;
}
return 0;
}
int main(int argc, char* argv[])
{
WSADATA Ws;
SOCKET ServerSocket, CientSocket;
SOCKADDR_IN LocalAddr, ClientAddr;
int Ret = 0;
int AddrLen = 0;
HANDLE hThread = NULL;
//Init Windows Socket
if (WSAStartup(MAKEWORD(2, 2), &Ws) != 0)
{
cout << "Init Windows Socket Failed::" << GetLastError() << endl;
return -1;
}
//Create Socket
ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ServerSocket == INVALID_SOCKET)
{
cout << "Create Socket Failed::" << GetLastError() << endl;
return -1;
}
LocalAddr.sin_family = AF_INET;
LocalAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS);
LocalAddr.sin_port = htons(PORT);
memset(LocalAddr.sin_zero, 0x00, 8);
//Bind Socket
Ret = bind(ServerSocket, (struct sockaddr*)&LocalAddr, sizeof(LocalAddr));
if (Ret != 0)
{
cout << "Bind Socket Failed::" << GetLastError() << endl;
return -1;
}
Ret = listen(ServerSocket, 10);
if (Ret != 0)
{
cout << "listen Socket Failed::" << GetLastError() << endl;
return -1;
}
cout << " 服务端已经启动 " << endl;
while (true)
{
AddrLen = sizeof(ClientAddr);
CientSocket = accept(ServerSocket, (struct sockaddr*)&ClientAddr, &AddrLen);
if (CientSocket == INVALID_SOCKET)
{
cout << "Accept Failed::" << GetLastError() << endl;
break;
}
cout << "客户端连接::"<<inet_ntoa(ClientAddr.sin_addr)<<":"<<ClientAddr.sin_port<<endl;
hThread = CreateThread(NULL, 0, ClientThread, (LPVOID)CientSocket, 0, NULL);
if (hThread == NULL){
cout << "Create Thread Failed!" << endl;
break;
}
CloseHandle(hThread);
}
closesocket(ServerSocket);
closesocket(CientSocket);
WSACleanup();
return 0;
}
/*lpsa :线程句柄的安全属性,比如子进程是否可以继承这个线程句柄,一般情况
设置为 NULL
cbStack:线程栈大小,一般取0 表示默认大小 lpStartAddr :线程入口函数
lpvThreadParam :就是线程入口函数的参数,就是 ThreadProc( void* lpParamete) 的参数
fdwCreate :控制线程创建的标志一般为0,表示线程立即启动。
如果你想创建之后 CREATE_SUSPENDED,传入这个参数你需要再适当的地方调用
把线程挂起来可以传入 ResumeThread 启动线程
lpIDThread :是线程 ID 返回值,这个用来接收线程返回的
ID*/
客户端
// multhreadClient.cpp : Defines the entry point for the console application.
//
#include <cstdio>
#include<iostream>
#include<string>
#include <winsock2.h>
#include <Windows.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
#define PORT 8000
#define IP_ADDRESS "127.0.0.1"
int main() {
WSAData Ws;
// 存储被 WSAStartup 函数调用后返回值
SOCKET CientSocket;
SOCKADDR_IN ServerAddr;
int Ret = 0;
int AddrLen = 0;
HANDLE hThread = NULL;
char SendBuffer[MAX_PATH];
//Init Windows Socket
if (WSAStartup(MAKEWORD(2, 2), &Ws) != 0)
{
cout << "Init Windows Socket Failed::" << GetLastError() << endl;
return -1;
}
//Create Socket
CientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (CientSocket == INVALID_SOCKET)
{
cout << "Create Socket Failed::" << GetLastError() << endl;
return -1;
}
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.s_addr = inet_addr(IP_ADDRESS);
ServerAddr.sin_port = htons(PORT);
memset(ServerAddr.sin_zero, 0x00, 8);
Ret = connect(CientSocket, (struct sockaddr*)&ServerAddr, sizeof(ServerAddr));
if (Ret == SOCKET_ERROR)
{
cout << "Connect Error::" << GetLastError() << endl;
return -1;
}
else
{
cout << " 连接成功 !" << endl;
}
char recvbuf[100];
memset(recvbuf, 0x00, sizeof(recvbuf));
Ret = recv(CientSocket, recvbuf, 100, 0);
if (Ret == 0 || Ret == SOCKET_ERROR)
{
cout << " 接受失败 !" << endl;
}
cout << " 接收到的服务器端时间 :" << recvbuf << endl;
while (true)
{
cin.getline(SendBuffer, sizeof(SendBuffer));
// 接受一个字符串,可以接收空格并输出
Ret = send(CientSocket, SendBuffer, (int)strlen(SendBuffer), 0);
if (Ret == SOCKET_ERROR)
{
cout << "Send Info Error::" << GetLastError() << endl;
break;
}
}
closesocket(CientSocket);
WSACleanup();
return 0;
}
IO复用服务器:
#include <winsock2.h>
#include <Windows.h>
#include <MSWSock.h>
#include <stdio.h>
#include <iostream>
#include <map>
using namespace std;
#include <time.h>
#pragma comment(lib,"Ws2_32.lib")
#pragma comment(lib,"Mswsock.lib")
int main()
{
WSAData wsaData;
if (0 != WSAStartup(MAKEWORD(2, 2), &wsaData))
{
printf("初始化失败!%d\n", WSAGetLastError());
Sleep(5000);
return -1;
}
USHORT nport = 8000;
SOCKET sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
u_long ul = 1;
ioctlsocket(sListen, FIONBIO, &ul);
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(nport);
sin.sin_addr.S_un.S_addr = ADDR_ANY;
if (SOCKET_ERROR == bind(sListen, (sockaddr*)&sin, sizeof(sin)))
{
printf("bind failed!%d\n", WSAGetLastError());
Sleep(5000);
return -1;
}
listen(sListen, 5);
//1)初始化一个套接字集合fdSocket,并将监听套接字放入
fd_set socketSet;
FD_ZERO(&socketSet);
FD_SET(sListen, &socketSet);
TIMEVAL timev = { 1,0 };
char buf[4096];
fd_set readSet;
FD_ZERO(&readSet);
fd_set writeSet;
FD_ZERO(&readSet);
while (true)
{
//2)将fdSocket的一个拷贝fdRead传给select函数
readSet = socketSet;
writeSet = socketSet;
//同时检查套接字的可读可写性。
int nRetAll = select(0, &readSet, &writeSet, NULL, NULL/*&timev*/);//若不设置超时则select为阻塞
if (nRetAll >0) //-1
{
//是否存在客户端的连接请求。
if (FD_ISSET(sListen, &readSet))//在readset中会返回已经调用过listen的套接字。
{
if (socketSet.fd_count < FD_SETSIZE)
{
sockaddr_in addrRemote;
int nAddrLen = sizeof(addrRemote);
SOCKET sClient = accept(sListen, (sockaddr*)&addrRemote, &nAddrLen);
if (sClient != INVALID_SOCKET)
{
FD_SET(sClient, &socketSet);
printf("\n接收到连接:(%s)", inet_ntoa(addrRemote.sin_addr));
}
}
else
{
printf("连接数量已达上限!\n");
continue;
}
}
for (int i = 0; i<socketSet.fd_count; i++)
{
if (FD_ISSET(socketSet.fd_array[i], &readSet))
{
//调用recv,接收数据。
int nRecv = recv(socketSet.fd_array[i], buf, 4096, 0);
if (nRecv > 0)
{
buf[nRecv] = 0;
printf("\nrecv %d : %s", socketSet.fd_array[i], buf);
}
}
if (FD_ISSET(socketSet.fd_array[i], &writeSet))
{
char buf[64];
time_t t = time(0);
strftime(buf, sizeof(buf), "%Y/%m/%d %X %A 本年第 %j 天 %z", localtime(&t));
int nRet = send(socketSet.fd_array[i], buf, strlen(buf) + 1, 0);
if (nRet <= 0)
{
if (GetLastError() == WSAEWOULDBLOCK)
{
//do nothing
}
else
{
closesocket(socketSet.fd_array[i]);
FD_CLR(socketSet.fd_array[i], &socketSet);
}
}
else
{
//printf("\nsend hello! %d", socketSet.fd_array[i]);
}
}
}
}
else if (nRetAll == 0)
{
printf("time out!\n");
}
else
{
printf("select error!%d\n", WSAGetLastError());
Sleep(5000);
break;
}
Sleep(1000);
}
closesocket(sListen);
WSACleanup();
}
IO复用客户端:
#include<stdlib.h>
#include<WINSOCK2.H>
#include <windows.h>
#include <process.h>
#include<iostream>
#include<string>
using namespace std;
#define BUF_SIZE 64
#pragma comment(lib,"WS2_32.lib")
void recv(PVOID pt)
{
SOCKET sHost = *((SOCKET *)pt);
while (true)
{
char buf[BUF_SIZE];//清空接收数据的缓冲区
memset(buf, 0, BUF_SIZE);
int retVal = recv(sHost, buf, sizeof(buf), 0);
if (SOCKET_ERROR == retVal)
{
int err = WSAGetLastError();
//无法立即完成非阻塞Socket上的操作
if (err == WSAEWOULDBLOCK)
{
Sleep(1000);
printf("\nwaiting reply!");
continue;
}
else if (err == WSAETIMEDOUT || err == WSAENETDOWN || err == WSAECONNRESET)//已建立连接
{
printf("\nrecv failed!");
closesocket(sHost);
WSACleanup();
return;
}
}
Sleep(100);
printf("\n%s", buf);
//break;
}
}
int main()
{
WSADATA wsd;
SOCKET sHost;
SOCKADDR_IN servAddr;//服务器地址
int retVal;//调用Socket函数的返回值
char buf[BUF_SIZE];
//初始化Socket环境
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
printf("WSAStartup failed!\n");
return -1;
}
sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//设置服务器Socket地址
servAddr.sin_family = AF_INET;
servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//在实际应用中,建议将服务器的IP地址和端口号保存在配置文件中
servAddr.sin_port = htons(8000);
//计算地址的长度
int sServerAddlen = sizeof(servAddr);
//调用ioctlsocket()将其设置为非阻塞模式
int iMode = 1;
retVal = ioctlsocket(sHost, FIONBIO, (u_long FAR*)&iMode);
if (retVal == SOCKET_ERROR)
{
printf("ioctlsocket failed!");
WSACleanup();
return -1;
}
//循环等待
while (true)
{
//连接到服务器
retVal = connect(sHost, (LPSOCKADDR)&servAddr, sizeof(servAddr));
if (SOCKET_ERROR == retVal)
{
int err = WSAGetLastError();
//无法立即完成非阻塞Socket上的操作
if (err == WSAEWOULDBLOCK || err == WSAEINVAL)
{
Sleep(1);
printf("check connect!\n");
continue;
}
else if (err == WSAEISCONN)//已建立连接
{
break;
}
else
{
printf("connection failed!\n");
closesocket(sHost);
WSACleanup();
return -1;
}
}
}
unsigned long threadId = _beginthread(recv, 0, &sHost);//启动一个线程接收数据的线程
while (true)
{
//向服务器发送字符串,并显示反馈信息
printf("input a string to send:\n");
std::string str;
//接收输入的数据
std::cin >> str;
//将用户输入的数据复制到buf中
ZeroMemory(buf, BUF_SIZE);
strcpy(buf, str.c_str());
if (strcmp(buf, "quit") == 0)
{
printf("quit!\n");
break;
}
while (true)
{
retVal = send(sHost, buf, strlen(buf), 0);
if (SOCKET_ERROR == retVal)
{
int err = WSAGetLastError();
if (err == WSAEWOULDBLOCK)
{
//无法立即完成非阻塞Socket上的操作
Sleep(5);
continue;
}
else
{
printf("send failed!\n");
closesocket(sHost);
WSACleanup();
return -1;
}
}
break;
}
}
system("pause");
return 0;
}
第二次作业:
使用RAW socket捕捉数据,客户端和服务器使用的第一次多线程socket通信的客户端和服务器
#include <stdio.h>
#include <Winsock2.h>
#include <mstcpip.h>
#include <time.h>
#include <iostream>
#include <string>
#pragma comment(lib,"WS2_32.lib")
#pragma pack(1)
using namespace std;
struct TCPHeader
{
WORD SrcPort; // 源端口
WORD DstPort; // 目的端口
DWORD SeqNum; // 顺序号
DWORD AckNum; // 确认号
BYTE DataOff; // TCP头长
BYTE Flags; // 标志(URG、ACK等)
WORD Window; // 窗口大小
WORD Chksum; // 校验和
WORD UrgPtr; // 紧急指针
};
struct UDPHeader
{
WORD SrcPort; // 源端口
WORD DstPort; // 目的端口
WORD DataOff; // UDP头长
WORD Chksum; // 校验和
};
struct IPHeader
{
union {
BYTE Version; // 版本
BYTE HdrLen; // IHL
};
BYTE ServiceType; // 服务类型
WORD TotalLen; // 总长
WORD ID; // 标识
union {
WORD Flags; // 标志
WORD FragOff; // 分段偏移
};
BYTE TimeToLive; // 生命期
BYTE Protocol; // 协议
WORD HdrChksum; // 头校验和
DWORD SrcAddr; // 源地址
DWORD DstAddr; // 目的地址
//BYTE Options; // 选项
};
// test
struct NetData
{
char strData[10000]; // 数据
NetData() {
memset(this, 0, sizeof(NetData));
}
};
#pragma pack()
void printNetData(NetData p) {
if (p.strData[0]!='\0') {
cout << "捕获的数据是: " << p.strData << endl;
}
else {
cout << "捕获到的数据为空" << endl;
}
}
//编程实现主要有以下几个步骤:
//1.初始化WINSOCK库;
//2.创建SOCKET句柄;
//3.绑定SOCKET句柄到一个本地地址;
//4.设置该SOCKET为接收所有数据的模式;
//5.接收数据包;
//6.关闭SOCKET句柄,清理WINSOCK库.
// 使用原始套接字抓取网卡数据包
int main()
{
WSADATA wsaData;
SOCKET sock = SOCKET_ERROR;
int iRet = 0;
iRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iRet != 0) {
printf("WSAStartup failed with error %d\n", WSAGetLastError());
return -1;
}
// 此处必须以管理员运行VS,否则返回10013
sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
if (sock == SOCKET_ERROR) {
printf("socket failed with error %d\n", WSAGetLastError());
goto tag_end;
}
char strHostName[255];
iRet = gethostname(strHostName, sizeof(strHostName));
if (iRet != 0) {
printf("gethostname failed with error %d\n", WSAGetLastError());
goto tag_end;
}
// 根据主机名取得主机地址
hostent *pHostent = gethostbyname(strHostName);
sockaddr_in addrSelf;
addrSelf.sin_family = AF_INET;
addrSelf.sin_port = htons(0);
//addrSelf.sin_addr.S_un.S_addr = htonl(0x3b481341);//59.72.19.65
addrSelf.sin_addr.S_un.S_addr = htonl(0x7f000001);//127.0.0.1
//memcpy(&addrSelf.sin_addr.S_un.S_addr, pHostent->h_addr_list[0], pHostent->h_length);
printf("self local ip addr is %s\n\n", inet_ntoa(addrSelf.sin_addr));
iRet = bind(sock, (PSOCKADDR)&addrSelf, sizeof(addrSelf));
if (iRet != 0) {
printf("bind failed with error %d\n", WSAGetLastError());
goto tag_end;
}
DWORD dwBufferLen[10] = { 0 };
DWORD dwBufferInLen = 1;
DWORD dwBytesReturned = 0;
// 设置该SOCKET为接收所有流经绑定的IP的网卡的所有数据,包括接收和发送的数据包
iRet = WSAIoctl(sock, SIO_RCVALL, &dwBufferInLen, sizeof(dwBufferInLen),
&dwBufferLen, sizeof(dwBufferLen), &dwBytesReturned, NULL, NULL);
if (iRet != 0) {
printf("WSAIoctl failed with error %d\n", WSAGetLastError());
goto tag_end;
}
sockaddr_in addrFrom;
int fromlen = sizeof(addrFrom);
time_t temp;
char strFromIP[16] = { 0 }, strCurTime[32] = { 0 };
char strBuffer[1024 * 100], strData[4096] = { 0 };
IPHeader ipData;
TCPHeader tcpData;
UDPHeader udpData;
NetData netData;
while (true)
{
memset(strBuffer, 0, sizeof(strBuffer));
iRet = recvfrom(sock, strBuffer, sizeof(strBuffer), 0, (sockaddr*)&addrFrom, &fromlen);
if (iRet <= 0) {
printf("recv failed with error %d\n", WSAGetLastError());
break;
}
//printf("%s\n", strBuffer);
strcpy(strFromIP, inet_ntoa(addrFrom.sin_addr));
// if(strcmp(strFromIP, "192.168.0.104") == 0
// || strcmp(strFromIP, "192.168.0.1") == 0
// || strcmp(strFromIP, "192.168.0.103") == 0){
// continue;
// }
/*if (strcmp(strFromIP, "192.168.0.104") != 0) {
continue;
}*/
// 处理IP包头数据
memcpy(&ipData, strBuffer, sizeof(ipData));
BYTE Protocol = 0;
sockaddr_in addrSrc, addrDst;
char strSrcIP[16] = { 0 }, strDstIP[16] = { 0 };
addrSrc.sin_addr.S_un.S_addr = ipData.SrcAddr;
addrDst.sin_addr.S_un.S_addr = ipData.DstAddr;
strcpy(strSrcIP, inet_ntoa(addrSrc.sin_addr));
strcpy(strDstIP, inet_ntoa(addrDst.sin_addr));
// if (strcmp(strSrcIP, "192.168.0.104") != 0 || strcmp(strDstIP, "192.168.0.104") != 0) {
// continue;
// }
time(&temp);
printf("=======================================\n");
strftime(strCurTime, sizeof(strCurTime), "%Y-%m-%d %H:%M:%S", localtime(&temp));
printf("IP HEADER\n");
printf("Time:%s Length:%d from %s\n", strCurTime, iRet, strFromIP);
printf("版本:%d\n", ipData.Version>>4);
int len = ntohs(ipData.HdrLen);
cout << "首部长度" << ((ipData.HdrLen & 0x0f) * 4) << endl;
//printf("首部长度:%d\n", len);
cout << "服务类型:优先级 " << (ipData.ServiceType >> 5) << "TOS子域" << ((ipData.ServiceType >> 1) & 0x0f) << endl;
//printf("服务类型:%d\n", ipData.ServiceType);
cout << "总长度为:" << ipData.TotalLen << endl;
//printf("总长度:%d\n", ipData.TotalLen);
printf("16位标识:%d\n", ipData.ID);
cout << "标志位: " << ((ipData.Flags >> 15) & 0x01) << " ,DF= " << ((ipData.Flags >> 14) & 0x01)
<< ",Mf= " << ((ipData.Flags >> 13) & 0x01) << endl;
cout << "片偏移:" << ((ipData.FragOff & 0x1fff)) << endl;
//printf("标志:%d\n", ipData.Flags);
//printf("位偏移:%d\n", ipData.FragOff);
printf("寿命:%d\n", (int)ipData.TimeToLive);
printf("上层协议:%d\n", ipData.Protocol);
printf("首部校验和:%d\n", ipData.HdrChksum);
printf("目的IP地址:%s\n", strDstIP);
printf("源IP地址:%s\n", strFromIP);
// UDP协议
if (ipData.Protocol == IPPROTO_UDP)
{
if (iRet != 1052) {
continue;
}
memcpy(&udpData, strBuffer + sizeof(ipData), sizeof(udpData));
int iSrcPort = ntohs(udpData.SrcPort);
int iDstPort = ntohs(udpData.DstPort);
printf("****UDP协议****:\n");
memcpy(&netData, strBuffer + sizeof(ipData) + sizeof(udpData), sizeof(netData));
printf("源端口号:%d\n", iSrcPort);
printf("目的端口号:%d\n", iDstPort);
printNetData(netData);
}
// TCP协议
else if (ipData.Protocol == IPPROTO_TCP)
{
printf("****TCP协议****:\n");
//if (iRet != 1064) {
// continue;
//}
memcpy(&tcpData, strBuffer + sizeof(ipData), sizeof(tcpData));
int iSrcPort = ntohs(tcpData.SrcPort);
int iDstPort = ntohs(tcpData.DstPort);
memcpy(&netData, strBuffer + sizeof(ipData) + sizeof(tcpData), sizeof(netData));
printf("源端口号:%d\n", iSrcPort);
printf("目的端口号:%d\n", iDstPort);
//int len = ntohs(netData.iDataLen);
//cout << netData.strData << endl;
//cout << "数据长度为:" << len << endl;
//cout << netData.chChche << endl;
printNetData(netData);
}
else {
printf("****其他协议,不处理****:\n");
}
}
tag_end:
if (sock != SOCKET_ERROR) {
if (closesocket(sock) == SOCKET_ERROR) {
printf("closesocket failed with error %d\n", WSAGetLastError());
return -1;
}
}
if (WSACleanup() == SOCKET_ERROR) {
printf("WSACleanup failed with error %d\n", WSAGetLastError());
return -1;
}
return 0;
}
第三次作业:基本一个FTP服务器的东西,简单用控制台实现了,参照:https://blog.csdn.net/qq_38941327/article/details/89279348
第四次作业:和上次的合并了,没做的很复杂