MFC 쓰레드 실행중에 출력하는법이 있나요?

l595659의 이미지

현재 winsock으로 TCP/UDP 통신을 만들고 있습니다.

쓰레드를 다른 파일의 cpp파일에 구현을 해서 dlg.cpp에서 AfxbeginThread하여 실행을 시킨 상태입니다.

문제는 recv하는 값은 잘 들어오는데 이것을 ListBox에 뿌리는 방법을 모르겠습니다.

포인터를 쓰면 뺄 수 있다는데 AddString하고 포인터로 해도 출력이 안되더라구요

어떤 방식으로 해야 출력을 시킬 수 있을까요? 또, 클라이언트가 접속 했을 때 역시 접속했다고 출력을 시켜야하는데 이것도 문제가 되네요 쓰레드를 하나 더 실행시켜서 뽑아보려고 했는데 이것도 안되네요

하단에 쓰레드 두개와 쓰레드 실행부분 올려보겠습니다
쓰레드1

UINT ThreadTcpComm(LPVOID pVoidClass)
{
	TCPServer tcpserver;
	tcpserver.IniInfo(_T("./NetworkPath.ini"), _T("ServerInfo"), _T("Port"));
	tcpserver.ServerPort_Num = _ttoi(tcpserver.KeyInfo);
	tcpserver.IniInfo(_T("./NetworkPath.ini"), _T("ServerInfo"), _T("ClientCount"));
	tcpserver.ClientCount_Num = _ttoi(tcpserver.KeyInfo);
 
	winsockInit();
	serverSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	tcpserver.BeforeAccept();
 
	while (1)
	{
		//ACCEPT 
		tcpserver.ClntAccept();
		if (m_socket_comm.clnt_sock == SOCKET_ERROR)
		{
			break;
		}
		tcpserver.MultiClient();
 
		CTime cTime = CTime::GetCurrentTime();
		tcpserver.ConnTime.Format("%04d-%02d-%02d %02d:%02d:%02d", cTime.GetYear(), cTime.GetMonth(), cTime.GetDay(), cTime.GetHour(), cTime.GetMinute(), cTime.GetSecond());
		strcpy(m_user_list[m_socket_comm.index].strConnTime, tcpserver.ConnTime);
		//ConnTime - 접속한 시간 
 
		m_socket_comm.index++;
		//recvsend쓰레드 실행
		_beginthread(ThreadTcpSendRecv, NULL, (void*)m_socket_comm.clnt_sock);
	}
	closesocket(m_socket_comm.server_sock);
	WSACleanup;
	return 0;
}

쓰레드 2
void ThreadTcpSendRecv(void *sock)
{
#if 1
	TCPServer tcpserver;
	//------------------------------------------------------------------------------------------------------------------------------------------
 
	//------------------------------------------------------------------------------------------------------------------------------------------
	SOCKET socket = (SOCKET)sock;
	while (1)
	{
		//recv
		tcpserver.ServRecv(socket);
		if (tcpserver.recvsize <= 0) {
			break;
		}
 
		CTime cRecvTime = CTime::GetCurrentTime(); //접속시간 받기 위해
		//dlg->m_ListChat.AddString(tcpserver.strMsg);
 
		for (int i = 0; i < m_socket_comm.client_list.size(); i++)
		{
			if (m_socket_comm.client_list[i] == socket)
			{
				tcpserver.strRecvTime.Format("%04d-%02d-%02d %02d:%02d:%02d", cRecvTime.GetYear(), cRecvTime.GetMonth(), cRecvTime.GetDay(), cRecvTime.GetHour(), cRecvTime.GetMinute(), cRecvTime.GetSecond());
 
			}
		}
#if 1
		//send
		tcpserver.ServSend();
	}
#endif
#if 1
	//클라이언트가 접속 종료시
	//-------------------------------------------
	for (int i = 0; i < m_socket_comm.client_list.size(); i++)
	{
		if (m_socket_comm.client_list[i] == socket)
		{
 
			CString DisConnectInfo_str;
			CString DisConnectInfo_str2;
			DisConnectInfo_str.Format("IP : %s 가 종료했습니다. \n", m_user_list[i].Ip_Address);
			DisConnectInfo_str2.Format("IP : %s\n", m_user_list[i].Ip_Address);
 
			CTime cTime = CTime::GetCurrentTime(); //종료시간 받기 위해
			CString CloseTime = _T("");
			CloseTime.Format("%04d-%02d-%02d %02d:%02d:%02d", cTime.GetYear(), cTime.GetMonth(), cTime.GetDay(), cTime.GetHour(), cTime.GetMinute(), cTime.GetSecond());
			strcpy(m_user_list[i].strCloseTime, CloseTime);
 
 
			int j;
			for (j = i; j < m_socket_comm.client_list.size() - 1; j++)
			{
				m_user_list[j] = m_user_list[j + 1];
			}
			strcpy(m_user_list[m_socket_comm.client_list.size() - 1].Ip_Address, "");
			break;
		}
	}
	tcpserver.EraseVec(socket);
#endif
	if (socket == NULL)
	{
	}
	closesocket(socket);
#endif
}

