• 검색 결과가 없습니다.

TCP 소켓

N/A
N/A
Protected

Academic year: 2023

Share "TCP 소켓"

Copied!
41
0
0

로드 중.... (전체 텍스트 보기)

전체 글

(1)

TCP 소켓

인터넷 프로토콜 02장

(2)

목차

 제 2장 TCP 소켓

 2.1 IPv4 TCP 클라이언트

 2.2 IPv4 TCP 서버

 2.3 소켓의 생성과 해지

 2.4 주소지정

 2.5 소켓에 연결

 2.6 소켓을 주소에 바인딩하기

 2.7 클라이언트의 연결 요청 처리

 2.8 데이터 주고받기

(3)

소켓 통신 과정

 간략화한 소켓 통싞 과정

 소켓 생성

 TCP or UDP

 소켓에 주소 정보 할당

 IP address, Port number

 소켓 연결

 클라이언트 소켓과 서버 소켓 연결

 bind(), listen(), connect(), accept()

 데이터 젂송

(4)

TCP/IP 소켓의 생성

소켓 생성

어떠한 소켓을 생성할 것인가를 명시(프로토콜 종류)

TCP/IP 소켓의 경우

Socket 식별자

UNIX의 파일 식별자와 동일

Windows의 경우, WinSock에서 사용하는 소켓 핸들

 Windows의 경우, 파일 핸들과 같지 않음

Family Type Protocol

TCP PF_INET SOCK_STREAM IPPROTO_TCP

UDP SOCK_DGRAM IPPROTO_UDP

int socket(int family,int type,int proto);

(5)

TCP/IP 소켓 식별자

 유닉스/리눅스 계열에서 식별자 공간

Descriptor Table

0 1 2 3 4

Data structure for file 0 Data structure for file 1

Family: PF_INET

Service: SOCK_STREAM Local IP: 111.22.3.4

Remote IP: 123.45.6.78

(6)

TCP/IP 소켓의 주소 지정 (1)

 struct sockaddr 사용

 여러 가지 프로토콜을 사용하기 때문에 1) 프로토콜 종류,

2) 주소를 지정해야 함

 TCP/IP의 경우는 인터넷 주소임을 알리는 AF_INET, IP 주소,

Port 번호가 필요

 IP : IPv4 주소 형식과 IPv6 주소 형식으로 나뉨

 Ports : TCP/UDP 관계없이 0~65535

사이의 값 사용

(7)

TCP/IP 소켓의 주소 지정 (2)

 범용(Generic) 소켓 주소 구조체

 IPv4 에 사용되는 소켓 주소 구조체

struct sockaddr {

unsigned short sa_family; /* Address family (e.g., AF_INET) */

char sa_data[14]; /* Protocol-specific address information */

};

struct sockaddr_in {

unsigned short sin_family; /* Internet protocol (AF_INET) */

unsigned short sin_port; /* Port (16-bits) */

struct in_addr sin_addr; /* Internet address (32-bits) */

char sin_zero[8]; /* Not used */

};

struct in_addr {

unsigned long s_addr; /* Internet address (32-bits) */

};

sockaddr Family Blob

(8)

TCP/IP 소켓의 주소 지정 (3)

 IPv6 에 사용되는 소켓 주소 구조체

 모든 종류의 sockaddr을 수용하기 위한 구조체

struct sockaddr_in6 {

sa_family_t sin6_family; // Internet protocol(AF_INET6) in_port_t sin6_port; // Address port(16bits)

uint32_t sin6_flowinfo; // Flow information struct in6_addr sin6_addr; // IPv6 address(128bits) uint32_t sin6_scope_id; // Scope identifier };

struct in_addr{

uint32_t s_addr[16]; // Internet address(128bits) };

struct sockaddr_storage {

(9)

주소 정보를 소켓에 할당

 bind() 를 사용하여 주소 정보를 생성된 소켓에 할당

 성공 시 ‘0’, 실패 시 ‘-1’

int mysock,err;

struct sockaddr_in myaddr;

char* servIP; /* ex)203.252.164.143 */

mysock = socket(PF_INET,SOCK_STREAM,0);

myaddr.sin_family = AF_INET;

myaddr.sin_port = htons( portnum );

myaddr.sin_addr.s_addr = inet_addr(servIP);

int bind( int sockfd, struct sockaddr *localaddr, int addrlen);

(10)

클라이언트 – 서버 통신

 클라이언트가 먼저 서버에게 연결 요청

 서버의 프로세스는 미리 소켓을 열고 대기하고 있어야 함

 서버는 특정 포트를 열고 대기하여야 하며 클라이언트는 이 포트를 알고 있어야 함

