해당 게시글은 한동대학교 고윤민 교수님의 자료를 바탕으로 제작되었습니다.
https://github.com/YunminGo/netprog
해당 자료는 고윤민 교수님 캠프의 실습 자료들입니다.
Socket
앞서서 이야기했듯이 TCP / UDP와 밀접한 관계가 있다. 또한 이것들은 IP address와 Port Number로 어디에 있는지 위치를 파악을 할 수 있는데 각각의 socket은 다음과 같은 다섯개의 구성요소로 이루어져 있다.
Protocol, Source address, Source port, Destination Address, Destination port
각각 해당 구성요소에 접근하기 위한 function들이다.
protocol: socket()
Source address, and port: bind()
Destinatin address, and prot : connect(), sendto()
위와 같은 여러 함수들은 밑에서 다뤄보기로 하자. 그러면 우선 TCP와 관련하여 이야기 해보겠다
TCP socket programming
TCP는 다음과 같은 구조로 통신을 한다.
TCP Server/Client Function Call
우선 각각 server와 client에서 socket()이라는 함수를 사용하여 socket을 생성해준다. 그러한 다음 server의 bind() 함수에서 자기 자신의 주소 정보를 mapping한다. 그 후에는 어떤 request가 올지 client로 부터 대기하는 listen()을 시행한다. 이후에 client에서 connent() 함수를 사용하여 Destination Address(Server)을 연결한다.
그렇게 server는 request를 받게 되고 listen()에서 accpet()함수로 그 요청을 리턴받을 수 있다. 이후에 read()나 write()함수를 이용하여 server와 client 간의 data 통신이 이루어진다. 모든 활동이 종료되었다면 close()함수를 사용하여 연결한 socket을 끊어주면 통신은 종료된다.
실습을 통해서 살펴보겠다.
Example: Session03/tcp_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
void error_handling(char *message);
int main(int argc, char *argv[])
{
int serv_sock;
int clnt_sock;
struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size;
char message[] = "Hello World!";
if (argc != 2){
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
//socket()
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
if (serv_sock == -1)
error_handling("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(argv[1]));
//bind()
if (bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)
error_handling("bind() error");
//listen()
if (listen(serv_sock, 5) == -1)
error_handling("listen() error");
clnt_addr_size = sizeof(clnt_addr);
//accept()
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr,&clnt_addr_size);
if (clnt_sock == -1)
error_handling("accept() error");
//write()
write(clnt_sock, message, sizeof(message));
//close()
close(clnt_sock);
close(serv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
Example: Session03/tcp_client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
void error_handling(char *message);
int main(int argc, char* argv[])
{
int sock;
struct sockaddr_in serv_addr;
char message[30];
int str_len = 0;
int idx = 0, read_len = 0;
if (argc != 3) {
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}
//socket()
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1)
error_handling("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(atoi(argv[2]));
//connent()
if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
error_handling("connect() error!");
//read()
while (read_len = read(sock, &message[idx++], 1))
{
if (read_len == -1)
{
error_handling("read() error!");
break;
}
str_len += read_len;
}
printf("Message from server: %s \n", message);
printf("Function read call count: %d \n", str_len);
//close
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
앞서서 이야기했듯이 server와 client 간의 통신이 있어야 하기에 terminal을 두개 동시에 사용하였습니다.
./tcp_server [Port Number]
./tcp_client [Server IP Address] [Server Port Number]
다음과 같이 argument에 넣어서 사용해보았습니다. 다음과 같이 서버에 저장되어있는 "Hellow World!"라는 문장을 client로 받아와 잘 출력되어 나오는 것을 알 수 있었습니다. 이제 ip 주소만 알고 있다면 이렇게 직접적으로 소통할 수 있습니다.
그러면 이어서 다음 실습으로 넘어가겠습니다.
Simple Echo Server & Client 구현
• 1. 클라이언트가 입력하는 문자열을 서버로 전송
• 2. 서버에서는 문자열을 수신하고, 수신 문자열을 화면에 출력
• 3. 수신한 문자열을 그대로 클라이언트에게 전송
• 4. 클라이언트는 서버로부터 수신한 문자열을 화면에 출력
다음과 같은 맥락으로 구현하는 것이 목표입니다.
저는 아래와 같이 구현을 하였고 이와 관련된 함수들은 다른 게시글을 통해 작성할 예정이오니 참고바랍니다.
Example: Session03/simple_echo_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
void error_handling(char *message);
int main(int argc, char *argv[])
{
int sock ;
int serv_sock, clnt_sock;
char message[BUF_SIZE];
int str_len;
int idx = 0, read_len = 0 ;
struct sockaddr_in serv_adr;
struct sockaddr_in clnt_adr;
socklen_t clnt_adr_sz;
if (argc != 2) {
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
// TODO: socket()
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
if (serv_sock == -1)
error_handling("socket() error");
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(atoi(argv[1]));
// TODO: bind()
if (bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr)) == -1)
error_handling("bind() error");
printf("Server Listen...\n");
// TODO: listen()
if (listen(serv_sock, 5) == -1)
error_handling("listen() error");
clnt_adr_sz = sizeof(clnt_adr);
// TODO: accept()
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr,&clnt_adr_sz);
if (clnt_sock == -1)
error_handling("accept() error");
puts("Client is connected!");
// TODO: 2. read message from client
str_len = read(clnt_sock, message, BUF_SIZE-1) ;
printf("Client's message: %s", message);
// TODO: 3. write message to client
write(clnt_sock, message, str_len) ;//sizeof(message));
close(clnt_sock);
close(serv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
Example: Session03/simple_echo_client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
void error_handling(char *message);
int main(int argc, char *argv[])
{
int sock;
char message[BUF_SIZE];
int str_len;
int idx = 0, read_len = 0;
struct sockaddr_in serv_adr;
if (argc != 3) {
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_STREAM, 0);
if(sock==-1)
error_handling("socket() error");
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = inet_addr(argv[1]);
serv_adr.sin_port = htons(atoi(argv[2]));
// TODO: connect()
if (connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1)
error_handling("connect() error!");
else
puts("Client is connected!");
fputs("Input message: ", stdout);
fgets(message, BUF_SIZE, stdin);
// TODO: 1. write message to server
write(sock, message, sizeof(message));
// TODO: 4. read message from server
str_len = read(sock, message, BUF_SIZE-1) ;
message[str_len] = 0 ;
printf("Message from server: %s", message);
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
연결이 완료된 후에 client에서 Input message: 를 출력하여 메세지 입력을 기다렸고 그렇게 입력된 메세지가 server로 이동하고 다시 client로 이동하는 것을 볼 수 있습니다.
해당 결과는 아래 github를 통해 보실 수 있습니다.
https://github.com/gurcks8989/NetworkProgramming/tree/master/Session03