서버 실행하는 부분입니다
void CServerDlg::OnBnClickedButtonStart()
{
	//pThreadTcpServer = AfxBeginThread(ServerFunc, this);
	if (((CButton*)GetDlgItem(IDC_CHECK_TCP))->GetCheck())
	{
		AfxBeginThread(ThreadTcpComm, this);
		GetDlgItem(IDC_BUTTON_START)->EnableWindow(FALSE);
		m_button_start.SetWindowTextA("서버 실행중");
	}
}

라스코니의 이미지

코드가 읽기 좋은 모습이 아니네요. code /code로 감싸주세요.
그리고 debug 해 보셨나요? 받은 값이 string 데이터가 맞나요?
ListBox에 뿌리는 부분에 break 걸고 넘겨지는 데이터가 잘 들어오는지, char 값들인지 확인하면 될 부분으로 보이는데요.

l595659의 이미지

앞으로 글 올릴때 code /code로 감싸겠습니다
디버그를 해봤었는데 ListBox에 뿌리는 부분이 char 값으로 옵니다.

저 두개 쓰레드는 계속해서 반복되는데 다른 쓰레드에서 원하는 값을 가져오는게 감이 안잡힙니다.
시도해본건 쓰레드를 하나 더 생성해서 그 안에다가

	if (tcpserver.ClntAccept)
	{
		dlg->m_ListClient.InsertString(-1, tcpserver.ConnectInfo_str);
	}
 이런식으로 출력해보려고 해도 ClntAceept가 함수라서인지 비표준 구문이라고 뜨네요
라스코니의 이미지

아직 문제 해결에 익숙하지 않으신가 보네요. 끈기가 있으시니 잘 해결하시리라 생각합니다.

수신시 dlg->m_ListChat.AddString(_T("Test")); 로 해서 리스트박스에 계속 Test가 찍히는지 보시고,
찍힌다면 호출 자체에는 문제가 없으니

