Post

반응형
TIL 시작


반응형

'이전게시판 > TIL' 카테고리의 다른 글

2018.12.08_TIL  (0) 2018.12.08
2018.12.07_TIL  (0) 2018.12.08
18.10.21_TIL  (0) 2018.10.21
18.10.20_TIL  (0) 2018.10.20
18.10.19_TIL  (0) 2018.10.19
18.10.18_TIL  (0) 2018.10.18
18.10.17_TIL  (0) 2018.10.17
18.10.16_TIL  (0) 2018.10.16

Post

반응형

Javascript 정리


html과 javascript의 차이


  • html은 있는 그대로 보여주지만 javascript는 동적으로 페이지를 보여준다.
  • 아래 예는 같은 1+1을 했지만 html은 글자로써 그대로 보여주는 반면
  • javascript는 1+1의 결과값 2를 출력한다.
<!--DOCTYPE html-->
<html>
<head>
  <meta charset="utf-8">
  <title></title>
</head>
<body>
  <script>
    document.write("1+1");
    document.write(1+1);
  </script>
  <h1>1+1</h1>
</body>
</html>

javascript event


  • html에 on으로 시작하는 속성들이 있다.
  • on으로 시작하는 속성은 javascript 이벤트를 대입해야한다.
  • onclick="alert('OK')"
  • onclick 속성은 마우스 클릭(이벤트)시 OK알람창(javascript)이 뜬다.
<!--DOCTYPE html-->
<html>
<head>
  <meta charset="utf-8">
  <title></title>
</head>
<body>
  <input type="button" value="Click button" onclick="alert('OK')">
  <input type="text" onchange="alert('changed')">
  <input type="text" onkeydown="alert('key down!')">
</body>
</html>

javascript consol


  • 크롬 웹 페이지에서 오른쪽 클릭 - 검사-console 창 뜸
  • 간단한 javascript code 를  console 창에서 테스트 할 수 있다.


데이터타입-문자열과 숫자


  • 데이터 타입이 문자열인지 숫자인지에 따라 결과가 달라진다.
  • 데이터 타입 숫자 : 1+1 = 2
  • 데이터 타입 문자 : "1"+"1"="11"


프로그래밍이란?


  • javascript는 프로그래밍 언어. html 아님.
  • 프로그램이란 순서라는 의미가 있음. 만드는사람은 프로그래머.
  • html은 시간의 순서에 따라 순서대로 작동x
  • javascript는 시간의 순서에 따라 순서대로 작동.


조건문


  • == : 값이 같은지 확인
  • === : 데이터 값이 같은지 확인
  • ex.
  • 1 == "1" : true
  • 1 === "1" : false
  • true == 1 : true
  • true === 1 : false

var target = document.querySelector('body');
if(this.value === 'night')
{
    target.style.backgroundColor = 'black';
    target.style.color = 'white';
    this. value = 'day';
} else {
    target.style.backgroundColor = 'white';
    target.style.color = 'black';
    this.value = 'night';
}

function


중복된 코드가 많아지면 function 으로 grouping 해야 소스 관리가 쉽다.

  • 함수선언
<script>
function FunctionName(arg)
{
return arg;
}
</script>

  • 사용법
<input type="button" value="click" onclick="FunctionName(value)"

객체


  • 소스코드가 커지면 function 이름의 중복이 많이 나타난다.
  • function이 중복되면 나중에 선언된 function만 남고, 먼저 선언된걸 지워버린다.
  • 객체를 사용하면 객체와 관련있는 변수, 함수를 Grouping하기 때문에 function 이름 중복을 방지할 수 있다.
  • 사용법
선언
var player = { "id":"value" }
가져오기
player.id , player.value
Method 선언
var func = {
    MethodName:function(arg){
            // 내용
    },
    MethodName2:function(arg){
            // 내용
    }
}
  • Method 사용

func.MethodName1(value);



파일로 정리하기


  • js객체 정의가 각 파일마다 정의되어있다면 소스가 복잡해진다.
  • js파일을 한 파일에 넣고 정리할 수 있다.
  • js파일 참조하기
<head> 안에 <script src = "js이름.js"></script>

