채팅프로그래밍을 서버와 클라이언트 창을 하나로할려면 어떻게해야하죠 ??
사이
서버와 클라이언트 따로 실행시켜서 다른컴퓨터와 1:1채팅을 하였습니다. 이것을 하나의 프로젝트로 콘솔창 하나로 서버와 클라이언트에서 데이터를 주고받을순 없나요?
#include winsock2.h
#include stdio.h
#include stdlib.h
void ErrorDisplay(char *szError);
void ErrorPrint(char *szError);
void main()
{
int ret;
WSADATA wsa;
SOCKET s;
SOCKADDR_IN saddr;
SOCKADDR_IN caddr;
int caddrlen;
SOCKET cs;
int nSend;
/*
함수 : int WSAStartup(WORD wVersionRezuested, LPWSADATA lpWSAData);
기능 : WS2_32.DLL의 사용을 초기화 한다.
인수 : 버전, WSADATA 구조체 번지.
셜명 : 보통 winsock 2.2를 사용, 설정을 위하여 MAKEWORD(2,2)를 사용
반환 : 성공 시 0, 실패 시 에러 코드
주의 : 이 함수는 특별한 경우가 아니면, 한 프로그램에 한 번만 호출 된다.
*/
ret = WSAStartup(MAKEWORD(2,2),&wsa);
if(ret != 0){
ErrorDisplay(WSAStartup);
}
/*
함수 : SOCKET socket(lnt af,int type,int protocol);
기능 : 소켓을 생성
인수 : 1. AF_INET == TCP/IP,UDP일 경우 사용,
2. SOCK_STREAM(TCP/IP) 또는 SOCK_DGRAM(UDP)
3. IPPROTO_TCP(TCP/IP) 또는 IPPROTO_UDP(UDP),일반적으로 0사용
설명 :소켓 디스크립터를 생성하고 , 필요 자원을 할당한다.
반환 : 성공 시 소켓 디스크립터, 실패시 INVALID_SOCKET
*/
s = socket(AF_INET,SOCK_STREAM,0);
if(s==INVALID_SOCKET){
ErrorDisplay(socket());
}
/*
함수 : int bind(SOCKET s, const struct sockaddr FAR* name,int namelen);
기능 : 생성된 소켓 s에 네트워크 주소와 포트 번호를 연결한다.
인수 : 1. socket 함수에 의해 생성된 소켓 디스크립터(s)
2. SOCKADDR_IN 구조체.(SOCKADDR* 또는 struct sockaddr FAR*)
구조체는 주소체계(AF_INET),port,네트워크 주소로 채워져야 함.
3. SOCKADDR_IN 구조체의 크기
설명 : 생성된 소켓 s 디스크립터에 포트번호와 네트워크 주소를 설정.
반환 : 성공 시 0, 실패시 SOCKET_ERROR
보충 : htons, htonl은 호스트의 바이트 순서를 네트워크 바이트 순서로 바꿔주는
함수. inet_addr()함수는 네트워크 주소를 4바이트의 IN_ADDR구조로 바꿔준다.
*/
ZeroMemory(&saddr,sizeof(saddr));
saddr.sin_family = AF_INET; // 주소체계
saddr.sin_port = htons(5000); // 포트 번호
saddr.sin_addr.s_addr = htonl(INADDR_ANY); // 네트워크 카드 설정
//saddr.sin_addr.s_addr = inet_addr(127.0.0.1);
ret = bind(s,(SOCKADDR *)&saddr,sizeof(saddr));
if(ret == SOCKET_ERROR){
ErrorDisplay(bind());
}
/*
함수 : int listen(SOCKET s,int backing);
기능 : 클라이언트 접속을 받을 수 있는 대기 상태로 진입한다.
인수 : 1. socket 함수에 의해 생성된 소켓 디스크립터
2. 동시에 접근 가능한 최대 클라이언트수.
설명 : 이 함수를 호출 하면 클라이언트가 접속 할 수 있다.
반환 : 성공 시 0, 실패시 SOCKET_ERROR
주의 :backog 값이 5이면 , 동시 접속자 수는 5이기 때문에 경우에 따라
사용자가 많은 서버 에서는 접속 실패가 빈번하게 일어남.
윈도우 환경에서 이값은 SOMAXCONN이 권장,
UNIX나 Linux에서는 해당 시스템에 맞게 값을 적절히 조정해야함.
*/
ret = listen(s,SOMAXCONN);
if(ret == SOCKET_ERROR){
ErrorDisplay(listen());
}
while(TRUE){
/*
함수 : SOCKET accept(SOCKET s, struct sockaddr FAR* addr,int FAR* addrlen);
기능 : 접속된 클라이언트를 받아 들인다.
인수 : 1. socket 함수에 의해 생성된 소켓 디스크립터
2. SOCKADDR_IN 구조체.구조체는 주소체계(AF_INET),port,네트워크 주소로 채워진다.
3.SOCKADDR_IN 구조체의 크기
설명 : listen 함수에 의해 클라이언트의 접속이 허용되면, 클라이언트가 접속해온다.
accept 삼수는 클라이언트가 접속할 경우 ,그와 연관된 소켓을 생성하여 반환한다.
반환 : 성공 시 새로 생성된 SOCKET 디스크립터, 실패 시 INVAID_SOCKET
주의 : accept 함수는 블로킹 함수이다. 블로킹이란 함수 호출 시 더 이상 진행하지 않고
함수 내에서 멈춰져 있다는 것.
*/
caddrlen =sizeof(caddr);
cs = accept(s,(SOCKADDR*)&caddr,&caddrlen);
if(cs == INVALID_SOCKET){
ErrorPrint(accept());
continue;
}
/************************************************************************/
/* 클라이언트 정보 표시
inet_ntoa() 함수는 IN_ADDR 구조체의 값을 문자열로 변환 해준다.
ntohs() 함수는 네트워크 바이트 오더의 포터 번호를 호스트에
맞게 변환 해준다. */
/************************************************************************/
printf(클라이언트 IP : %s\n,inet_ntoa(caddr.sin_addr));
printf(클라tf(클라이언트 port: %d\n,ntohs(caddr.sin_port));
//클라이언트의 데이터 수신 및 재송신(Echo Server)
while(TRUE){
char buf[1024];
int nRecv;
memset(buf,0,sizeof(buf));
/************************************************************************/
/* 함수 :int recv(SOCKET s, char FAR* buf,int len,int flags);
기능 : TCP/IP를 통해 데이터를 수신한다.
인수 : 1.accept 함수에 의해 생성된 소켓 디스크립터
2.데이터를 수신할 버퍼
3.수신할 바이트의 최대값
4.일반적으로는 0,MSG_REEK또는 MSG_OOB사용 가능.
MSG_PEEK는 데이터를 읽어오고 수신 버퍼를 비우지 않는다.
일반적으로는 수신 버퍼는 데이터를 읽어온 후 비어진다.
설명 : 데이터를 수신한다.
반환 : 성공 시 수신된 바이트 수(항상 0보다 크다),실패 시 SOCKET_ERROR.
클라이언트와 접속이 단절 시 0.
주의 : recv 함수는 블로킹 함수 */
/************************************************************************/
nRecv = recv(cs,buf,sizeof(buf),0);
if(nRecv == SOCKET_ERROR){
ErrorPrint(recv());
break;
}
else if(nRecv == 0){
printf(클라이언트와의 접속이 끊어졌습니다.\n);
}
else{
//받은 데이터 출력
printf(%d 바이트 수신 :%s\n,nRecv,buf);
/************************************************************************/
/* 함수 : int send(SOCKET s,const char FAR *buf,int len,int flags);
기능 : TCP/IP를 통해 데이터를 전송한다.
인수 : 1. accept 함수에 의해 생성된 소켓 디스크립터
2. 데이터를 전송할 버퍼.
3. 전송할 바이트 수
4. 일반적으로는 0,MSG_DONTROUTE 또는 MSG_OOB 사용가능
MSG_DONTROUTE는 로클 라우팅 테이블을 참조 하지 않도록 설정.
MSG_OOB는 긴급 데이터를 보낼 때 사용, 잘사용하지 않는다.
설명 : 데이터를 전송한다.
반환 : 성공 시 전송된 바이트 수, 실패 시 SOCKET_ERROR.
주의 : send 함수는 블로킹 함수 이다. */
/************************************************************************/
nSend = send(cs,buf,nRecv,0);
if(nSend == SOCKET_ERROR){
ErrorPrint(send());
break;
}
}
}
/************************************************************************/
/* 함수 : int closesocket( SOCKET s );
기능 : 소켓을 닫고, 자원을 해제한다.
인수 : 1. 닫을 소켓 디스크립터(s 또는 cs)
설명 : 소켓을 닫는다.
반환 : 성공 시 0, 실패 시 SOCKET_ERROR.
주의 : closesocket 함수 사용 시 SO_LINGER 옵션과
SO_DONTLINGER 옵션의 영향을 받기 때문에 주의하자.
아래의 표에 보면,
SO_LINGER의 설정 값이 l_onoff는 0이고, l_linger도 0일 때에는
즉시 닫힘(hard close)이 발생하여 소켓은 즉시 제거되고,
대기 중인 송수신 데이터는 모두 잃게 된다.
안전한 데이터 송수신의 종료를 위해서는 shutdown 함수를 사용하는 것이 좋다.
----------------------------------------------------------------------------------
옵션 | 타임아웃간격 | 닫힘유형 | 닫힘대기여부 | 비 고
----------------------------------------------------------------------------------
SO_DONTLINGER | 상관 없음 | 정상 | 블록 안됨 | 데이터 안전 전송
SO_LINGER | 0 | hard | 블록 안됨 | 데이터 손실 가능
SO_LINGER | 0 | 정상 | 블록 됨 | 일정시간 데이터 전송
----------------------------------------------------------------------------------
SO_LINGER 옵션은 다음과 같이 설정할 수 있다. 이렇게 설정하면 closesocket 함수
사용 시 60초간 대기하면서 큐에 있는 데이터를 전송한다. 만약 전송이 완료되면,
60초가 되기 전에 블록이 해제된다. 중요한 데이터를 송신 중일 때는 이 방법을
사용하는 것이 좋다. 물론 closesocket 함수는 기본적으로 SO_DONTLINGER 옵션이
있기 때문에 이 방법을 사용하지 않아도 된다.
LINGER linger = { 1, 60 }; // 1(기능 사용), 종료 시 60 초간 대기 설정
if( setsockopt( s, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger) ) != 0 )
{
printf( 종료 설정 실패, 에러코드 = %d \n, WSAGetLastError() );
closesocket( s ); // 소켓 제거
WSACleanup(); // WS2_32.DLL 사용 종료
return;
} */
/************************************************************************/
closesocket( cs );
}
// 서버 접속용 소켓을 해제한다.
closesocket( s );
/************************************************************************/
/* 함수 : int&nbs int WSACleanup( void );
기능 : WS2_32.DLL의 사용을 종료한다.
인수 : 1. 닫을 소켓 디스크립터(s 또는 cs)
설명 : WS2_32.DLL의 사용을 종료한다.
반환 : 성공 시 0, 실패 시 SOCKET_ERROR.
주의 : 프로그램이 닫혀지기 전에 한 번만 호출하면 된다.
만약 프로그램이 실행 중에 이 함수를 호출한다면 모든 소켓은 종료된다. */
/************************************************************************/
WSACleanup();
}
/************************************************************************/
/*
함수 : void ErrorDisplay( char *szError )
기능 : 메시지 박스에 에러를 표시하고, 프로그램을 종료(exit)한다.
인수 : WSAGetLastError()-가장 최근에 발생한 소켓 에러의 번호를 리턴함
설명 : FormatMessage 함수는 내부적으로 버퍼를 할당하고, 가장 최근에 발생한
에러에 대한 메시지를 생성한다. 생성된 메시지의 버퍼는 포인터 변수인
lpBuff가 받아 온다.
주의 : FormatMessage 함수는 내부적으로 메모리를 할당하기 때문에 LocalFree
함수를 호출하여 메모리를 해제하여야 한다. */
/************************************************************************/
void ErrorDisplay(char *szError)
{
LPVOID lpBuff;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
WSAGetLastError(), // 에러 코드를 읽어 옴
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpBuff,
0,
NULL );
MessageBox( NULL, (LPCTSTR)lpBuff, szError, MB_ICONSTOP );
LocalFree( lpBuff );
exit( 0 ); // 프로그램을 종료
}
/************************************************************************/
/*
함수 : void ErrorPrint( char *szError )
기능 : 메시지 박스에 에러를 표시하고, 프로그램을 종료(exit)한다.
인수 : WSAGetLastError()-가장 최근에 발생한 소켓 에러의 번호를 리턴함
설명 : FormatMessage 함수는 내부적으로 버퍼를 할당하고, 가장 최근에 발생한
에러에 대한 메시지를 생성한다. 생성된 메시지의 버퍼는 포인터 변수인
lpBuff가 받아 온다.
주의 : FormatMessage 함수는 내부적으로 메모리를 할당하기 때문에 LocalFree
함수를 호출하여 메모리를 해제하여야 한다. */
/************************************************************************/
void ErrorPrint(char *szError)
{
LPVOID lpBuff;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
(LPTSTR)&lpBuff,
0,
NULL);
printf(%s : %s\n,szError,lpBuff);
LocalFree(lpBuff);
}
------------------------------클라이언트 ------------------------------------
#include winsock2.h
#include stdio.h
#include stdio.h
void ErrorDisplay(char *szError);
void ErrorPrint(char *szError);
void main()
{
int ret;
WSADATA wsa;
SOCKET s;
SOCKADDR_IN saddr;
ret=WSAStartup(MAKEWORD(2,2),&wsa);
if(ret != 0){
ErrorDisplay(WSAStartup);
}
s = socket(AF_INET,SOCK_STREAM,0);
if(s == INVALID_SOCKET){
ErrorDisplay(socket());
}
/************************************************************************/
/* 함수 : int connect( SOCKET s, const struct sockaddr FAR* name, int namelen );
기능 : 서버에 접속한다.
인수 : 1. socket 함수에 의해 생성된 소켓 디스크립터(s)
2. SOCKADDR_IN 구조체. (SOCKADDR*는 struct sockaddr FAR*)
구조체는 주소체계(AF_INET), Port, 네트워크 주소로 채워진다.
3. SOCKADDR_IN 구조체의 크기
설명 : 대기 중인 서버에 접속한다. 접속 IP는 127.0.0.1로 한다.
반환 : 성공 시 0, 실패 시 SOCKET_ERROR
주의 : connect 함수는 블로킹 함수이다. 접속이 완료되었을 때 또는 실패 되었을 때만
함수는 리턴한다. */
/************************************************************************/
saddr.sin_family = AF_INET;
saddr.sin_port = htons(5000);
saddr.sin_addr.s_addr = inet_addr(127.0.0.1);
ret = connect(s,(SOCKADDR*)&saddr,sizeof(saddr));
if(ret != 0){
ErrorDisplay(connect());
}
while(TRUE){
char buf[1024];
int nSend;
int nRecv;
memset( buf, 0, sizeof buf );
gets( buf );
// scanf(%s,buf); //공백 포함을 안함..!!
nSend = send(s,buf,strlen(buf),0);
if(nSend == SOCKET_ERROR){
ErrorPrint(send());
break;
}
nRecv = recv(s,buf,sizeof(buf),0);
if(nRecv == SOCKET_ERROR){
ErrorPrint(recv());
break;
}
else if(nRecv == 0){
printf(클라이언트와 접속이 끊어졌습니다.\n);
break;
}
else{
printf( %d 바이트 수신 : %s \n, nRecv, buf );
}
}
closesocket( s );
WSACleanup();
}
void ErrorPrint(char *szError)
{
LPVOID lpBuff;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
(LPTSTR)&lpBuff,
0,
NULL);
printf(%s : %s\n,szError,lpBuff);
LocalFree(lpBuff);
}
void ErrorDisplay(char *szError)
{
LPVOID lpBuff;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
WSAGetLastError(), // 에러 코드를 읽어 옴
MAKELAN MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpBuff,
0,
NULL );
MessageBox( NULL, (LPCTSTR)lpBuff, szError, MB_ICONSTOP );
LocalFree( lpBuff );
exit( 0 ); // 프로그램을 종료
}
번호 | 제 목 | 글쓴이 | 날짜 |
---|---|---|---|
2676182 | 숫자 순서대로 배열하는법 | 권뉴 | 2024-11-24 |
2676152 | 기본적인거 하나 질문드립니다. | 개미 | 2024-11-24 |
2676124 | 함수선언관련 질문이에요~...털썩..수정완료 (2) | 가지 | 2024-11-24 |
2676092 | C언어 책 (2) | 아서 | 2024-11-24 |
2676065 | 웹사이트 또는 메신저 등에서 원하는 텍스트를 검사하는방법?? (1) | 모든 | 2024-11-23 |
2676033 | 배열 기초연습중 발생하는 에러 ㅠㅜ... | Creative | 2024-11-23 |
2676005 | keybd_event 게임 제어 | 영글 | 2024-11-23 |
2675900 | 진짜기본적인질문 | 글길 | 2024-11-22 |
2675845 | 수정좀해주세요ㅠㅠㅠ | 해골 | 2024-11-21 |
2675797 | 병합 정렬 소스 코드 질문입니다. (2) | 도래솔 | 2024-11-21 |
2675771 | 큐의 활용이 정확히 어떻게 되죠?? | 해긴 | 2024-11-21 |
2675745 | 도서관리 프로그램 질문이요 | 도리도리 | 2024-11-20 |
2675717 | 2진수로 변환하는것! (3) | 동생몬 | 2024-11-20 |
2675599 | for문 짝수 출력하는 법 (5) | 널위해 | 2024-11-19 |
2675575 | Linux 게시판이 없어서.. | 첫삥 | 2024-11-19 |
2675545 | 구조체 이용할 때 함수에 자료 넘겨주는 것은 어떻게 해야 하나요? | 아연 | 2024-11-19 |
2675518 | 사각형 가로로 어떻게 반복해서 만드는지좀.. 내용 | 신당 | 2024-11-18 |
2675491 | !느낌표를 입력하는것은 어떻게합니까~~?ㅠㅠ (5) | 사지타리우스 | 2024-11-18 |
2675411 | 파일입출력으로 받아온 파일의 중복문자열을 제거한 뒤 파일출력 | 앨버트 | 2024-11-17 |
2675385 | 링크드리스트 주소록 질문드립니다. (1) | 겨루 | 2024-11-17 |