CString str;
str.Format(_T(tcpserver's msg));
dlg->m_ListChat.AddString(str); 식으로 해 보세요. 자질구레한 syntax는 CString example 등을 찾아보면서 수정하시고...

l595659의 이미지

아직 초보인 상태에서 이것저것 해보려니까 문제는 생기는데 해결이 안되네요..
말씀하신대로 수신할 때 Test가 찍히는지 확인을 해보려고 새로만든 쓰레드에 아래와 같이 넣어서 테스트 해봤는데 중단점을 걸어도 recv, send, accept 가 있는 쓰레드만 돌고 이쪽으론 가진 않네요
데이터가 recv send는 되는데 찍는 부분이 문제네요

if (tcpserver.recvsize >= 0)
	{
		dlg->m_ListChat.AddString(_T("Test"));
	}
라스코니의 이미지

while(1) {
    ...
    tcp.receiving;
    ...
    dlg->m_ListChat.AddString(_T("Test"));  <-- 이게 호출이 안된다는 말씀인가요?
}

dlg->m_ListChat.AddString(_T("Test"));를 어디 tcp server 초기화되는 (즉, 무조건 불릴 수밖에 없는 곳) 곳에 넣고 해보세요. 그래도 listbox에 표시가 안되면 listbox가 disable 되어 있거나 한 상황일 겁니다.

l595659의 이미지

지금은 MFC에서 테스트 하는데 쓰레드 두개랑 변수 및 함수를 Class 화 시킨걸 dll/lib화 시킨다고 tcp server 구현한 쓰레드에다가는 Dialog 관련한 함수를 다 빼라고 하더라구요.

그래서 MFC 프로젝트 cpp 파일에 쓰레드 만들어서 거기다가 해보고 있는 중 입니다.

그래도 말씀하신대로 무조건 불릴 수 밖에 없는 곳에다가 넣어서 테스트 해봤는데 이 때는 listbox에 출력이 됩니다.

라스코니의 이미지

아마 listbox 객체가 그 쓰레드가 도달(access)할 수 없는 위치에 있나 보네요. 예를들어 dll/lib화 했을 때 본체의 컨트롤을 쓰려면 그 레퍼런스를 dll에 다 넘겨야 하거든요. 그럴수 없을 때 shared memory 같은 것으로 log 데이터를 넘겨서 출력하고 하는데.....

뭐 소스가 어떤 구조인지를 모르겠지만 dll/lib 쪽 코드에 connectParentControlObject(listbox *box) 같은 것을 두시고.... 본체에서 dll/lib 로드시에 그 함수를 불러서 본체의 listbox 컨트롤을 넘겨 보세요. 즉

본체...
   m_listbox box;
 
   // load dll
   calleddll->connectParentControlObject(&box);
 
 
dll 쪽
   listbox *p = NULL;
 
   void connectParentControlObject(listbox *box)
   {
      p = box;
   }

이런 느낌인 방식인데 호출이 되리라고는 장담 못하겠네요. 아니면 좀더 상세한 코드를 올려 보세요.

l595659의 이미지

아직 dll 및 lib 하기전에 Class화 시키는 연습 느낌도 있는지라 말씀하신게 어떤건진 모르겠지만 찾아서 공부해봐야죠

이게 상세한 코드를 올리게 되면 너무 글이 길어질 것 같아서 안올렸는데 빠른시일내에 올려보겠습니다. 파일이
Class.h, Class.cpp, Dlg.cpp 이렇게 있다보니까 꽤 길어질듯 해서요

라스코니의 이미지

디버그를 line-by-line 으로 해보세요.
tcp.recv() 떨어진 뒤에 수행이 listbox 뿌리는 곳으로 가는지...
listbox 호출 하는 곳에 컴파일 오류가 없었다는 것은 링커가 어떻게 listbox를 붙여줄지를 알았다는 것이고 그러면 무조건 listbox에 찍혀야 해요.

l595659의 이미지

조건문이랑 이것저것 바꾸면서 말씀해주신대로 line-by-line으로 디버그 해보겠습니다
이게 혹시 쓰레드 실행하는 부분에서 문제가 생길 수도 있을까요?

server 실행관련 쓰레드 다음에 바로 실행되게 해두고 return하면 쓰레드가 종료되다보니까 반복문 걸어서 계속 돌아가게끔 했거든요

mangg의 이미지

오랜만에 kldp 들어왔다가 오지랍 한번 넓혀 보는 마음으로 몇자 적습니다.
보통 MFC 컨트롤들은 Thread safe가 아닌 경우가 많습니다.

그래서 thread 에서 MFC 컨트롤들을 직접 처리 하지 않고 PostMessage, SendMessage로 특정 메시지를 전달하고
처리하고자 하는 데이터를 전달하는 형태로 구현했습니다.(? 네.. 저도 오래되서....) 몇몇 컨트롤들은 MFC 내부에서 WIN32 API 로 SendMessage로 Wrapping 되는 경우가 있어서 thread에서 사용이 가능 할 수 있습니다.

하지만 안전하고 정신건강에 좋은 코딩을 위해선 , 일반 쓰레드 에서 MFC 컨트롤들을 직접 액세스 하지 않는게
제일 좋은 방법입니다. 지금 당장 , 잘 실행되고 운영된다고 하더라도 어느 순간 문제를 일으키는 곳이
바로 MFC 컨트롤입니다. 이건 GUI를 사용하는 모든 곳에서 크로스 쓰레드 라고 해서 존재하는 것으로 알고 있습니다.

더 혼란스럽게 했다면 죄송합니다.

-------------------
나는 Copy&Paster 이다. 나의 화려한 기술 조합에 모두들 나를 두려워 한다. 나도 코드 Maker 이고 싶다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.