jquery


  • jquery 라이브러리 포함(CDN)
  • jquery 다운로드 페이지 접속 (https://jquery.com/download/)
  • 페이지에서 CDN 검색 , 구글 CDN 클릭
  • jQuery을 찾아서 아래 script를 <head>태그 안에 넣음
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

  • jquery는 javascript로 복잡하게 적은 소스를 간단하게 변경할 수 있는 좋은 라이브러리.


반응형

Post

반응형

Git Cui Commit


Git을 사용할 폴더 만들기


Git cmd 환경은 Git Bash를 실행시키면 된다.
Bash는 시그윈을 통해 윈도우에서 Linux명령어를 사용할 수 있도록 되어있다.

mkdir [폴더이름]

폴더로 이동
cd [폴더 이름]

상위폴더로 이동
cd ../ 

Git에게 이 폴더에서 Git을 사용하겠다고 알림.
git init

ls -la
현재 폴더에 들어있는 파일목록을 보여줌.
git init 후 git 관련 폴더가 생긴것을 확인할 수 있음.

Git이 관리할 파일로 등록


파일 생성
vim filename.txt

입력모드로 설정
i

입력종료
esc

나가기
:wq (내용 저장 및 나가기)

내용보기
cat filename.txt

파일의 상태보기
git status
Untracked files 에 filename.txt 가 있다.
아직 추적하고 있지 않다는 뜻이다.

추적하라는 명령
git add filename.txt

git add 시 아래와 같은 에러가 떴다.
warning : LF will be replaced by CRLF in filename.txt.
The file will have its original line endings in your working directory

찾아보니 파일 끝줄이 LF -> CRLF로 대체되었다는 내용이다.
윈도우는 끝줄을 CRLF로 사용하고, 리눅스는 LF만 사용한다.
이것은 공동 개발자들이 멀티플랫폼 사용시에 문제가 될 수 있다고 한다.
찾아보니 커밋할 때 CRLF -> LF 변환, Checkout할 때 LF -> CRLF 로 변환해주는 설정이 있다고 한다.

git config --global core.autocrlf true

버전만들기(commit)


git 이력에 보여줄 자신의 이름을 세팅해야함,
git config --global user.name mail

파일 커밋
git commit

vim이 실행된다. Log를 적는다.위 vim 에서 작성했던것 처럼 i 를 눌려 입력모드로 변환 후 
Log를 작성한다. 다 작성하였다면 esc, :wq를 해서 나온다. 그러면 commit이 완료된다.

다시 수정 및 커밋
filename.txt 수정 후 commit 하면 안된다.
다시 git 버전관리에 등록해야한다.
git add filename.txt
git commit

git stage area


filetext.txt 을 복사한다.
cp filename.txt filename2.txt

그리고 커밋 및 로그 확인
git add filename2.txt
git commit
git log

그리고 filename.txt만 add하고 2는 add하지 않는다면
filename.txt는 stage area에 있고, filename2.txt는 working copy에 있다고 볼 수 있다.
git add 를 하면 working copy -> stage area 로 이동해서
commit 대기 상태가 된다고 보면 된다.



반응형

'이전게시판 > Git' 카테고리의 다른 글

Git Branch  (0) 2018.10.12
invalid username or password Sourcetree 해결  (0) 2018.10.12
GIT 사용 방법 정리  (0) 2018.10.12
Git 기초_1 Clone, Commit, Push (with.TortoriseGit)  (0) 2016.07.13

Post

Git Branch

[ 이전게시판/Git ] 2018. 10. 12. 22:21
반응형

Git Branch 사용법

원격저장소(Remote)


인터넷, 네트워크에 연결되어있는 저장소. 로컬 저장소와 원격저장소를 연결해서 소스코드를 백업할 수 있다.

SourceTree 메뉴에서 Repository - Add Remote 클릭.
일단 SSH말고 Http로 연결하려고 한다. URL/Path에 git 저장소 주소를 넣는다.
기본 저장소라는 의미로 Default remote를 체크한다.

원격저장소로 소스코드 올리기(Push)


Push를 누르면 로컬에 있는 파일이 올라간다.

만약 여기서 
nvalid username or password Sourcetree,
Updates were rejected because the tip of your current branch is behind
에러가 발생한다면 http://dramadramingdays.tistory.com/126 글을 참고한다.

원격저장소로 업로드


수정사항을 Commit 하면 Push에 1이 뜬다.

원격저장소와 local저장소가 하나의 차이가 있다는 뜻이다.
(origin/master가 원격저장소인데 graph에서 하나 차이일 것 이다.)

저장소 복제(Clone)


원격 저장소에 내용을 자신의 local로 가져오는 것.

Sourcetree 의 local 저장소 이름 옆에 플러스(+)를 클릭한다.
그리고 메뉴에서 Clone 클릭. 원격저장소위치, 이름 등을 적고 Clone 클릭.

협업 pull,push


1.branch에서 pull 하고 충돌나는것을 수정한다.
2.다 수정했으면 commit 한다.
3.master branch 로 이동하고 작업branch의 오른쪽마우스 클릭해서
'Merge work into current branch'를 누른다 한다.

4.작업 branch의 내용을 master로 가져왔다. 이제 push 한다.

반드시 작업 전에 pull을 해서 원격저장소 내용을 내 local로 가져온다.
작업하고 나서 push하기 전에 반드시 pull을 한다. 안하면 pull 받으라고 push 실패 뜬다.

Stash


완성하지 못한 내용을 안전한 곳으로 이동시키는 것.

working copy 에 변경된 소스코드가 있을 때 Stash 클릭.
Stash Changes? 창이 뜨면 메시지를 입력하고 OK 클릭.
우리가 작업한 내용이 Graph에 사라진것을 확인할 수 있음.
사이드바에 Stashes 를 클릭하면 우리가 아까 Stash한 코드가 보인다.
다시 불러올려면 On master; 을 오른쪽 클릭하고 'Apply Stash 'On master:'을 클릭한다.

Tag


수많은 버전 중 의미있는 버전을 기록할 때 사용.
Graph 에서 의미있는 버전의 오른쪽 마우스 클릭 후 Tag 클릭.
Tag는 원격저장소로 올리려면 Push할 때 Push all tags 체크한다.

gitgnore


버전관리에서 파일을 무시한다.

무시하려는 파일은 sourcetree 에서 working copy 에 물음표 아이콘으로 등록되어있다.
오른쪽 클릭을 해서 Ignore을 선택한다.
Ignore exact filename(s) : 해당 파일이름을 가진 파일만 무시한다.
Ignore all files with this extension : 해당 파일의 확장자를 가지면 무시한다.
.gitignore 파일이 만들어졌다.(git 환경설정파일) 커밋한다.
(패턴으로 무시하려면 glob을 찾아본다)
(gitignore.io 에 들어가서 각 환경마다 어떤 파일을 무시하는지 알아낼 수 도 있다.)

반응형

'이전게시판 > Git' 카테고리의 다른 글

Git Cui Commit  (0) 2018.10.13
invalid username or password Sourcetree 해결  (0) 2018.10.12
GIT 사용 방법 정리  (0) 2018.10.12
Git 기초_1 Clone, Commit, Push (with.TortoriseGit)  (0) 2016.07.13

Post

반응형

invalid username or password Sourcetree 해결

상황


로컬저장소와 원격저장소 동기화를 위해 Push 선택.

아이디, 비밀번호 입력 후 틀렸는지 실패해서 다시한번 시도하려고 했는데

아이디, 비밀번호 입력창이 뜨지 않아 계속

 invalid username or password 에러 발생


해결


Sourcetree 인증정보를 삭제해야한다.

C:\Users\user\AppData\Local\Atlassian\SourceTree 폴더에서
(AppData는 숨김파일이므로 숨김파일 표시를 한다)
passwd, userhosts 파일에 저장된 id, 비밀번호를 전부 삭제한다.

sourcetree를 재시작하고 push하면 id, pass를 입력하라는 창이 다시 뜬다.


Updates were rejected because the tip of your current branch is behind 해결



상황


local 에서 작업한 내용을 원격저장소에 올리려고 Push 하니 아래와 같은 에러 발생.

hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.

hint: See the 'Note about fast-forwards' in 'git push --help' for details.

해결


아마 새로 생성한 Repository에 Readme.md 파일이 있을 것이다.

Local 저장소 내용을 올리려고 했는데 알수없는 Readme.md 파일이 있어서
올릴수 없다고 뜨는것 같다(정확히는 모른다;; 추측)
새로운 Repository 생성 시 Initialize this repository with a  README 를 체크하지 않고 생성한다.
그리고 Repository에 Push 하면 소스코드가 잘 올라간다.


반응형

'이전게시판 > Git' 카테고리의 다른 글

Git Cui Commit  (0) 2018.10.13
Git Branch  (0) 2018.10.12
GIT 사용 방법 정리  (0) 2018.10.12
Git 기초_1 Clone, Commit, Push (with.TortoriseGit)  (0) 2016.07.13

Post

반응형

GIT 사용 방법 정리

Git , Sourcetree 다운로드

특별한 설정 없이 그대로 next 누르면 된다.

sourcetree  다운로드

Commit


파일추가 및 커밋을 하려면 해당 git 폴더에서 파일 생성한다.

그러면 sourcetree에 unstaged files에 자동적으로 생성한 파일이 보인다.
이 파일을 staage Selected 버튼을 눌러 위로 올린 후 Commit 버튼을 누르면 커밋이 완료된다.

파일을 추가해서 커밋하려고 하면 커밋이 실패한다.
커밋하려는 사람의 정보가 없기 때문이다.
sourcetree에서 Tools-Options-General에서 FullName, Email address를 설정하면 commit 성공한다.

File Status 에 공간이 두개 있는데
맨 아래있는 것이 working copy 이다.
stage Selected 클릭 시 올라가는 공간은 index, stage area 라고 부른다.
commit 하면 하나의 버전이 된다. 이 각각 버전이 저장된 공간을 repository 라고 부른다.

Discard 


working copy에 수정된 것을 원래대로 돌리고 싶을 때 선택.


Reset


이미 커밋한 것을 되돌린다.

Log/History에서 Graph 창에서 되돌리고 싶은 버전을 마우스오른쪽 클린한다.
Reset current branch to this commit 선택.
Hard는 되돌리고 싶은 버전으로 돌아가고, working copy, stage area 에 있는 내용 전부 사라진다.
Mix는 되돌리고 싶은 버전으로 돌아가고, stage area 내용은 사라진다. 하지만 working copy에 있는건 그대로다.
Soft는 되돌리고 싶은 버전으로 돌아가고, stage area, working copy 내용도 그대로고, 되돌린 버전의 내용도 stage area에 들어가 있다. 

Revert


Reset과 비슷하지만 다르다.

선택한 버전을 취소해서 이전 상태로 돌린다. 그리고 내가 되돌렸다는 것이 이력에 남는다.
(여러버전을 넘어가면 안되고, 순차적으로 역순으로 revert해야한다)
Log/History Graph창에서 Reverse commit 클릭.

Branch


Branch는 미래에 넣을 수도 있고, 언제 들어갈지 모르는 새로운 작업을 할 수 있도록

독립된 공간을 만들어서 작업 하는 것이다.
Branch를 만들 때는 Branch 버튼 클릭. Branch 이름을 정하고 Create Branch 버튼을 누른다.

Merge


2개의 Branch를 병합하는 것을 말한다.

Master에 Branch 병합하는 법.
  1. Master Branch 선택
  2. 병합하려는 Branch를 오른쪽 클릭하면 'Merge Branch into current branch' 메뉴가 있다. 클릭한다.
  3.  Merge branch 'branchName' Graph가 생겼다. 

충돌


Marster, Branch 의 소스코드의 같은 위치에 변경사항이 있다면 충돌이 일어난다.

소스코드를 수정 한 후 working copy 에서 수정된 파일을 오른쪽 클릭한다.
충돌을 해결했다는 것을 Git에게 알려주기 위해서 Resolve conflicts-Mark Resolved 클릭
그리고 Commit 한다.

충돌 예방


만약 장시간동안 Branch 작업을 한다고 했을 때 그 기간동안, Master 소스를 가져와서 동기화를 해야한다.

작업 Branch를 현재작업중으로 놓고, Master Branch 오른쪽 클릭 - Merge master into current branch 클릭.
그러면 Master Branch 내용이 작업Branch 내용과 병합된다.

반응형

'이전게시판 > Git' 카테고리의 다른 글

Git Cui Commit  (0) 2018.10.13
Git Branch  (0) 2018.10.12
invalid username or password Sourcetree 해결  (0) 2018.10.12
Git 기초_1 Clone, Commit, Push (with.TortoriseGit)  (0) 2016.07.13

Post

반응형

Bitnami로 서버 접속 ( Windows )


  1. 아파치를 설치하기 위해 Bitnami( https://bitnami.com/ )를 다운받는다. MySql, PHP는 사용X
  2. 설치 확인을 위해 Go to Aplication 버튼을 클릭한다. Bitnabi 웹페이지가 잘 나온다면 성공한 것이다.
  3. Manage Servers 메뉴를 선택해 Apache Web Server를 Stop 시키면 Apache 서버가 꺼진 것이다.(웹페이지를 다시불러오면 페이지를 찾지 못했다는 창이 뜬다)D F
  4. Bitnami가 설치된 폴더로 가면( C:\Bitnami\wampstack-7.1.21-0\apache2\htdocs ) index.html 파일을 찾을 수 있다.
  5. index.html이 있는 폴더 내용을 전부 삭제 하고, 자신이 만든 html 파일을 넣으면 새로 고침 후 홈페이지가 반영된 것을 알 수 있다.
  6. 기존엔 내 컴퓨터에 html 파일을 연거지만, 5번은 웹 클라이언트가 웹 서버에 페이지를 요청을 하고, 클라이언트가 응답을 받아서 웹페이지가 나온 것이다.

댓글 달기 기능 


  1. https://disqus.com/ 사이트 접속
  2. 회원가입, 로그인 후 나오는 페이지에서 Get Started - I want to install Disqus on my site 클릭
  3. Website Name을 적고 Create Site 클릭. 그 후 무엇으로 홈페이지를 만들었는지 선택하는 화면이 나오는데 맨 밑에 'I don't see my platform...' 클릭
  4. 어떤 코드가 나옴. 그 코드를 소스코드에 넣으면 댓글 추가 완료. ( File이 아니라 웹서버에 요청해서 페이지를 열어야 한다! 파일로는 댓글이 안보인다. )

채팅 기능 추가


  1. https://www.tawk.to/ 사이트 접속
  2. 가입, 로그인 후 Dashboard 화면이 뜬다. ( https://dashboard.tawk.to/#/dashboard )
  3. 여기서 왼쪽 맨 아래에 수레바퀴 모양(설정 모양) 버튼을 클릭한다.
  4. 자신의 홈페이지 이름, 주소를 적고 완료.
  5.  Widget Code를 복사해서 웹 사이트에 추가한다. 채팅창이 추가된 것을 확인할 수 있다.(http로 열어야 한다. 파일x)
  6. 방문자가 채팅을 치면 알람소리가 들리고 Dashboard에서 알람이 뜬 것을 확인할 수 있다. 알람 뜬 아이콘을 선택 후 Join을 누르면 사용자와 채팅을 할 수 있다.


반응형

Post

반응형

HTML 기본태그


  • <strong>내용</strong> : 중요한 단어라고 설정하고, 글자를 진하게 한다. <b>는 그냥 글자만 진하게 한다.
  • <u>내용</u> : 밑줄 영어로 underline. 앞글자만 따서 <u> 태그는 글자에 밑줄을 긋는 태그이다.
  • <h1> ~ <h6> : 제목을 나타내는 태그. 1이 제일 글자 크기가 크고 6이 제일 작다.
  • <br> : 줄바꿈 태그. 닫는 태그가 없다.
  • <p>내용</p> : 단락을 가르는 태그. 웹페이지에서 단락이라는 의미를 가지기 때문에 <br>태그와 비슷하지만 <p> 태그를 쓰면 더 좋다.그리고 <p>태그는 정해진 여백크기가 있지만 style을 사용하면 여백 크기를 다르게 만들 수 있다.

ex. <p style = "margin-top:45px;">

  • <img>태그 : 이미지를 나타내는 태그. 사용 방법은 <img src = "이미지 위치" width = "100"> 이 때 src, width은 속성이다.
태그의 이름만으로는 부족할 때 태그의 속성에 정보를 준다.
  • <li>목록</li> 태그 : 목록 태그.  목록의 영어이름 list에 앞 두글자를 땄다. <ul>태그는 <li>태그로 이루어진 목록을
그룹짓는 역활을 한다. 숫자목록을 하고 싶으면 <ul>대신 <ol>태그를 사용한다.
  • <ul>태그는 Unordered List의 약자
  • <ol>태그는 OrderedList 의 약자
  • <title>타이틀</title> : 웹페이지의 제목을 명시적으로 알려줌. 검색엔진이 검색할 때 유용하게 사용된다.
  • <a> : 링크. ancher 약자. <a href = 주소></a>


GIT으로 정적 웹 호스팅하기


  1. 로그인 후 새로운 저장소를 만든 후 Upload files 버튼을 클릭해서 웹파일을 git에 올린다.
  2. Settings 클릭 후 GitHub Pages 에서 source 를 master branch로 바꾼 후 저장.
  3. Source 위에 웹 주소가 생긴다. git에서 정적웹 호스팅이 생성된 것이다. 
이제 누구나 내가 만든 웹페이지에 접근할 수 있다.
( git은 static web만 호스팅 된다고 한다. static web이란 화면 변화 없이 그대로 보여주는 웹이고, dynamic web이란 클라이언트에서 보내주는 값에 의해 화면이 바뀔 수 있는 web이다. html로만 만든 웹 클라이언트는 서버가 없어서 동적인 변화를 못주기 때문에 static web이다.) 

그 외 추천 웹호스팅 https://www.bitballoon.com/ , http://neocities.org , Google Cloud Storage , Azure Blob




반응형

Post

반응형

2018/10/04 - [Programing/Server] - IOCP window 채팅 클라이언트 소스

IOCP window 채팅 클라이언트 소스


아래는 IOCP window 채팅 클라이언트 프로젝트이다

WinChatClnt.zip

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "resource.h"
#include 

#pragma comment(lib, "ws2_32.lib")

struct SOCKDATA
{
    SOCKET hSocket;
    SOCKADDR_IN servAdr;
};

SOCKET hSocket = 0;     // 소켓. 메시지 처리에서도 사용해야되서 전역변수로 선언
HWND hMsgWnd = 0;     // 메시지 받고 나서 ui처리해야 되서 전역변수로 선언


#pragma region RegionName
#define BUF_SIZE 1024
void ErrorHandling(const char* message);
void StartServerConnect(WSADATA& wsaData, SOCKET& hSocket, SOCKADDR_IN& servAdr);       // 소켓 초기화
DWORD WINAPI ServerThreadMain(LPVOID socket);                                   // 서버와 데이터를 주고 받는 Thread

BOOL Dlg_OnInitDialog(HWND hWnd, HWND hWndFocus, LPARAM lParam) {
    // WM_INITDIALOG: Dialog Box 초기화 코드 작성
    return (TRUE);
}

void Dlg_OnCommand(HWND hWnd, int id, HWND hWndCtl, UINT codeNotify) {

    if (0 == hMsgWnd)
    {
        hMsgWnd = hWnd;
    }

    //WM_COMMAND
    switch (id) {
    case IDOK:
    {
        TCHAR buff[BUF_SIZE];
        GetDlgItemText(hWnd, IDC_CHAT_ENTER, buff, BUF_SIZE);
        char temp[BUF_SIZE];
        WideCharToMultiByte(CP_ACP, 0, buff, BUF_SIZE, temp, BUF_SIZE, NULL, NULL);
        printf("%s \n", temp);

        int len = strlen(temp);
        send(hSocket, temp, len, 0);

        memset(buff, 0, BUF_SIZE);

        // 글자 없애기
        SetDlgItemText(hWnd, IDC_CHAT_ENTER, buff);

        break;
    }
    case WM_DESTROY:
        exit(0);

        break;
    }
}

// DialogBox Procedure
INT_PTR WINAPI Dlg_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_INITDIALOG:
        return (SetDlgMsgResult(hWnd, uMsg, HANDLE_WM_INITDIALOG((hWnd), (wParam), (lParam), (Dlg_OnInitDialog))));

    case WM_COMMAND:
        return (SetDlgMsgResult(hWnd, uMsg, HANDLE_WM_COMMAND((hWnd), (wParam), (lParam), (Dlg_OnCommand))));
    }
    return (FALSE);
}

int APIENTRY _tWinMain(HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPTSTR    lpCmdLine,
    int       nCmdShow)
{
    WSADATA wsaData;
    SOCKADDR_IN servAdr;
    
    StartServerConnect(wsaData, hSocket, servAdr);

    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);


    // DialogBox생성 함수      
    DialogBox(hInstance,
        MAKEINTRESOURCE(CHAT_DIALOG/*DialogBox ID*/),
        NULL,
        Dlg_Proc/*DialogBox Procedure*/);

    return(0);
}

