1. socket()를 이용하여 TCP 소켓을 생성
2. connect()를 이용하여 서버와의 연결을 설정
3. send(), recv()를 이용하여 통신을 수행
4. close()를 이용하여 연결을 종료
TCPEchoClient4.c는 IPv4 기반의 TCP에코 클라이언트를 구현한 코드이다.
|
#ifndef __Practical_h__
#define __Practical_h__
#define BUFSIZE 100
void DieWithUserMessage(const char *msg, const char *detail);
void DieWithSystemMessage(const char *msg);
#endif
#include<stdio.h>
#include<stdlib.h>
void DieWithUserMessage(const char *msg, const char *detail)
{
fputs(msg, stderr);
fputs(": ", stderr);
fputs(detail, stderr);
fputc('\n', stderr);
exit(1);
}
void DieWithSystemMessage(const char *msg)
{
perror(msg);
exit(1);
}
IPv4 TCP 서버
서버의 역할은 통신 종단점의 설정과 클라이언트의 접속을 기다리는 것이다. TCP서버는 다음과 같은 4단계의 통신 과정을 일반적으로 가지게 된다.
1. socket()를 통해 TCP 소켓을 생성
2. bind()를 통해 소켓에 포트 번호를 할당
3. listen()을 통해 해당 포트가 연결을 받아들이도록 시스템에 알림
4. 다음과 같은 일을 계속적으로 반복
- accept()를 통해 각 클라이언트와 통신에 필요한 새로운 소켓을 획득
- 새롭게 생성된 클라이언트 소켓에 send(), recv()를 호출하여 통신을 수행
- close()를 통해 클라이언트와 연결을 종료
소켓의 생성, 데이터 송신, 수신, 소켓 종료 등은 클라이언트와 동일하다. 서버가 소켓을 사용하는데 있어서 클라이언트와 다른 점은 주소를 소켓에 바인드(bind)한다는 것이며, 그 소켓을 클라이언트와 연결이 되는 또 다른 소켓을 생성하는 매개체로 이용한다는 것이다. 서버가 클라이언트와 통신하는 과정은 단순하다.
클라이언트로부터 데이터를 받아서 동일한 데이터를 클라이언트에게 다시 돌려보내게 되며, 이 과정은 클라이 언트가 연결을 종료할 때까지 반복한다.
TCPEchoServer4.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Practical.h"
static const int MAXPENDING = 5; //연결 요청을 대기할 수 있는 최대수
int main(int argc, char *argv[])
{
in_port_t servPort;
int servSock; //서버 소켓 식별자
struct sockaddr_in servAddr; //지역 주소
char clntName[INET_ADDRSTRLEN]; //클라이언트 주소를 담기 위한 문자열
if(argc != 2) //명령어 인자의 개수 확인
{
DieWithUserMessage("Parameter(s)", "<Server Port>");
}
servPort = atoi(argv[1]); //첫 번째 인자 : 지역 포트
//연결 요청을 처리하는 소켓 생성
if((servSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
DieWithSystemMessage("socket() failed");
}
//지역 주소 구조체 생성
memset(&servAddr, 0, sizeof(servAddr)); //0으로 구조체 초기화
servAddr.sin_family = AF_INET; //IPv4 주소 패밀리
servAddr.sin_addr.s_addr = htonl(INADDR_ANY); //호스트의 어떠한 IP로도 연결 요청 수락
servAddr.sin_port = htons(servPort); //지역 포트
//지역 주소에 바인드
if(bind(servSock, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0)
{
DieWithSystemMessage("bind() failed");
}
//소켓이 들어오는 요청을 처리할 수 있도록 설정
if(listen(servSock, MAXPENDING) < 0)
{
DieWithSystemMessage("listen() failed");
}
for(;;)
{
struct sockaddr_in clntAddr; //클라이언트 주소
//클라이언트 주소 구조체의 크기설정
socklen_t clntAddrLen = sizeof(clntAddr);
//클라이언트의 연결을 기다림
int clntSock = accept(servSock, (struct sockaddr *) &clntAddr, &clntAddrLen);
if(clntSock < 0)
{
DieWithSystemMessage("accept() failed");
}
//clntSock가 클라이언트와 연결됨!
if(inet_ntop(AF_INET, &clntAddr.sin_addr.s_addr, clntName, sizeof(clntName)) != NULL)
{
printf("Handling client %s/%d\n", clntName, ntohs(clntAddr.sin_port));
}
else
{
puts("Unable to get client address");
}
HandleTCPClient(clntSock);
}
//실행되지 않음
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#define BUFSIZE 100
void HandleTCPClient(int clntSocket)
{
char buffer[BUFSIZE]; //에코 문자열을 위한 버퍼
ssize_t numBytesSent;
ssize_t numBytesRcvd;
//클라이언트로부터 메시지 수신
numBytesRcvd = recv(clntSocket, buffer, numBytesRcvd, 0);
if(numBytesRcvd < 0)
{
DieWithSystemMessage("recv() failed");
}
//수신한 문자열을 전송하고 여분의 데이터를 스트림이 끝날 때까지 수신
while(numBytesRcvd > 0)
{
//클라이언트로 에코 메시지를 돌려보냄
numBytesSent = send(clntSocket, buffer, numBytesRcvd, 0);
if(numBytesSent < 0)
{
DieWithSystemMessage("sent() failed");
}
else if(numBytesSent != numBytesRcvd)
{
DieWithSystemMessage("sent()", "sent unexpected number of bytes");
}
//받을 수 있는 데이터가 더 남아 있는지 확인
numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0);
if(numBytesRcvd < 0)
{
DieWithSystemMessage("recv() failed");
}
}
close(clntSocket); //클라이언트 소켓 종료
}
실행
먼저 각 함수들 *.o파일을 생성한다.
gcc -c 함수입력한파일.c
그러면 함수입력한파일.o가 생성된다.
그 다음 server와 client실행파일 생성
예) gcc -o TCPEchoServer4 TCPEchoServer4.c HandleTCPClient.o
예) gcc -o TCPEchoClient4 TCPEchoClient4.c DieWithMessage.o
이렇게 실행파일 생성 후
서버 실행
TCPEchoServer4 포트번호
클라이언트 실행
TCPEchoClient4 IP주소 "Hello world" 포트번호
결과
'TCP/IP' 카테고리의 다른 글
프로세스간 통신 (0) | 2011.07.20 |
---|---|
양방향 통신(1:1 대화) (0) | 2011.07.19 |
inet_addr()함수, (=inet_aton) (0) | 2011.07.01 |
호스트pc(리틀엔디안) <-> 네트워크(빅엔디안) (0) | 2011.07.01 |
gethostbyname() 함수 (0) | 2011.07.01 |