这个是当时整理的资料,就不整理了。。。。。
虚拟机IP地址:192.168.109.128
箭头左边是指针,点左边是 变量;
VS同时打开多个项目:
文件-》添加-》现有项目即可;
可以shift将两个控件选中后,在工具链选中上对齐,右对齐之类的。
Password控件属性password选为true,以圆点形式出现。
添加一个对话框,需要添加一个类来找到他。添加完类以后,需要创建窗口,在APP的init里面添加头文件,创建模态窗口。Dlg.domodle();
assert函数用来不满足条件就终止程序执行!
Csocket内部模式:(为什么不用创建线程)
- 阻塞模式:
Serve端与客户端处于同步的状态下。在 while循环里不断accept等待客户端的连接,如果在主线程里就会阻塞,需要创建新的线程!
- 非阻塞模式:
基于socket的消息机制,server端与client端的通信处于异步状态,利用 CSocketWnd 作为所有 sockets 的消息池,是实现 socket 事件 的消息机制的关键技术。
Server 端 socket 正在接收来自 Client 端 socket 连接请求,这将会触发 FD_ACCEPT 事件,同时 Server 端的 网络传输服务进程 向 Server 端的 socket window (CSocketWnd )发送事件通知消息 WM_SOCKET_NOTIFY , 通知有 FD_ACCEPT 事件产生 , CsocketWnd 在收到事件通知消息后,调用消息处理函数 OnSocketNotify:
项目梳理:
服务器端:
- 在initinstance里面,加载动态库 ,建立监听socket,,listen,注册页面
- Cserversocket 是服务器端监听的socket,重载了onaccept函数,(新建了一个与客户端通信的socket,把这个socket放入队列里;accept函数:第一个是 服务器端与客户端通信的socket的指针,第二个参数是客户端的地址结构体,第三个参数是地质结构体长度的指针,返回的是实际接收到地址的长度。三个参数均为输出型参数!)
- CClientsocket是服务器与客户端通信的socket。重载了onreceive函数:根据收到的消息类型,进行不同的处理:
登录:更新日志,在线列表,通知其他客户端更新列表;
OnloginIN函数来进行相关处理 :
更新日志:
NetcharserverDLG的displaylog函数更新;
更新在线列表:
Updateserver函数:遍历list函数,保存下所有客户端的名字;调用serverDLG的updateuserinfo函数进行更新;
通知其他客户端更新列表:
构造一个update的报文(头是那样的,内容为空),fromuser还是那个登录的人,遍历那个list给每一个客户端都发送这个消息;
发送消息:根据touser进行转发:
关闭的时候,发送更新报文,更新日志,在线列表;通知其他人更新;
更新日志:
Ondisplay函数 进行处理;
更新服务器在线名单:
从list 中删掉这个节点!调用updateserverlog函数!
通知其他客户端更新列表:
传给Updateuuser函数所有用户在线名单和from user(为“0”)
客户端:创建 ,连接,发送,接收!
- app文件里的initinstance()函数里添加 动态链接库 ,create套接字(默认分配端口,tcp类型)
注册登录窗口,
注册聊天窗口!
- loginDLG文件:
点击登录按钮:
与服务器 进行连接connect函数;
发送登录报文;
Mfc:
先修改字符集格式:项目->属性->高级->字符集->多字符集
1.修改窗口大小:在mainframe里oncreatewindow里cs.cx,cs.cy;
2.添加菜单:资源视图里menu直接点击添加,写名字。
在dialog里添加资源,画新窗口。添加类。回到menu里,添加事件处理程序,选择在mainframe里,先引入头文件,在函数里面写:cadddlg dlg; dlg.domodal();
- int与char数组之间的转换:_itoa_s函数。
- 计算器:给button绑定函数:获取编辑框的值,进行运算:
char ch1[10], ch2[10], ch3[10];
int num1, num2, num3;
GetDlgItem(IDC_EDIT1)->GetWindowText(ch1, 10);
GetDlgItem(IDC_EDIT2)->GetWindowText(ch2, 10);
num1 = atoi(ch1);
num2 = atoi(ch2);
num3 = num1 + num2;
_itoa_s(num3,ch3,10);
GetDlgItem(IDC_EDIT3)->SetWindowText(ch3);
//UpdateData(false);
也可以给编辑框添加变量:
UpdateData(true);//是把编辑框中的值传给变量
result = num1 + num2;
UpdateData(false);//把变量的值传给编辑框
QQ聊天程序 :
服务器端:
记录谁登陆了,谁离开了;
转发消息;
更新在线列表;
更新日志;
创建套接字:
WSAData wsData;
if(!AfxSocketInit(&wsData)) //加载套接字动态库;
{
AfxMessageBox(_T("Socket 库初始化出错!"));
return false;
}
//创建服务器端Socket、采用TCP
m_iSocket = new CServerSocket();
if(!m_iSocket)
{
AfxMessageBox(_T("动态创建服务器套接字出错!"));
return false;
}
if(!m_iSocket->Create(8989)) //创建套接字,create有三个参数:1.端口号,若为0 或 不 写, 由操作系统随便指定;
2.socket—stream类型 还是 socket_dGram类型,默认是stream类型 ;
3.IP地址
自己会调用bind函数进行监听!
{
AfxMessageBox(_T("创建套接字错误!"));
m_iSocket->Close();
return false;
}
if(!m_iSocket->Listen()) //默认最多同时5个来连接 !
{
AfxMessageBox(_T("监听失败!"));
m_iSocket->Close();
return false;
}
功能 一:更新在线列表:
知识点:Clistbox列表框(对应控件list control)
CListBox* pList = (CListBox*)GetDlgItem(IDC_LT_ONLINE);
有如下 函数:
CListBox::ResetContent//清空组合框内容void ResetContent( );
CListBox::AddString//添加lpszString 至组合框尾部int AddString( LPCTSTR lpszString );
CListBox::DeleteString//删除nIndex行int DeleteString( UINT nIndex );
CListBox::InsertString //在nIndex行后,插入行int InsertString( int nIndex, LPCTSTR lpszString );
CListBox::SelectString //可以选中包含指定字符串的行int SelectString( int nStartAfter, LPCTSTR lpszString );
CListBox::FindString //可以在当前所有行中查找指定的字符传的位置,nStartAfter指明从那一行开始进行查找。 int FindString( int nStartAfter, LPCTSTR lpszString ) const;
CListBox::GetCount //获取行数int GetCount( ) const;
CListBox::GetCurSel//获取当前选中行的行号int GetCurSel( ) const;
CListBox::SetCurSel(n)//设置第n行内容为显示的内容int SetCurSel( int nSelect );
int CListBox::SetItemHeight( int nIndex, UINT cyItemHeight );//设置一个项的高度,注意:列表框具有//LBS_OWNERDRAWVARIABLE风格才可以单独设置一个项的高度,否则是所有项的高度//------------------------上述函数与 CListBox::与CComboBox::::几乎相同
int CListBox::GetText( int nIndex,CString &rString ) const;//根据索引获得项文本 类似CComboBox::GetLBText
不知道哪来的struserinfo(遍历保存客户socket的list,用#分割!),他应该包含了所有在线用户的姓名,并用#分割;
pList->ResetContent(); //清除所有的数据
while(!strUserInfo.IsEmpty())
{
int n = strUserInfo.Find('#'); //查找第一次出现#的位置,读取第一次数据
strTemp = strUserInfo.Left(n);
//strArray.Add(strTemp);
pList->AddString(strTemp); //加入数据
strUserInfo = strUserInfo.Right(strUserInfo.GetLength()-n-1); //减去第一个的位置
}
服务器接受连接后,把创建的用于和用户数据收发的套接字 保存在一个队列里;用于数据连接的套接字应该是有消息发送过来的时候触发receive函数,收到后,根据头的类型进行不同的操作:
登录 :更新日志和在线列表,转发给其他用户;
更新日志:((CEdit*)GetDlgItem(IDC_ET_INFO))->ReplaceSel(strInfo);
知识点:replacesel函数本来为替换函数,将选中的文本替换为strinfo,但是如果没有选择的文本,就讲strinfo插入现在光标所在的位置!
发送消息:根据touser转发给相应用户。
退出:更新日志和在线列表,转发给其他用户.
更新信息:这种报文一定是由服务器发出的!当客户登录和退出服务器都会发送这样的报文!
当有一个客户socket要关闭时,会触发onclose函数,在关闭之前要 发送更新信息通知其他客户端和服务器端来更新列表。
这个客户端的名字是怎么保存进去的?
服务器端的结构:
App文件里:
一个用于监听的socket:m_ISocket
inITinstance里: 加载socket动态库,create,listen;
声明窗口!
Cserversocket:
一个用于保存客户端socket的list;
重写onaccept函数:存进list;
Cclientsocket :
重写recevice函数:
根据收到的消息:
登录:更新服务器的log和 列表,通知客户端更新列表 ;
转发:根据to user遍历list转发给相应用户!
退出:触发close函数,更新服务器和客户端;
ServerDlg: 更新在线列表;
win32:
#include <Windows.h>
#include <stdio.h>
LRESULT CALLBACK WinProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
int WINAPI WinMain(
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow // show state
){
WNDCLASS wndcls;
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
wndcls.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
wndcls.hCursor=LoadCursor(NULL,IDC_CROSS);
wndcls.hIcon=LoadIcon(NULL,IDI_QUESTION);
wndcls.hInstance=hInstance;
wndcls.lpfnWndProc=WinProc;
wndcls.lpszClassName="njust";
wndcls.lpszMenuName=NULL;
wndcls.style=CS_VREDRAW|CS_HREDRAW;
RegisterClass(&wndcls);
HWND hwnd= CreateWindow("njust","test",WS_OVERLAPPEDWINDOW,100,100,300,200,NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,SW_SHOWNORMAL);
UpdateWindow(hwnd);
MSG msg;
while(GetMessage(&msg,NULL,0,0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WinProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
){
switch(uMsg){
case WM_CLOSE:
if(IDYES==MessageBox(hwnd,"really exit?","tip",MB_YESNO))
PostQuitMessage(0);
break;
case WM_CHAR:
char sChar[50];
sprintf(sChar,"char is %d",wParam);
MessageBox(hwnd,sChar,"tip",MB_OK);
break;
case WM_LBUTTONDOWN:
MessageBox(hwnd,"WM_LBUTTONDOWN","tip",MB_OK);
break;
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
}
#include <Windows.h>
//using namespace std;
LRESULT CALLBACK test(HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter);
{
return 0;
}
//命令行参数和状态
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance, LPSTR IpCmdLine, int nCmdShow) {
//设计
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.cbWndExtra = 0;
// wndclass.cbSize = 1000;
wndclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
wndclass.hCursor = LoadCursor(NULL, IDC_SIZEWE);//instance为什么是null?
wndclass.hIcon = LoadIcon(NULL, IDI_WARNING);//同上;
wndclass.hInstance = hinstance;
wndclass.lpfnWndProc = test;
wndclass.lpszClassName = "catty";
wndclass.lpszMenuName = NULL;
//注册窗口类
RegisterClass(&wndclass);
//创建
char* lpClassName = "firstwin32";
char* lpWindowName = "test";
HWND hwnd = CreateWindow(lpClassName, lpWindowName, WS_TILEDWINDOW, 0, 0, 500, 500, NULL, NULL, hinstance, NULL);
//更新显示
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);