DWORD WINAPI ServerThreadMain(LPVOID data)
{
    SOCKDATA* socketInfo = (SOCKDATA*)data;
    if (NULL == socketInfo)
    {
        return 0;
    }

    if (connect(socketInfo->hSocket, (SOCKADDR*)(&socketInfo->servAdr), sizeof(socketInfo->servAdr)) == SOCKET_ERROR)
    {
        DWORD dwError = GetLastError();
        ErrorHandling("connect() error!");
    }
    else
        puts("Connected...........");

    char message[BUF_SIZE];
    memset(message, 0, BUF_SIZE);
    TCHAR uniMessage[BUF_SIZE];
    memset(uniMessage, 0, BUF_SIZE * sizeof(TCHAR));
    
    int strLen = 0;
    int readLen = 0;

    while (true)
    {
        //fputs("Input message(Q to quit): ", stdout);
        //fgets(message, BUF_SIZE, stdin);
        //if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
        //    break;

        //strLen = strlen(message);
        //send(socketInfo->hSocket, message, strLen, 0);

        readLen = 0;
        while (1)
        {
            memset(message, 0, BUF_SIZE);
            readLen += recv(socketInfo->hSocket, &message[readLen], BUF_SIZE - 1, 0);
            if (readLen >= strLen)
                break;
        }
        strLen = readLen;
        message[strLen] = 0;

        // 유니코드로 변경
        memset(uniMessage, 0, BUF_SIZE * sizeof(TCHAR));
      

        // 유니코드 변경 전 유니코드 길이 구하기
        int nUniLen = MultiByteToWideChar(CP_ACP, 0, message, strlen(message), NULL, NULL);

        MultiByteToWideChar(CP_ACP, 0, message, strLen+1, uniMessage, nUniLen);
        

        if (0 != hMsgWnd)
        {
            //SetDlgItemText(hMsgWnd, CHAT_VIEW, uniMessage);
            SendDlgItemMessage(hMsgWnd, CHAT_VIEW, LB_GETCURSEL, 0, 0);
            SendDlgItemMessage(hMsgWnd, CHAT_VIEW, LB_ADDSTRING, 0, (LPARAM)uniMessage);
        }
        strLen = 0;
        //printf("Message from server: %s", message);
    }
    
    delete socketInfo;
    closesocket(socketInfo->hSocket);
    WSACleanup();

    return 0;
}

