服务器接收数据:
//开启服务 按钮响应函数
void CTcpServerDlg::OnButtonListen()
{
// TODO: Add your control notification handler code here
if(SOCKET_ERROR == listen(sockSrv, 5))
{
MessageBox("侦听失败!", "提示", MB_ICONERROR | MB_OKCANCEL);
return;
}
while(TRUE)
{
m_sockClient = accept(sockSrv, (SOCKADDR*)&addrClient, &m_nAddLength);
if(m_sockClient == INVALID_SOCKET)
{
return ;
}
HANDLE hRecvThread;
hRecvThread = CreateThread(NULL, 0, recvProc, (LPVOID)this, 0, NULL);
SetDlgItemText(IDC_EDIT_SEND, m_strRecv);
CloseHandle(hRecvThread);
}
}//线程函数
DWORD WINAPI CTcpServerDlg::recvProc(LPVOID lpParameter)
{
SOCKET sockClient = ((CTcpServerDlg*)lpParameter)->m_sockClient;
SOCKET sock = ((CTcpServerDlg*)lpParameter)->sockSrv;
int len = sizeof(SOCKADDR); char recvbuff[1024] = {0};
CString strRecv = "";
int recvError = 0;
recvError = recv(sockClient, recvbuff, sizeof(recvbuff), 0);
if(recvError == SOCKET_ERROR)
{
return 0;
}
strRecv += recvbuff;
send(sockClient, (char*)"Server received data successfully", sizeof("Server received data successfully"), 0);
((CTcpServerDlg*)lpParameter)->m_strRecv = strRecv;
return 1;
}为什么实现不了啊,哪里有毛病,麻烦给指正一下,谢谢了。
//开启服务 按钮响应函数
void CTcpServerDlg::OnButtonListen()
{
// TODO: Add your control notification handler code here
if(SOCKET_ERROR == listen(sockSrv, 5))
{
MessageBox("侦听失败!", "提示", MB_ICONERROR | MB_OKCANCEL);
return;
}
while(TRUE)
{
m_sockClient = accept(sockSrv, (SOCKADDR*)&addrClient, &m_nAddLength);
if(m_sockClient == INVALID_SOCKET)
{
return ;
}
HANDLE hRecvThread;
hRecvThread = CreateThread(NULL, 0, recvProc, (LPVOID)this, 0, NULL);
SetDlgItemText(IDC_EDIT_SEND, m_strRecv);
CloseHandle(hRecvThread);
}
}//线程函数
DWORD WINAPI CTcpServerDlg::recvProc(LPVOID lpParameter)
{
SOCKET sockClient = ((CTcpServerDlg*)lpParameter)->m_sockClient;
SOCKET sock = ((CTcpServerDlg*)lpParameter)->sockSrv;
int len = sizeof(SOCKADDR); char recvbuff[1024] = {0};
CString strRecv = "";
int recvError = 0;
recvError = recv(sockClient, recvbuff, sizeof(recvbuff), 0);
if(recvError == SOCKET_ERROR)
{
return 0;
}
strRecv += recvbuff;
send(sockClient, (char*)"Server received data successfully", sizeof("Server received data successfully"), 0);
((CTcpServerDlg*)lpParameter)->m_strRecv = strRecv;
return 1;
}为什么实现不了啊,哪里有毛病,麻烦给指正一下,谢谢了。
DWORD WINAPI recvProc(LPVOID lpParameter)没调试,不知道你具体问题在哪里,不过从你这个框架上看,按钮按下去了界面也就死了
TcpServer.obj : error LNK2001: unresolved external symbol "public: virtual __thiscall CTcpServerApp::~CTcpServerApp(void)" (??1CTcpServerApp@@UAE@XZ)
Debug/TcpServer.exe : fatal error LNK1120: 1 unresolved externals什么原因?
建议在线程中调用accept的循环等待或者用异步通讯(select方式)
recvError = recv(sockClient , recvbuff, sizeof(recvbuff), 0);
可能要这样用recvError = recv(m_sockClient, recvbuff, sizeof(recvbuff), 0);
在 MFC 中,一般用 AfxBeginThread 函数创建并运行线程。
简单问一下吧,服务器那个“开启服务”按钮的响应函数该怎么写啊?
2,accept()和recerve()放在一个循环里面么?
谢谢了。
>>当线程创建过程的代码执行完之后,相关代码就执行完成了,不会有等待。
2,accept()和recerve()放在一个循环里面么?
>>accept是在一个listening Socket当中accept连接,receive是接收accepted scoket当中的数据,两两不相关系,如果你在把accept和receive放同一线程当中,则你accept到一个连接之后就进入到receive当中去了,此时没有再次调用accept,新的连接是连不上来的。当然也可以使用多多个线程如此去各自accept一个连接,然后进行receive/send.
谢谢你这么耐心
大致看了一下你的代码,accept循环是过不去的,除非你closesocket(sockSrv),如果你要帧听多个连接,那你就得另开一个线程进行accept,否则,当你accept到一个有效的连接后,你就得break掉accept的循环进行下面的工作,你不防用工具看看你的线程是否创建成功了,任务管理器中把线程计数打开,你会发现你的recv线程是没开启的
另外,MFC中不要用CreateThread创建线程,看看MSDN吧,CreateThread在MFC中有memory leak,用_beginthread 或者_beginthreadex,但记得要_endthread或者_endthreadex
谢谢你这么宝贵的意见。
{
m_sockClient = accept(sockSrv, (SOCKADDR*)&addrClient, &m_nAddLength);
if(m_sockClient == INVALID_SOCKET)
{
return ;
}
HANDLE hRecvThread;
hRecvThread = CreateThread(NULL, 0, recvProc, (LPVOID)this, 0, NULL);
SetDlgItemText(IDC_EDIT_SEND, m_strRecv);
CloseHandle(hRecvThread);
}
这段代码也得放到一个线程中去,否则accpet的时候主线程就阻塞了,你的程序界面自然就没反应了,其余的地方没仔细看,估计问题也都不大,注意处理recv返回0的情况,返回0是表示socket已经关闭了。
SOCKET应该是在程序关闭的时候才关闭吧,如果不处理返回0的情况,会发生什么后果呢?
返回0是对方已经关闭socket,所以你再对此socket进行IO操作都是惘然
如果是在做项目,建议你在继续工作之间先巩固一下基础知道,再把多线程以及线程安全方面的知识吃透了再继续,否则你写出来的的程序得不到任何保障
如果纯粹是学习,而且对程序开发感兴趣,那就更应该把基本功打扎实!recv线程的回调函数跟你创建线程的数量没有关系,不管你是1个线程还是1000个线程,只是每次创建线程时,Caller会从你程序的代码段中找此回调函数并交给新创建的线程,你的程序中有很多地方需要修改,比方说你的m_sockClient,如果你接收了第二个连接,那第一连接的套接字描述符就被覆盖了,你就没办法再对其进行任何操作了
你应该为每个连接创建一个客户端对象,然后把套接字描述符保存在此客房端对象中
class CClient
{
public:
CClient(SOCKET sock) {m_sock = sock;}
~CClient(); // ...
SOCKET m_sock;
};
// listening (in accept thread)
while(Listening)
{
SOCKET sock = accept(...);
// validity checking
if (sock == INVALID_SOCKET || sock == SOCKET_ERROR) continue; CClient *pClient = new CClient(sock);
// add the client to the client array
// ... // Create worker thread (Attention: pClient object used here)
HANDLE hWorker = CreateThread(NULL, 0, recvProc, (LPVOID)pClient, 0, NULL);
CloseHandle(hWorker);
}// 线程函数(此处你把此函数作为类CTcpServerDlg的成员函数,不行吧,如果确实要这样使用,那你在头文件中需要将其定义成static)
DWORD WINAPI CTcpServerDlg::recvProc(LPVOID lpParameter)
{
CClient *pClient = (CClient *)lpParameter; // 进行你的操作: recv()/send()
// 其套接字描述符为pClient->m_sock
// ...
return 0;
}
// 正是因为用到了lpParameter,所以recvProc()才知道自己要干什么,具体操作哪个socket对象,如果你再不明白,那我真没办法了
可以结帖了,还想问一下,我想把recvProc线程里收到的字串显示在主窗口的EDIT控件里,请问该怎么做?
p->SetWindowText()吗?线程里这样用,UI上会对这个控件更新显示么?
线程安全机制是不是就是使用同步呢?
然后再请教一个问题吧:
对话框中点击一个“连接”按钮,弹出一个对话框,设置服务器IP,PORT,
然后我打算开一个线程去连接这个服务器,请问这个线程写在“连接”的按钮事件中呢,还是模式对话框的OnOK()中呢。我是写在了“连接”中,当在对话框中点CANCEL按钮的时候,本来是没填IP什么的,可是 int nRet = connect(*psock, (SOCKADDR*)&addr, sizeof(SOCKADDR));
if(SOCKET_ERROR == nRet)
{
AfxMessageBox("连接超时!");
return 0;
}
这个对话框还是弹出来了,是不是我线程开的地方不对,或者是有什么办法解决吗?谢谢。
把线程去掉,直接调用你的代码即可,在弹出的对话框中加一个标志,用于保存用户是点击OK还是Cancel,如果是OK则执行连接线程安全是一个挺烦琐的东西,不等同于同步,简单地说吧,比如你开了一块内存空间,LPBYTE pBuf = new BYTE[32]; 在多个线程中都有这块内存的指针,当其中一个线程正往pBuf所指向的内存空间写数据时另一线程也发起对pBuf的写操作,这时就会出现内存错误,程序基本上崩溃,所以你一定要加一个保护锁,在有线程操作(写)pBuf时,其它有写操作的线程都挂起排队,直到获得前一线程写完数据的通知才继续,如果有多个线程在等待,那一次也只能放一个线程,多线程同时读操作不会导致内存错误,但数据的完成性不能够保证,比如一线程在写pBuf时,另一线程开始读操作,内存的读写操作是遵循一定顺序进行的,而且读操作比写操作要快,此时就可能出现你读出的数据一半(比例不一定)是之前的旧数据,一半是写线程刚写的新数据,所以读写操作也是要进行保护的,保护的方法很多,临界区、信号灯、Event、Mutex等都可以进行多线程保护以达到线程安全
以上希望对你有所帮助...
E-MAIL:[email protected]
在你的EDIT控件里用向导加个成员变量m_csOutPut,然后把接收到的BUFF赋给m_csOutPut, 即可显示.
问题,你的多线程做完了吗?
如果问题解决了,写一下你解决的思路吧
我也遇到同样的问题了
Sky_huang(Sky)说地很清楚了。
还是自己动手写一下吧。
我的程序必须做成多线程或是异步处理,不然不能多客户端与服务器通时通信
问题:
1.在服务端accept的时候用多线程吗?
2.在连接时给不同的客户端如何创建不同的SOKECT?
提供一下思路吧
这个贴子我研究了两天了.
#define WM_FD_ACCEPT WM_USER+1
#define WM_FD_RECEIVE WM_USER+2
#define WM_FD_CLOSE WM_USER+3#include "stdafx.h"
#include "afxsock.h"class CNewSocket : public CSocket
{
// Attributes
public:// Operations
public:
CNewSocket(HWND hwnd);
virtual ~CNewSocket();// Overrides
public: // ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CNewSocket)
//}}AFX_VIRTUAL virtual void OnClose(int nErrorCode);
virtual void OnReceive(int nErrorCode);
virtual void OnAccept(int nErrorCode); // Generated message map functions
//{{AFX_MSG(CNewSocket)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
private:
HWND m_hWnd;// Implementation
protected:
};//////////////////*CNewSocket.cpp*//////////////////////
#include "stdafx.h"
#include "CNewSocket.h"CNewSocket::CNewSocket(HWND hwnd)
{
m_hWnd = hwnd;
}CNewSocket::~CNewSocket()
{
}void CNewSocket::OnAccept(int nErrorCode)
{
AfxMessageBox(_T("Sure to Connect ?"));
PostMessage(m_hWnd, WM_FD_ACCEPT, 0, 0);
CSocket::OnAccept( nErrorCode );
}void CNewSocket::OnReceive(int nErrorCode)
{
// AfxMessageBox(_T("Dates will send to the server!"));
PostMessage(m_hWnd, WM_FD_RECEIVE, 0, 0);
CSocket::OnReceive( nErrorCode );
}void CNewSocket::OnClose(int nErrorCode)
{
PostMessage(m_hWnd, WM_FD_CLOSE, 0, 0);
CSocket::OnClose( nErrorCode );
}
// CCSmodeDlg message handlersBOOL CCSmodeDlg::OnInitDialog()
{
CDialog::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
} // Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here
m_nStatus = -1;
m_pServerSocket = NULL;
m_pClientSocket = NULL;
m_csConnect = "";
m_IpAddress = "127.0.0.1";
m_Port = 7897;
//GetDlgItem(IDC_BtnReceiveClear)->EnableWindow(FALSE);
//GetDlgItem(IDC_BtnSendClear)->EnableWindow(FALSE);
return TRUE; // return TRUE unless you set the focus to a control
}void CCSmodeDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.void CCSmodeDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CCSmodeDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}LRESULT CCSmodeDlg::OnReceive(WPARAM wParam,LPARAM lParam)
{ TCHAR *buff = new TCHAR[1024 * 1024];
memset(buff, 0, 1024 * 1024);
int n = m_pClientSocket->Receive(buff , 1024* 1024);
m_csInPut = buff;
delete []buff;
//m_pServerSocket->Socket()RemoteAddress
UpdateData(FALSE);
return S_OK;
}
LRESULT CCSmodeDlg::OnAccept(WPARAM wParam,LPARAM lParam)
{
//m_csConnect += "OK!";
m_csConnect = "Already Conntected";
UpdateData(FALSE);
m_pClientSocket = new CNewSocket(this->m_hWnd);
m_pServerSocket->Accept(*m_pClientSocket);
return NULL;
}LRESULT CCSmodeDlg::OnClose(WPARAM wParam, LPARAM lParam)
{
if (1 == m_nStatus)
{
m_csConnect = "Client";
}
else
{
m_csConnect = "Server";
}
m_csConnect += " has disconnected";
UpdateData(FALSE);
return NULL;
}void CCSmodeDlg::SocketReset()
{
if (m_pServerSocket != NULL)
{
delete m_pServerSocket;
m_pServerSocket = NULL;
} if (m_pClientSocket != NULL)
{
delete m_pClientSocket;
m_pClientSocket = NULL;
} m_csConnect = "";
UpdateData(FALSE);
}void CCSmodeDlg::OnBnClickedBtnstart()
{
// TODO: Add your control notification handler code here
m_nStatus = 1;
if (m_pServerSocket != NULL) //服务器Socket是否已经创建
{
m_csConnect = "Please disconnect!";
UpdateData(FALSE);
}
else
{
m_csConnect = "Wait for client...";
UpdateData(FALSE);
if (!AfxSocketInit()) //Socket初始化
{
MessageBox(_T("WindowSocket initial failed!", "Send", MB_ICONSTOP));
return;
}
//将Socket与窗口关联
m_pServerSocket = new CNewSocket(this->m_hWnd);
//如果创建Socket失败则提示,成功则开始监听
if (!m_pServerSocket->Create(m_Port))
{
MessageBox(_T("SendSocket Create failed!", "Send", MB_ICONSTOP));
}
else
{
m_pServerSocket->Listen();
}
}
}void CCSmodeDlg::OnBnClickedBtnconnect()
{
// TODO: Add your control notification handler code here
m_nStatus = 0;
if (m_pClientSocket != NULL) //判断客户端Socket是否已经创建
{
m_csConnect = "Please disconnect!";
UpdateData(FALSE);
}
else
{
m_csConnect = "Connect to the server...";
UpdateData(FALSE);
if (!AfxSocketInit())
{
MessageBox(_T("WindowSocket initial failed!", "Receive", MB_ICONSTOP));
return;
}
m_pClientSocket = new CNewSocket(this->m_hWnd);
if(!m_pClientSocket->Create())
{
MessageBox(_T("ReceiveSocket create failed!","Receive",MB_ICONSTOP));
return;
}
else
{
if (!m_pClientSocket->Connect(m_IpAddress, m_Port))
{
CString strTmp = m_csConnect;
SocketReset();
m_csConnect = strTmp;
m_csConnect += "Error!";
UpdateData(FALSE);
}
else
{
m_csConnect += "OK!";
}
UpdateData(FALSE);
}
}
}void CCSmodeDlg::OnBnClickedBtnsend()
{
// TODO: Add your control notification handler code here
UpdateData(TRUE);
if(m_pClientSocket != NULL)
this->m_pClientSocket->Send(m_csOutPut.GetBuffer(0), m_csOutPut.GetLength() * sizeof(TCHAR));
else
MessageBox(_T("The connection hasn't built yet!"));
}void CCSmodeDlg::OnBnClickedBtnsendclear()
{
// TODO: Add your control notification handler code here
m_csOutPut = "";
UpdateData(FALSE);
}void CCSmodeDlg::OnBnClickedBtnreceiveclear()
{
// TODO: Add your control notification handler code here
m_csInPut = "";
UpdateData(FALSE);
}void CCSmodeDlg::OnBnClickedBtndisconnect()
{
// TODO: Add your control notification handler code here
SocketReset();
m_csConnect = "Disconnect!";
UpdateData(FALSE);
}void CCSmodeDlg::OnBnClickedBtnend()
{
// TODO: Add your control notification handler code here
SocketReset();
m_csConnect = "Server";
m_csConnect += " has disconnected";
UpdateData(FALSE);
}界面没法贴了.我想在此基础上做出如下改进:
A与B连接后C又与A连接,A能同时与BC直接互通消息,且B不必先断开后再连接
求思路方法!
另祝中秋快乐