- 指针学习
- 0参考资料
- 1. 指针
- 2. 二级指针:存放的内容是变量地址的地址
- 3 const 修饰
- 4函数指针
- 内存管理
- 0.参考资料
- 1.C++中内存分为5个区
- 2 堆与栈的区别
- 3保守的使用内存分配,重载new/delete
- 4.常见的内存错误及其策略
- 5 数组与指针的对比
- 回调函数
- 0 学习资料
- 1. 对Person的一组对象进行排序
- 2. 实现回调的三种方式
- 编程练习
- 0
- 1阶乘
- 2. 拆分字符串
- 3建立MFC对话框过程
- 4 删除目录
- 5 以' '拆分一个缓冲
- 6MFC按照界面规范重新布局下面的窗口
- 7.编写一个表格控件类
- 8 网络协议
- 网络协议
- 1TCP通信
- socket通信/windows
- 0
- 1两种Internet套接字类型
- 2.OSI网络模型,TCP/IP网络模型
- 3确认信息的三要素:IP,MAC,Port
- socket中的函数
- socket 程序演示
博客
C/C++指针详解之基础篇, 尘海折柳
C/C++指针详解之提高篇, 尘海折柳
(1)指针是特殊的变量,存放的内容是变量地址
(2)通过指针访问数组元素
#includeusing namespace std; int main() { int a[]={ 8,5,85,850,8500 }; int* pa = a; //用数组名和指向数组名的指针打印数组元素 for (int i = 0; i < sizeof(a)/sizeof(int); i++) cout << a[i] << " "; cout << endl; for (int i = 0; i < sizeof(a) / sizeof(int); i++) cout << *(pa+i) << " "; cout << endl; return 0; }
(3)数组名可以看成是特殊的指针,能改变指向的内容不能改变指向,所以将数组名a+1是不对的
(4)指针数组:是数组,数组中每个元素是指针,char * a[85]等价于char* (a[85])
#include2. 二级指针:存放的内容是变量地址的地址using namespace std; int main() { const char* wyb[] = { "wyb","is","ssj","zkad","bb" }; for (int i = 0; i < sizeof(wyb) / sizeof(wyb[0]); i++) cout << wyb[i] << " "; cout << endl; return 0; }
(1)二级指针能改变一级指针的指向
#includeusing namespace std; int main() { int i = 85; int* pi = &i; int** ppi = π cout << "i=" << i << "t*pi=" << *pi << "tt**ppi=" << **ppi << endl; *pi = 850; int b = 8500; *ppi = &b; //修改ppi的内容使得:pi由指向a变为指向b cout << "i=" << i << "t*pi=" << *pi << "t**ppi=" << **ppi << endl; return 0; }
(2)二级指针的步长:指针的步长是存储的内容的类型的大小,二级指针存储的内容是一级指针,一级指针的大小是4字节
#includeusing namespace std; int main() { double i = 85; double* pi = &i; double** ppi = π cout << "*pi的地址:" << pi << "pi走一步*(pi+1)的地址:" << pi + 1 << endl; cout << "pi的步长是:" <<(int) (pi + 1) -(int) pi << endl;//8 cout << "*ppi的地址:" << ppi << "ppi走一步*(ppi+1)的地址:" << ppi + 1 << endl; cout << "ppi的步长是:" << (int)(ppi + 1) - (int)ppi << endl;//4 return 0; }
(3)用二级指针指向指针数组
#includeusing namespace std; int main() { const char* wyb[] = { "wyb","is","ssj","zkad","bb" }; const char** ppwyb = wyb; //用指针数组名和指向数组名的二级指针打印 for (int i = 0; i < sizeof(wyb) / sizeof(wyb[0]); i++) cout << wyb[i] << " "; cout << endl; for (int i = 0; i < sizeof(wyb) / sizeof(wyb[0]); i++) cout << *(ppwyb+i) << " "; cout << endl; return 0; }
通过在指针数组增加NULL,使得二级指针打印的时候不需要提前计算出维度
#includeusing namespace std; int main() { const char* wyb[] = { "wyb","is","ssj","zkad","bb",NULL }; const char** ppwyb = wyb; //在指针数组末尾增加NULL,使得二级指针遍历时不需要提前计算指针数组元素个数 while (*ppwyb != NULL) { cout << *ppwyb++ << " "; } cout << endl; return 0; }
(4)在堆上申请一维数组
#includeusing namespace std; //用一维指针申请内存 char* allocSpace(int n) { char* p =(char*) malloc(n*sizeof(char)); //char* p = new char[n * sizeof(char)];//C++中的,但是听说malloc/free更快,我还小白 return p; } //用二维指针申请内存,返回-1申请失败 int allocSpace_1(char **p, int n) { *p = (char*)malloc(n * sizeof(char)); /) : CDialogEx(IDD_EXERCISE31_DIALOG, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINframe); m_pTipDlg = NULL; }
修改onBnClickedAddButton()的函数体
void Cexercise31Dlg::OnBnClickedCancel()
{
// TODO: 在此添加控件通知处理程序代码
if (NULL == m_pTipDlg1){
// 创建非模态对话框实例
m_pTipDlg1 = new CTipDlg1();
m_pTipDlg1->Create(IDD_TIP_DIALOG, this);
}
// 显示非模态对话框
m_pTipDlg1->ShowWindow(SW_SHOW);
//CDialogEx::onCancel();
}
//CDialogEx::onCancel();不注释会发生闪退,注释了以后点击主窗口的叉号键不能正常退出
5该对话框背景为一位图拉伸铺满整个窗口显示
不知道怎么弄,先手拉吧->_->
6.点击窗口关闭按钮退出程序
重写onclose()
右击CxxxDlg类,点击属性,点击重写
void Cexercise31Dlg::OnClose()
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
EndDialog(IDCANCEL); //关闭窗口
CDialog::OnClose();
}
7.按回车、ESC键不执行任何操作
重写CXXXDlg类中的Cexercise31Dlg::PreTranslateMessage(MSG* pMsg)
可以是ESC键失效
BOOL Cexercise31Dlg::PreTranslateMessage(MSG* pMsg)
{
// TODO: 在此添加专用代码和/或调用基类
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ESCAPE) return TRUE; //去掉esc退出功能
//if(pMsg->message==WM_KEYDOWN && pMsg->wParam==VK_RETURN) return TRUE; //去掉回车退出功能,当然在这里也会导致整个窗体的失去回车效果
else //其他正常
return CDialogEx::PreTranslateMessage(pMsg);
}
这段重写可以使enter和esc都失效
BOOL Cexercise31Dlg::PreTranslateMessage(MSG* pMsg){
if (pMsg->message == WM_KEYDOWN)//会使所有按钮失效
{
switch (pMsg->wParam)
{
case VK_RETURN:
return TRUE;
case VK_ESCAPE:
return TRUE;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
4 删除目录
这个对我来说太困难了 ||_||
我得分步骤慢慢来
WIN32_FIND_DATA结构体,第一个一属性DWORD dwFileAttributes; //文件属性
typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes; //文件属性
FILETIME ftCreationTime; // 文件创建时间
FILETIME ftLastAccessTime; // 文件最后一次访问时间
FILETIME ftLastWriteTime; // 文件最后一次修改时间
DWORD nFileSizeHigh; // 文件长度高32位
DWORD nFileSizeLow; // 文件长度低32位
DWORD dwReserved0; // 系统保留
DWORD dwReserved1; // 系统保留
TCHAR cFileName[MAX_PATH]; // 长文件名
TCHAR cAlternateFileName[14]; // 8.3格式文件名
} WIN32_FIND_DATA, * PWIN32_FIND_DATA;
FindFirstFile函数原型
HANDLE FindFirstFileW( LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData );
lpFileName是目录或路径以及文件名。文件名可以包含通配符(*)(?),lpFileName不能为NULL,无效字符串(缺少 ),以()结尾
lpFindFile是指针,用来接收收到的文件或目录信息
可以递归的查看路径wstrDir下名字带有字符串s文件和文件夹
递归的意思是指如果某个子文件夹下含有带有字符串s文件和文件夹也会被删除
#include#include #include using namespace std; //查看指定目录中所有直接文件和目录 void ListFilesInDirectory(wstring wstrDir, wstring s) { if (wstrDir.empty()) return; HANDLE hFind; //WIN32_FIND_DATA结构体,第一属性DWORD dwFileAttributes; //文件属性 WIN32_FIND_DATA findData; //在目录后添加“*”,目录中的所有直接文件和目录 wstring wstrTempDir = wstrDir + (L"\*"); hFind = FindFirstFile(wstrTempDir.c_str(), &findData); if (hFind == INVALID_HANDLE_VALUE) return; do { //过滤掉(.) if (wcscmp(findData.cFileName, L".") == 0) continue; //如果字符串中没有某个子串就过滤掉,If the string is not found, wcswcs() returns NULL. if (wcswcs(findData.cFileName, s.c_str()) == NULL) continue; wstring wstrFilename; wstrFilename.assign(wstrDir); wstrFilename.append(L"\"); wstrFilename.append(findData.cFileName); wcout << wstrFilename << endl;//(.)和(..)分别指当前目录和上级目录 if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { //如果是目录就递归的删除 ListFilesInDirectory(wstrFilename, s.c_str()); } } while (FindNextFile(hFind, &findData) != 0); FindClose(hFind); } int main() { ListFilesInDirectory(L"F:\C++\test\testFile1\test1",L"1"); return 0; }
#include#include #include using namespace std; void RemoveAllFiles1(wstring wstrDir) { if (wstrDir.empty()) return; HANDLE hFind; WIN32_FIND_DATA findData; wstring wstrTempDir = wstrDir + (L"\*"); hFind = FindFirstFile(wstrTempDir.c_str(), &findData); if (hFind == INVALID_HANDLE_VALUE) return; do { wstring wstrFilename; wstrFilename.assign(wstrDir); wstrFilename.append(L"\"); wstrFilename.append(findData.cFileName); if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { RemoveAllFiles1(wstrFilename); } else { DeleteFile(wstrFilename.c_str()); } } while (FindNextFile(hFind, &findData) != 0); FindClose(hFind); RemoveDirectory(wstrDir.c_str()); } //查看指定目录中所有直接文件和目录 BOOL RemoveAllFiles( wstring wstrDir, wstring s ){ if (wstrDir.empty()) return false; HANDLE hFind; //WIN32_FIND_DATA结构体,第一属性DWORD dwFileAttributes; //文件属性 WIN32_FIND_DATA findData; //在目录后添加“*”,目录中的所有直接文件和目录 wstring wstrTempDir = wstrDir + (L"\*"); hFind = FindFirstFile(wstrTempDir.c_str(), &findData); if (hFind == INVALID_HANDLE_VALUE) return false; do { wstring wstrFilename; wstrFilename.assign(wstrDir); wstrFilename.append(L"\"); wstrFilename.append(findData.cFileName); //如果字符串中没有某个子串就过滤掉, if (findData.cFileName!=NULL&& wcswcs(findData.cFileName, s.c_str()) != NULL) { RemoveAllFiles1(wstrFilename); } } while (FindNextFile(hFind, &findData) != 0); FindClose(hFind); //RemoveDirectory(wstrDir.c_str()); return TRUE; }
it did hhh ^ O ^ runrunrun~
还差:a.改格式
BOOL RemoveSpecifiedDir(LPCTSTR lpszDirPath, LPCTSTR lpszSpecifiedDir)
**c.“exe放到任意一个目录中,执行exe后就能把该目录下所有指定目录删除”**是要自动获取.exe所在文件夹路径?
string GetExePath()
{
char szFilePath[MAX_PATH + 1] = { 0 };
GetModuleFileNameA(NULL, szFilePath, MAX_PATH);
(strrchr(szFilePath, '\'))[0] = 0; // 删除文件名,只获得路径字串//
string path = szFilePath;
return path;
}
5 以' '拆分一个缓冲
怎么把带 的字符串输入到LPCTSTR里啊 搞不明白
这个代码输不进去 ,maybe 看文件的输入方式可以
一个方法就是赋值的时候不要经过string或者CString,直接
LPCTSTR lpszSrc = (LPCTSTR)(_T("wyb wyb wyb "));
但这要要从test()函数里自己输入
#include6MFC按照界面规范重新布局下面的窗口#include #include #include #include typedef std::vector CStringArray; void SplitBufferByZero(LPCTSTR lpBuf, int nBufLen, CStringArray& szaDst); void test() {//只能人工验证了吧。。。 //CString str(_T("wyb wyb wyb ")); //LPCTSTR lpszSrc = (LPCTSTR)str; LPCTSTR lpszSrc = (LPCTSTR)(_T("wyb wyb wyb "));//将string 转换为LPCTSTR CStringArray szaDst; SplitBufferByZero(lpszSrc,20, szaDst); for (int i = 0; i < szaDst.size(); i++) { //std::cout << (LPCTSTR)szaDst[i]<< " "; printf("%ls", (LPCTSTR)szaDst[i]); } std::cout << std::endl; } void SplitBufferByZero(LPCTSTR lpBuf, int nBufLen, CStringArray& szaDst) { CString tmp; //std::string s; for (int i = 0; i < nBufLen; i++) { tmp += lpBuf[i]; if (lpBuf[i] == ' ') {//把临时串写进 szaDst szaDst.push_back(tmp);//分割以指定字符串结尾 tmp = _T(""); } } if (tmp != _T("")) szaDst.push_back(tmp); } int main1() { test(); return 0; }
1.建议参考IE、Outlook、QQ、MSN等软件的选项设置界面
1.添加4 个static text:服务名称,可执行文件的路径,启动类型,服务状态
2. 2个下拉框 :添加2个Combo Box控件,ID分别为设置为IDC_NAME_COMBO,IDC_TYPE_COMBO,Type属性设为Drop List,,编辑框不允许用户输入,Sort属性设为False,以取消排序显示。
再添加一个静态文本控件和一个编辑框,静态文本控件的Caption属性设为“您选择的网站:”,编辑框的ID设为IDC_SEL_WEB_EDIT,Read Only属性设为True。此时的对话框模板如下图:
3 添加5个Button:浏览IDC_LOOK_BUTTON,启动IDC_START_BUTTON,停止IDC_STOP_BUTTON,注册IDC_REGISTER_BUTTON,注销IDC_LOGOUT_BUTTON
4. 去掉“确定按钮”,把“取消”按钮改成“退出”
5. 加一个与浏览关联的编辑框IDC_LOOK_EDIT,显示打开文件对话框中选择的文件路径
6.为浏览按钮IDC_LOOK_BUTTON添加事件处理程序,点击消息的消息处理函数
Cexercise5Dlg::onBnClickedLookButton()
void Cexercise5Dlg::OnBnClickedLookButton()
{
// TODO: 在此添加控件通知处理程序代码
TCHAR szFilter[] = _T("文本文件(*.txt)|*.txt|所有文件(*.*)|*.*||");
// 构造打开文件对话框
CFileDialog fileDlg(TRUE, _T("txt"), NULL, 0, szFilter, this);
CString strFilePath;
// 显示打开文件对话框
if (IDOK == fileDlg.DoModal())
{
// 如果点击了文件对话框上的“打开”按钮,则将选择的文件路径显示到编辑框里
strFilePath = fileDlg.GetPathName();
SetDlgItemText(IDC_LOOK_EDIT, strFilePath);
}
}
保存文件类似的有代码
void CExample17Dlg::OnBnClickedSaveButton()
{
// TODO: Add your control notification handler code here
// 设置过滤器
TCHAR szFilter[] = _T("文本文件(*.txt)|*.txt|Word文件(*.doc)|*.doc|所有文件(*.*)|*.*||");
// 构造保存文件对话框
CFileDialog fileDlg(FALSE, _T("doc"), _T("my"), OFN_HIDEREADonLY | OFN_OVERWRITEprompt, szFilter, this);
CString strFilePath;
// 显示保存文件对话框
if (IDOK == fileDlg.DoModal())
{
// 如果点击了文件对话框上的“保存”按钮,则将选择的文件路径显示到编辑框里
strFilePath = fileDlg.GetPathName();
SetDlgItemText(IDC_SAVE_EDIT, strFilePath);
}
}
- 给启动下拉列表添加选项
为组合框IDC_TYPE_COMBO添加控件变量(属性里添加) CComboBox m_comboType;
修改对话框初始函数BOOL Cexercise5Dlg::onInitDialog()
// TODO: Add extra initialization here
// 为组合框控件的列表框添加列表项
m_comboWeb.AddString(_T("手动"));
m_comboWeb.AddString(_T("自动"));
// 默认选择第一项
m_comboWeb.SetCurSel(0);
SetDlgItemText(IDC_SEL_WEB_EDIT, _T("手动"));
做出来的界面就是这样的~
不懂服务名称是什么意思,以及服务状态为什么是灰色的,要触发什么还是
下面的这段代码可以粗糙的客户端向服务器发送一个协议结构体,然后服务器输出这个结构体的信息
客户端
#include#include #include #pragma comment(lib, "ws2_32.lib") #include #include struct TUserInfo { char name[31]; char mima[31]; char address[63]; short port; }; struct messageHeader { char cmd; int lenData; }; const int cmdLen = 1, dataLen = 4; const int nameLen = 31, miamaLen = 31, addressLen = 63, portLen = 2; using namespace std; void sendMessage() { TUserInfo wyb = { "wyb","software","127.0.0.1",4477 }; //初始化ddl WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); //创建套接字 SOCKET clntSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); //向服务端发起请求 struct sockaddr_in sockAddr; memset(&sockAddr, 0, sizeof(sockAddr)); sockAddr.sin_family = PF_INET; sockAddr.sin_addr.S_un.S_addr = inet_addr(wyb.address); sockAddr.sin_port = htons(wyb.port); connect(clntSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)); //客户端发送数据 while (1) { char str[150] = { ' ' }; for (int i = 0; i < cmdLen; i++) str[i] = '1'; for (int i = cmdLen + dataLen; i < cmdLen + dataLen + nameLen; i++) str[i] = wyb.name[i-(cmdLen + dataLen)]; for (int i = cmdLen + dataLen + nameLen; i < cmdLen + dataLen + nameLen + miamaLen; i++) str[i] = wyb.mima[i-(cmdLen + dataLen + nameLen)]; for (int i = cmdLen + dataLen + nameLen + miamaLen; i < cmdLen + dataLen + nameLen + miamaLen+ addressLen; i++) str[i] = wyb.address[i-(cmdLen + dataLen + nameLen + miamaLen)]; string tmp= to_string(wyb.port); for (int i = cmdLen + dataLen + nameLen + miamaLen + addressLen; i < cmdLen + dataLen + nameLen + miamaLen + addressLen + tmp.size(); i++) str[i] = tmp[i-(cmdLen + dataLen + nameLen + miamaLen + addressLen)]; send(clntSock, str, 150, NULL); Sleep(10000); } //关闭套接字 closesocket(clntSock); //终止使用ddl WSACleanup(); //system("pause"); } int main() { thread mythread(sendMessage); mythread.join(); std::cout << "主线程执行" << std::endl; system("pause"); return 0; }
服务器
#include#include #include #pragma comment(lib, "ws2_32.lib") #include struct TUserInfo { char name[31]; char mima[31]; char address[63]; short port; }; struct messageHeader { char mingling; int lenData; }; using namespace std; void sendMessage() { //初始化dll WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); //创建套接字 SOCKET servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); //绑定套接字 struct sockaddr_in sockAddr; memset(&sockAddr, 0, sizeof(sockAddr));//每个字节都用0填充 sockAddr.sin_family = PF_INET; sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //InetPton(AF_INET, (LPCTSTR)"127.0.0.1", &sockAddr.sin_addr.S_un.S_addr); sockAddr.sin_port = htons(4477); bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)); //进入监听状态 listen(servSock, 20); //接收客户端请求 SOCKADDR clintAddr; int nSize = sizeof(SOCKADDR); SOCKET clntSock = accept(servSock, (SOCKADDR*)&sockAddr, &nSize); //向客户端发送数据 //接收服务器传回的数据 char szBuffer[MAXBYTE] = { 0 }; recv(clntSock, szBuffer, MAXBYTE, NULL); //输出接收到的数据 for (int i = 0; i < MAXBYTE; i++) { if (szBuffer[i] != ' ') cout << szBuffer[i]; } //关闭套接字 closesocket(servSock); closesocket(clntSock); //终止dll的使用 WSACleanup(); } int main() { thread mythread(sendMessage); mythread.join(); std::cout << "主线程执行" << std::endl; system("pause"); return 0; }
本题需要实现如下功能:
1.1某网络协议要求结构体成员四字节对齐和使用小印第安模式,依照此规则定义一个结构体TUserInfo,包含以下成员:用户名(31字节字符串)、密码(31字节字符串)、地址(63字节字符串)、端口(2字节短整型)。
1.2消息头结构体定义:命令字(1字节)、数据长度(4字节整型)。
1.3客户端程序开一个线程,定义一个TUserInfo的对象usrInfo并初始化,地址为服务器机器IP,端口为4477,用户名为自己的名字,密码为Software;然后将usrInfo加消息头(命令字为0x01)每隔10s发送给usrInfo中的地址和端口。
1.4服务器程序开一个线程,接受客户端向4477端口发来的消息,对0x01消息分析其数据(TUserInfo),打印出每次收到的用户名、密码、地址和端口等信息。在时间充裕的情况下可以考虑多客户端同时连接的情况。
要怎么处理头部1个字节的整数转化成字符串需要3个字节
服务器端:
#include#include #include #pragma comment(lib, "ws2_32.lib") #include struct TUserInfo { char name[31]; char mima[31]; char address[63]; short port; }; struct messageHeader { char mingling; int lenData; }; const int cmdLen = 1, dataLen = 4; const int nameLen = 31, miamaLen = 31, addressLen = 63, portLen = 2; using namespace std; void sendMessage() { //初始化dll WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); //创建套接字 SOCKET servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); //绑定套接字 struct sockaddr_in sockAddr; memset(&sockAddr, 0, sizeof(sockAddr));//每个字节都用0填充 sockAddr.sin_family = PF_INET; sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //InetPton(AF_INET, (LPCTSTR)"127.0.0.1", &sockAddr.sin_addr.S_un.S_addr); sockAddr.sin_port = htons(4477); bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)); //进入监听状态 listen(servSock, 20); SOCKADDR clintAddr; int nSize = sizeof(SOCKADDR); char szBuffer[MAXBYTE] = { 0 }; while (1) { SOCKET clntSock = accept(servSock, (SOCKADDR*)&sockAddr, &nSize); //接收客户端发送来的数据 int strLen=recv(clntSock, szBuffer, MAXBYTE, NULL); //如果命令字是1,就打印打印出每次收到的用户名、密码、地址和端口等信息 int cmd = szBuffer[0]-'0'; if (cmd == 1) { cout << "name: "; for (int i = cmdLen + dataLen; i < cmdLen + dataLen + nameLen; i++) { if (szBuffer[i] == ' ') continue; cout << szBuffer[i]; } cout << " mima:"; for (int i = cmdLen + dataLen + nameLen; i < cmdLen + dataLen + nameLen + miamaLen; i++) { if (szBuffer[i] == ' ') continue; cout << szBuffer[i]; } cout << " address:"; for (int i = cmdLen + dataLen + nameLen + miamaLen; i < cmdLen + dataLen + nameLen + miamaLen + addressLen; i++) { if (szBuffer[i] == ' ') continue; cout << szBuffer[i]; } cout << " port:"; for (int i = cmdLen + dataLen + nameLen + miamaLen + addressLen; i < 150; i++) { if (szBuffer[i] == ' ') continue; cout << szBuffer[i]; } cout << endl; } //关闭套接字 closesocket(clntSock); memset(szBuffer, 0, MAXBYTE); } closesocket(servSock); //终止dll的使用 WSACleanup(); } int main() { thread mythread(sendMessage); mythread.join(); std::cout << "主线程执行" << std::endl; system("pause"); return 0; }
客户端:
#include网络协议 1TCP通信#include #include #pragma comment(lib, "ws2_32.lib") #include #include struct TUserInfo { char name[31]; char mima[31]; char address[63]; short port; }; struct messageHeader { char cmd; int lenData; }; const int cmdLen = 1, dataLen = 4; const int nameLen = 31, miamaLen = 31, addressLen = 63, portLen = 2; using namespace std; void sendMessage() { TUserInfo wyb = { "wyb","software","127.0.0.1",4477 }; //初始化ddl WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); //向服务端发起请求 struct sockaddr_in sockAddr; memset(&sockAddr, 0, sizeof(sockAddr)); sockAddr.sin_family = PF_INET; sockAddr.sin_addr.S_un.S_addr = inet_addr(wyb.address); sockAddr.sin_port = htons(wyb.port); //客户端发送数据 char str[150] = { ' ' }; for (int i = 0; i < cmdLen; i++) str[i] = '1'; for (int i = cmdLen + dataLen; i < cmdLen + dataLen + nameLen; i++) str[i] = wyb.name[i - (cmdLen + dataLen)]; for (int i = cmdLen + dataLen + nameLen; i < cmdLen + dataLen + nameLen + miamaLen; i++) str[i] = wyb.mima[i - (cmdLen + dataLen + nameLen)]; for (int i = cmdLen + dataLen + nameLen + miamaLen; i < cmdLen + dataLen + nameLen + miamaLen + addressLen; i++) str[i] = wyb.address[i - (cmdLen + dataLen + nameLen + miamaLen)]; string tmp = to_string(wyb.port); for (int i = cmdLen + dataLen + nameLen + miamaLen + addressLen; i < cmdLen + dataLen + nameLen + miamaLen + addressLen + tmp.size(); i++) str[i] = tmp[i - (cmdLen + dataLen + nameLen + miamaLen + addressLen)]; while (1) { //创建套接字 SOCKET clntSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); connect(clntSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)); send(clntSock, str, 150, NULL); Sleep(10000); closesocket(clntSock); } //终止使用ddl WSACleanup(); //system("pause"); } int main() { thread mythread(sendMessage); mythread.join(); std::cout << "主线程执行" << std::endl; system("pause"); return 0; }
TCP是面向连接、可靠传输、面向字节流的传输层协议
服务端操作流程
(1)创建套接字接口:在内核中创建socket结构体,关联进程与网卡联系
(2)为套接字绑定地址信息:五元组{源IP,目标IP,源端口,目标端口,协议},必须主动绑定
(3)监听:只有处于监听状态的套接字才会接受客户端的连接请求,服务端为每个客户端请求创建一个新的socket结构体,通过这个结构体与客户端通信
(4)获取socket的句柄
(5)收发数据
(6)关闭套接字
客户端流程:
(1)创建套接字
(2)为套接字绑定地址信息,不推荐主动绑定地址,降低端口冲突的概率
(3)向服务端发起连接请求
(4)收发数据
(5)关闭套接字
socket 是计算机之间通信的一种约定或方式
windows区分socket和文件,Windows把socket当作一个网络连接对待
(1)流格式套接字 SOCK_STREAM:
a. 可靠的、双向的通信数据流:使用了TCP协议(The Transmission Control Protacol)
b. 顺序传输,
c. 数据的发送和接收不同步:流格式套接字内部有一个缓冲区,socket传输的数据保存到这个缓冲区,接收的数据有可能在缓冲区满后一次读取,也可能分好几次读取
应用:HTTP, FTP
(2)数据宝格式套接字SOCK_DGRAM
计算机只管传输数据,不作数据校验,传输效率高
a. 强调快速,非顺序,数据可能丢失或销毁,使用UDP协议(User Datagram Protocol)
b. 限制每次传输数据大小
c.数据的发送和接收是同步的
应用:DNS,即时聊天工具
OSI模型把网络通信工作分为七层:物理层,数据链路层,网络层,传输层,会话层,表示层,应用层
TCP/IP 模型四层:网络接口层,网际层,传输层,应用层
socket编程是在传输层基础上
计算机通信时需要遵循原则:
同一层通信,每层功能相同,数据逐层传输,每一层可以使用下层提供的服务并且向上层提供服务
(1)IP地址(Internet Protocol Address):通常一个局域网拥有一个IP地址
(2)MAC地址(Media Access Control Address):也称局域网地址,以太网地址,物理地址,唯一标识一台计算机
(3)端口号(Port Number):计算机为每个网络程序分配一个独一无二的端口号
(1)socket()函数创建套接字:
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);socket 程序演示
下面的程序客户端可以接收来自服务端发送的消息,
要先打开server.exe,再打开client.exe,否则收不到;要打开工程属性->C/C++常规->关闭sdl检查,否则代码sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
会发生警告 C4996 ‘inet_addr’: Use inet_pton() or InetPton() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings client82
server.cpp
#include#include //#include #pragma comment(lib, "ws2_32.lib") int main() { //初始化dll WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); //创建套接字 SOCKET servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); //绑定套接字 struct sockaddr_in sockAddr; memset(&sockAddr, 0, sizeof(sockAddr));//每个字节都用0填充 sockAddr.sin_family = PF_INET; sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //InetPton(AF_INET, (LPCTSTR)"127.0.0.1", &sockAddr.sin_addr.S_un.S_addr); sockAddr.sin_port = htons(2850); bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)); //进入监听状态 listen(servSock, 20); //接收客户端请求 SOCKADDR clintAddr; int nSize = sizeof(SOCKADDR); SOCKET clntSock = accept(servSock, (SOCKADDR*)&sockAddr, &nSize); //向客户端发送数据 const char* str = "hello wyb!n"; send(clntSock, str, strlen(str) + 1, NULL); //关闭套接字 closesocket(servSock); closesocket(clntSock); //终止dll的使用 WSACleanup(); return 0; }
client.cpp
#include#include #pragma comment(lib, "ws2_32.lib") int main() { //初始化ddl WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); //创建套接字 SOCKET clntSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); //向服务端发起请求 struct sockaddr_in sockAddr; memset(&sockAddr, 0, sizeof(sockAddr)); sockAddr.sin_family = PF_INET; sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); sockAddr.sin_port = htons(2850); connect(clntSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)); //接收服务器传回的数据 char szBuffer[MAXBYTE] = { 0 }; recv(clntSock, szBuffer, MAXBYTE, NULL); //输出接收到的数据 printf("message from server: %sn", szBuffer); //关闭套接字 closesocket(clntSock); //终止使用ddl WSACleanup(); system("pause"); return 0; }
一博实在太帅了,又福气旺旺