void StartServerConnect(WSADATA& wsaData, SOCKET& hSocket, SOCKADDR_IN& servAdr)
{
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
        ErrorHandling("WSAStartup() error!");
    
    hSocket = socket(PF_INET, SOCK_STREAM, 0);
    if (hSocket == INVALID_SOCKET)
        ErrorHandling("socket() error");

    memset(&servAdr, 0, sizeof(servAdr));
    servAdr.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &(servAdr.sin_addr));
    servAdr.sin_port = htons(atoi("7777"));

    SOCKDATA* sockData = new SOCKDATA();
    sockData->hSocket = hSocket;
    sockData->servAdr = servAdr;

    // 서버와 통신할 thread  생성
    _beginthreadex(NULL, 0, (_beginthreadex_proc_type)ServerThreadMain, (LPVOID)sockData, 0, NULL);
}


void ErrorHandling(const char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

#pragma endregion

Dialog ID : CHAT_DIALOG

채팅 ListView ID : CHAT_VIEW (Sort 하지말것)

채팅 메시지 입력 Edit ID : IDC_CHAT_ENTER 

확인 버튼 ID : IDOK


이전에 올린 IOCP 채팅 서버와 대응하는 클라이언트다

반응형

Post

반응형

IOCP 채팅 서버 소스 예제


윤성우의 TCP/IP 에코 채팅 서버에서

다른사람들에게 채팅 내용이 전송되도록 수정한 소스이다.


#include 
#include 
#include 
#include 
#include 
#include 
#include 


#pragma comment(lib, "ws2_32.lib")

#define BUF_SIZE 100
#define READ 3
#define WRITE 5

int const NAME_SIZE = 10;

typedef struct          // socket intfo

{
    SOCKET hClntSock;
    SOCKADDR_IN clntAdr;
}PER_HANDLE_DATA, *LPPER_HANDLE_DATA;

typedef struct // buffer info
{
    OVERLAPPED overlapped;
    WSABUF wsaBuf;
    char buffer[BUF_SIZE];
    int rwMode;             // READ or WRITE
    
}PER_IO_DATA, *LPPER_IO_DATA;

typedef struct
{
    char id[NAME_SIZE];
    unsigned int level;
    
}USER_INFO, *PUSER_INFO;        // 나중에 PER_HANDLE_DATA 상속하자

std::list UserList; // 다른사람에게 채팅 전달해야하므로 user list 추가

DWORD WINAPI EchoThreadMain(LPVOID CompletionPortIO);

CRITICAL_SECTION cs;        // UserList 동기화를 위한 Critical Section


int main()
{
    WSADATA wsaData;
    HANDLE hComPort;
    SYSTEM_INFO sysInfo;
    LPPER_IO_DATA ioInfo;
    LPPER_HANDLE_DATA handleInfo;

    SOCKET hServSock;
    SOCKADDR_IN servAdr;
    int recvBytes, i, flags = 0;

    if (0 != WSAStartup(MAKEWORD(2, 2), &wsaData))
    {
        printf("WSAStartup() error!");
        exit(1);
    }

    hComPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
    GetSystemInfo(&sysInfo);

    for (i = 0; i < (int)sysInfo.dwNumberOfProcessors; ++i)
    {
        // Thread 핸들 정리 추가할 것
        _beginthreadex(NULL, 0, (_beginthreadex_proc_type)EchoThreadMain, (LPVOID)hComPort, 0, NULL);
    }

    hServSock = WSASocketW(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
    memset(&servAdr, 0, sizeof(servAdr));
    servAdr.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &(servAdr.sin_addr));
    servAdr.sin_port = htons(atoi("7777"));

    bind(hServSock, (SOCKADDR*)&servAdr, sizeof(servAdr));
    listen(hServSock, 5);

    InitializeCriticalSection(&cs);

    while (1)
    {
        SOCKET hClntSock;
        SOCKADDR_IN clntAdr;
        int addrLen = sizeof(clntAdr);

        hClntSock = accept(hServSock, (SOCKADDR*)&clntAdr, &addrLen);
        
        handleInfo = (LPPER_HANDLE_DATA)malloc(sizeof(PER_HANDLE_DATA));        // 누군가 접속할 때 마다 소켓 정보를 생성하고
        handleInfo->hClntSock = hClntSock;                                      // hClntSock은 지역변수이므로 저장한다
        memcpy(&(handleInfo->clntAdr), &clntAdr, addrLen);

        EnterCriticalSection(&cs);
        UserList.push_back(handleInfo);
        LeaveCriticalSection(&cs);

        CreateIoCompletionPort((HANDLE)hClntSock, hComPort, (DWORD)handleInfo, 0);  // 접속한 클라 소켓을 IOCP에 등록함

        ioInfo = (LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA));                        // 신규 유저 접속 시 overlapped도 생성한다.
        memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED));                       // 그리고 WSARecv 함수로 소켓 io완료 시 반환되는 wasBuf, overlapped 주소를 등록한다
        ioInfo->wsaBuf.len = BUF_SIZE;
        ioInfo->wsaBuf.buf = ioInfo->buffer;
        ioInfo->rwMode = READ;

        WSARecv(handleInfo->hClntSock, &(ioInfo->wsaBuf), 1, (LPDWORD)&recvBytes, (LPDWORD)&flags, &(ioInfo->overlapped), NULL);    // recv가 완료되었을 때 GetQueue...에서 반환됨
    }   

    DeleteCriticalSection(&cs);

    return 0;
}