 클라이언트는 서버에 연결

 이때 클라이언트는 서버의 IP, Port 정보를

응용 프로그램에게 명시하여 접속 가능

(11)

TCP 연결 흐름도

다음 클라이언트로부터 연결 요청을

기다림

연결 요청

(12)

서버의 연결 대기 함수- listen()

 TCP 와 같은 연결 지향 서버에 사용

 소켓의 상태를 대기 상태로 바꿈

 socket: 생성된 소켓의 식별자

 queuelimit : 연결을 수행 중에 다른 연결이 들어오면 연결 요청을 queue에 넣고 보류, 이때 사용하는 queue의 크기

int listen(int socket, int queuelimit);

(13)

서버의 연결 대기 함수- accept()

 listen() 호출 후, accept()를 수행하면

 클라이언트의 연결 요청(connect())에 대해 응답합

 passive open

 클라이언트와 데이터 송수싞(send/recv)이 가능한 새로운 소켓 식별자를 반홖

int accept(int socket, struct sockaddr *clientdaddress, int *addr_len);

(14)

클라이언트의 연결 함수 - Connect()

 클라이언트는 connect()를 호출하여 연결의 상태를 „active open‟으로 만듬

 foreignAddress는 서버의 IP, port를 담고 있는 주소 구조체

int connect(int socket, struct sockaddr *foreignAddress, int addr_len);

(15)

Send(to), Recv(from)

 연결이 이루어진 후에는 send/recv를 이용하여 데이터의 송수싞이 가능

int send(int socket, char *message, int msg_len, int flags);

주어진 소켓을 통하여 message의 송싞이 가능

int recv(int scoket, char *buffer, int buf_len, int flags)

주어진 소켓을 통해 주어진 buffer에 데이터를 수싞

(16)

클라이언트와 서버의 통신

 클라이언트 : 연결을 초기화 하는 주체

서버 : 수동적으로 연결을 기다림

Client: Bob

“Hi. I‟m Bob.”

Server: Jane

“Hi, Bob. I‟m Jane”

“Nice to meet you, Jane.”

(17)

TCP 상의 서버/클라이언트 통신

 서버는 클라이언트의 연결을 받아들일 준비를 하고 시작

클라이언트

1. TCP 소켓 생성 2. 연결 설정

서버

1. TCP 소켓 생성 2. 소켓에 포트 할당

3. 소켓 상태를 대기(listen)로 변경 4. 다음을 반복적으로 수행

a. 새로운 연결을 받아들임

(18)

TCP 상의 서버/클라이언트 통신

/* Create socket for incoming connections */

if ((servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) DieWithSystemMessage("socket() failed");

클라이언트

서버

1. TCP 소켓 생성

2. 소켓에 포트 할당

3. 소켓 상태를 대기(listen)로 변경 4. 다음을 반복적으로 수행

(19)

TCP 상의 서버/클라이언트 통신

echoServAddr.sin_family = AF_INET; /* Internet address family */

echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY);/* Any incoming interface */

echoServAddr.sin_port = htons(echoServPort); /* Local port */

if (bind(servSock,(struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0) DieWithSystemMessage("bind() failed");

클라이언트

1. TCP 소켓 생성 2. 연결 설정

서버

1. TCP 소켓 생성

2. 소켓에 포트 할당

3. 소켓 상태를 대기(listen)로 변경 4. 다음을 반복적으로 수행

a. 새로운 연결을 받아들임

(20)

TCP 상의 서버/클라이언트 통신

/* Mark the socket so it will listen for incoming connections */

if (listen(servSock, MAXPENDING) < 0)

DieWithSystemMessage("listen() failed");

클라이언트

서버

1. TCP 소켓 생성 2. 소켓에 포트 할당

3. 소켓 상태를 대기(listen)로 변경

4. 다음을 반복적으로 수행

(21)

TCP 상의 서버/클라이언트 통신

for (;;) /* Run forever */

{

clntLen = sizeof(echoClntAddr);

if ((clntSock=accept(servSock,(struct sockaddr *)&echoClntAddr,&clntLen)) < 0) DieWithError("accept() failed");

클라이언트

1. TCP 소켓 생성 2. 연결 설정

서버

1. TCP 소켓 생성 2. 소켓에 포트 할당

3. 소켓 상태를 대기(listen)로 변경 4. 다음을 반복적으로 수행

a.

새로운 연결을 받아들임

(22)

TCP 상의 서버/클라이언트 통신

•서버는 이 시점에서 클라이언트의 연결을 처리하기 위해서 대기

•클라이언트는 서버에 연결 시도

클라이언트

서버

1. TCP 소켓 생성 2. 소켓에 포트 할당

3. 소켓 상태를 대기(listen)로 변경 4. 다음을 반복적으로 수행

(23)

TCP 상의 서버/클라이언트 통신

/* Create a reliable, stream socket using TCP */

if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) DieWithSystemMessage("socket() failed");

클라이언트

1. TCP 소켓 생성

2. 연결 설정

서버

1. TCP 소켓 생성 2. 소켓에 포트 할당

3. 소켓 상태를 대기(listen)로 변경 4. 다음을 반복적으로 수행

a.

새로운 연결을 받아들임

(24)

TCP 상의 서버/클라이언트 통신

echoServAddr.sin_family = AF_INET; /* Internet address family */

echoServAddr.sin_addr.s_addr = inet_addr(servIP); /* Server IP address */

echoServAddr.sin_port = htons(echoServPort); /* Server port */

if (connect(sock,(struct sockaddr *)&echoServAddr, sizeof(echoServAddr)) < 0) DieWithSystemMessage ("connect() failed");

클라이언트

서버

1. TCP 소켓 생성 2. 소켓에 포트 할당

3. 소켓 상태를 대기(listen)로 변경 4. 다음을 반복적으로 수행

(25)

TCP 상의 서버/클라이언트 통신

if ((clntSock=accept(servSock,(struct sockaddr *)&echoClntAddr,&clntLen)) < 0) DieWithError("accept() failed");

클라이언트

1. TCP 소켓 생성

2. 연결 설정

서버

1. TCP 소켓 생성 2. 소켓에 포트 할당

3. 소켓 상태를 대기(listen)로 변경 4. 다음을 반복적으로 수행

a.

새로운 연결을 받아들임

(26)

TCP 상의 서버/클라이언트 통신

echoStringLen = strlen(echoString); /* Determine input length */

/* Send the string to the server */

if (send(sock, echoString, echoStringLen, 0) != echoStringLen) DieWithUserMessage ("send() sent a different number of bytes than expected");

클라이언트

서버

1. TCP 소켓 생성 2. 소켓에 포트 할당

3. 소켓 상태를 대기(listen)로 변경 4. 다음을 반복적으로 수행

(27)

TCP 상의 서버/클라이언트 통신

/* Receive message from client */

if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0) DieWithSystemMessage("recv() failed");

클라이언트

1. TCP 소켓 생성 2. 연결 설정

서버

1. TCP 소켓 생성 2. 소켓에 포트 할당

3. 소켓 상태를 대기(listen)로 변경 4. 다음을 반복적으로 수행

a. 새로운 연결을 받아들임

(28)

TCP 상의 서버/클라이언트 통신

close(sock); close(clntSocket);

클라이언트

서버

1. TCP 소켓 생성 2. 소켓에 포트 할당

3. 소켓 상태를 대기(listen)로 변경 4. 다음을 반복적으로 수행

(29)

TCP 데이터 교환

 클라이언트는 사젂에 서버의 주소 정보(IP, port) 를 알아야함

 서버는 클라이언트가 접속할 포트만 정하고 있음

 send() 와 recv() 간에는 어떠한 정해진 룰이 없음

Client

send(“Hello Bob”)

Server

recv() -> “Hello ” recv() -> “Bob”

send(“Hi ”)

(30)

연결 종료

 연결을 종료하기 위해서 close()를 사용

 파일의 EOF와 유사

echo Client

send( string )

while (not received entire string) recv( buffer )

print( buffer )

echo Server

recv( buffer )

while(client has not closed connection)

send( buffer )

recv( buffer )

(31)

Practical.h

#ifndef PRACTICAL_H_

#define PRACTICAL_H_

#include <stdbool.h>

#include <stdio.h>

#include <sys/socket.h>

// Handle error with user msg

void DieWithUserMessage(const char *msg, const char *detail);

// Handle error with sys msg

void DieWithSystemMessage(const char *msg);

// Print socket address

void PrintSocketAddress(const struct sockaddr *address, FILE *stream);

// Test socket address equality

bool SockAddrsEqual(const struct sockaddr *addr1, const struct sockaddr *addr2);

// Create, bind, and listen a new TCP server socket int SetupTCPServerSocket(const char *service);

// Accept a new TCP connection on a server socket int AcceptTCPConnection(int servSock);

// Handle new TCP client

void HandleTCPClient(int clntSocket);

// Create and connect a new TCP client socket

int SetupTCPClientSocket(const char *server, const char *service);

enum sizeConstants { MAXSTRINGLENGTH = 128,

(32)

TCPEchoClient4.c

1 #include <stdio.h>

2 #include <stdlib.h>

3 #include <string.h>

4 #include <unistd.h>

5 #include <sys/types.h>

6 #include <sys/socket.h>

7 #include <netinet/in.h>

8 #include <arpa/inet.h>

9 #include "Practical.h"

10

11 int main(int argc, char *argv[]) { 12

13 if (argc < 3 || argc > 4) // Test for correct number of arguments 14 DieWithUserMessage("Parameter(s)",

15 "<Server Address> <Echo Word> [<Server Port>]");

16

17 char *servIP = argv[1]; // First arg: server IP address (dotted quad) 18 char *echoString = argv[2]; // Second arg: string to echo

19

20 // Third arg (optional): server port (numeric). 7 is well-known echo port 21 in_port_t servPort = (argc == 4) ? atoi(argv[3]) : 7;

22

(33)

TCPEchoClient4.c

27

28 // Construct the server address structure 29 struct sockaddr_in servAddr; // Server address

30 memset(&servAddr, 0, sizeof(servAddr)); // Zero out structure 31 servAddr.sin_family = AF_INET; // IPv4 address family

32 // Convert address

33 int rtnVal = inet_pton(AF_INET, servIP, &servAddr.sin_addr.s_addr);

34 if (rtnVal == 0)

35 DieWithUserMessage("inet_pton() failed", "invalid address string");

36 else if (rtnVal < 0)

37 DieWithSystemMessage("inet_pton() failed");

38 servAddr.sin_port = htons(servPort); // Server port 39

40 // Establish the connection to the echo server

41 if (connect(sock, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0) 42 DieWithSystemMessage("connect() failed");

43

44 size_t echoStringLen = strlen(echoString); // Determine input length 45

46 // Send the string to the server

47 ssize_t numBytes = send(sock, echoString, echoStringLen, 0);

48 if (numBytes < 0)

(34)

TCPEchoClient4.c

52

53 // Receive the same string back from the server

54 unsigned int totalBytesRcvd = 0; // Count of total bytes received 55 fputs("Received: ", stdout); // Setup to print the echoed string 56 while (totalBytesRcvd < echoStringLen) {

57 char buffer[BUFSIZE]; // I/O buffer

58 /* Receive up to the buffer size (minus 1 to leave space for 59 a null terminator) bytes from the sender */

60 numBytes = recv(sock, buffer, BUFSIZE - 1, 0);

61 if (numBytes < 0)

62 DieWithSystemMessage("recv() failed");

63 else if (numBytes == 0)

64 DieWithUserMessage("recv()", "connection closed prematurely");

65 totalBytesRcvd += numBytes; // Keep tally of total bytes 66 buffer[numBytes] = '\0'; // Terminate the string!

67 fputs(buffer, stdout); // Print the echo buffer 68 }

69

70 fputc('\n', stdout); // Print a final linefeed 71

(35)

TCPEchoServer4.c

1 #include <stdio.h>

2 #include <stdlib.h>

3 #include <string.h>

4 #include <sys/types.h>

5 #include <sys/socket.h>

6 #include <netinet/in.h>

7 #include <arpa/inet.h>

8 #include "Practical.h"

9

10 static const int MAXPENDING = 5; // Maximum outstanding connection requests 11

12 int main(int argc, char *argv[]) { 13

14 if (argc != 2) // Test for correct number of arguments 15 DieWithUserMessage("Parameter(s)", "<Server Port>");

16

17 in_port_t servPort = atoi(argv[1]); // First arg: local port 18

19 // Create socket for incoming connections 20 int servSock; // Socket descriptor for server

21 if ((servSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 22 DieWithSystemMessage("socket() failed");

(36)

TCPEchoServer4.c

24 // Construct local address structure

25 struct sockaddr_in servAddr; // Local address

26 memset(&servAddr, 0, sizeof(servAddr)); // Zero out structure 27 servAddr.sin_family = AF_INET; // IPv4 address family

28 servAddr.sin_addr.s_addr = htonl(INADDR_ANY); // Any incoming interface 29 servAddr.sin_port = htons(servPort); // Local port

30

31 // Bind to the local address

32 if (bind(servSock, (struct sockaddr*) &servAddr, sizeof(servAddr)) < 0) 33 DieWithSystemMessage("bind() failed");

34

35 // Mark the socket so it will listen for incoming connections 36 if (listen(servSock, MAXPENDING) < 0)

37 DieWithSystemMessage("listen() failed");

38

39 for (;;) { // Run forever

40 struct sockaddr_in clntAddr; // Client address

41 // Set length of client address structure (in-out parameter) 42 socklen_t clntAddrLen = sizeof(clntAddr);

43

(37)

TCPEchoServer4.c

48

49 // clntSock is connected to a client!

50

51 char clntName[INET_ADDRSTRLEN]; // String to contain client address 52 if (inet_ntop(AF_INET, &clntAddr.sin_addr.s_addr, clntName,

53 sizeof(clntName)) != NULL)

54 printf("Handling client %s/%d\n", clntName, ntohs(clntAddr.sin_port));

55 else

56 puts("Unable to get client address");

57

58 HandleTCPClient(clntSock);

59 }

60 // NOT REACHED 61 }

(38)

HandleTCPClient()

1 void HandleTCPClient(int clntSocket) {

2 char buffer[BUFSIZE]; // Buffer for echo string 3

4 // Receive message from client

5 ssize_t numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0);

6 if (numBytesRcvd < 0)

7 DieWithSystemMessage("recv() failed");

8

9 // Send received string and receive again until end of stream 10 while (numBytesRcvd > 0) { // 0 indicates end of stream

11 // Echo message back to client

12 ssize_t numBytesSent = send(clntSocket, buffer, numBytesRcvd, 0);

13 if (numBytesSent < 0)

14 DieWithSystemMessage("send() failed");

15 else if (numBytesSent != numBytesRcvd)

16 DieWithUserMessage("send()", "sent unexpected number of bytes");

17

18 // See if there is more data to receive

19 numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0);

20 if (numBytesRcvd < 0)

21 DieWithSystemMessage("recv() failed");

(39)

컴파일 방법 – 리눅스 환경

 Native 리눅스/VMware 리눅스/ Cygwin 홖경

• 유닉스 기반 (iris.mmu.ac.kr)

– $ gcc <컴파일 옵션> -o <실행파일> <소스파일들> -lsocket –lnsl

• 리눅스 기반 (lily.mmu.ac.kr)

– $ gcc <컴파일 옵션> -o <실행파일> <소스파일들>

– 주의

– –std=c99로 컴파일할 때 일부 헤더파일(netdb.h)을 제대로 포함시키지 못하는 오류가 확인되었습니다.

– -std=gnu99로 컴파일!!!

(40)

과제

 과제 1

 클라이언트 – 서버 프로그램 작성 (총 200점)

 동작 확인 (100점)

 echo_srv 6000

 echo_cli 220.68.174.178 Test 6000 (lily)

 과제 2

 프로그램 설명 (발표자료 또는 보고서 형식 100점)

 컴파일 과정

 주요 헤더 파일 설명

프로그램 코드 설명

(41)

응용과제

응용과제 1

클라이언트 프로그램 개선 1 (100점)

 echo_cli 주소 포트번호 문장 순서로 변경

 echo_cli 220.68.174.178 6000 This is a test

응용과제 2

클라이언트 프로그램 개선 2 (200점)

 echo_cli 주소 포트번호

 키보드로부터 입력을 받아 서버에게 젂달

 서버로부터 응답 받은 후 다시 반복

응용과제 3

서버 프로그램 개선 (500점)

 서버 프로그램 수정하여 접속한 클라이언트 주소-포트번호 파일에 추가 (로그 파일)

 Ctrl-c 로 중단시키는 경우 그동안 접속한 클라이언트 수 출력 (시그널 이용)

제출기한 : 3월 29일 자정

참조

관련 문서

 FileDialog(Frame fr, String title, int mode): fr 프레임에 부착되고 title을 제목으로 가지는 객체를 생성한다. Mode가 FileDialog.SAVE

auto double int struct break else long switch case enum register typedef char extern return union const float short unsigned continue for signed void.. default

– 주어진 함수를 다른 변수에 종속하는 새로운 함수로 만드 는 적분 형태의 변환 (예.. School of Mechanical Engineering

 열공간은 주어진 행렬에 대한 열벡터들의 모든 선형결합의 집합이 다.  행공간은 주어진 행렬에 대한 행벡터들의 모든

문자열 출력함수 • Status DrawStringconst WCHAR *string, INT length, cont Font *font, const PointF &origin, const Brush *brush • Status DrawStringconst WCHAR *string,

주어진 재료를 효과적으로 사용하였는가? 주어진 재료를 효과적으로 사용했다. 상 주어진 재료를 효과적으로 사용하지 못했다. 상 결과물이 미션의 목표를 일부

indicated input stream fgets( char *s, int n, FILE *) Address of the string/NULL pointer. fputs() Writes the character string pointed to

indicated input stream fgets( char *s, int n, FILE *) Address of the string/NULL pointer. fputs() Writes the character string pointed to