DWORD WINAPI EchoThreadMain(LPVOID pComPort)
{
    HANDLE hComPort = (HANDLE)pComPort;
    SOCKET sock;
    DWORD bytesTrans;
    LPPER_HANDLE_DATA   handleInfo;
    LPPER_IO_DATA       ioInfo;
    DWORD flags = 0;
    char msg[BUF_SIZE];

    while (1)
    {
        GetQueuedCompletionStatus(hComPort, &bytesTrans, (LPDWORD)&handleInfo, (LPOVERLAPPED*)&ioInfo, INFINITE);
        sock = handleInfo->hClntSock;

        if (READ == ioInfo->rwMode)
        {
            puts("message received!");

            if (0 == bytesTrans)
            {
                EnterCriticalSection(&cs);
                std::list::iterator iter;
                for (iter = UserList.begin(); iter != UserList.end(); ++iter)
                {
                    if (sock == (*iter)->hClntSock)
                    {
                        UserList.erase(iter);
                        break;
                    }
                }
                LeaveCriticalSection(&cs);

                closesocket(sock);
                free(handleInfo);
                free(ioInfo);
                continue;
            }

            memset(msg, 0, BUF_SIZE);
            memcpy(msg, ioInfo->buffer, BUF_SIZE);
            free(ioInfo);
            
            EnterCriticalSection(&cs);                      // UserList는 여러 스레드가 접근하는 공유자원이므로 접근 시 cs 필수 
            std::list::iterator iter;
            for (iter = UserList.begin(); iter != UserList.end(); ++iter)  
            {
                // 소켓마다 overlapped를 새로 만들어야 한다. 왜냐하면 하나의 overlapped를 여러 소켓이 쓸 수 없기 때문이다. 
                ioInfo = (LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA));
                memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED));
                ioInfo->wsaBuf.buf = msg;                   // msg는 스레드마다 가지고 있는 지역변수이므로 공유자원이 아니므로 cs 사용을 하지 않는다
                ioInfo->wsaBuf.len = bytesTrans;
                ioInfo->rwMode = WRITE;
                WSASend((*iter)->hClntSock, &(ioInfo->wsaBuf), 1, NULL, 0, &(ioInfo->overlapped), NULL);
            }
            LeaveCriticalSection(&cs);

            ioInfo = (LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA));
            memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED));
            ioInfo->wsaBuf.len = BUF_SIZE;
            ioInfo->wsaBuf.buf = ioInfo->buffer;
            ioInfo->rwMode = READ;
            WSARecv(sock, &(ioInfo->wsaBuf), 1, NULL, &flags, &(ioInfo->overlapped), NULL);
        }
        else
        {
            puts("message sent!");
            free(ioInfo);
        }
    }
    
    return 0;
}





실행하면 아래처럼 여러사람들에게 메시지가 전송된다.

내가 개인적으로 저장할 용도로 올리는거라서 수정, 추가할 부분이 많다:Q...

( 메시지큐를 이용해서 데이터 받는 부분, 처리부분 분리하기, 패킷 덜 왔을 때 합치기, acceptEx , 메모리풀 등..)



반응형
▲ top