소켓 프로그래밍

--·2023년 1월 3일
0

클라이언트 소켓 (Client Socket)

1. 클라이언트 소켓 생성 : socket()

소켓 통신을 위해 가장 먼저 해야 할 일은 소켓을 생성하는 것이다. 이 때 소켓의 종류를 지정할 수 있는데, TCP 소켓을 위해서는 스트림 타입, UDP 소켓을 위해서는 데이터그램 타입을 지정할 수 있다.

최초 소켓이 만들어지는 시점에는 어떠한 연결 대상에 대한 정보도 들어 있지 않다. 그러기에 연결 대상 즉, IP 와 Port 을 지정하고 연결 요청을 전달하기 위해서는 생성한 소켓을 사용하여 connect() API를 호출해야 한다.

2. 연결 요청 : connect()

connect() API는 IP주소와 포트 번호로 식별되는 대상으로 연결 요청을 보낸다.

connect() API는 블록 방식으로 동작하기에, 연결 요청에 대한 결과가 결정되기 전에는 connect()의 실행이 끝나지않고 대기한다.

호출이 성공하면 send() / recv() API 를 통해 데이터를 송수신 할 수 있다.

3. 데이터 송수신 : send() / recv()

연결된 소켓을 통해 데이터를 보낼 때는 send() 수신에는 recv() API를 사용한다.두 API 모두 connect와 동일하게 블럭 방식으로 동작한다.

그러므로 두 호출이 모두 결과가 결정되기 전까지 API가 리턴되지 않는다.

특히 recv() 같은 경우는 한번 실행되면 언제 어떤 데이터가 전송되어 올 것인지 알 수 없기때문에, 데이터 수신을 위해서 별도의 스레드를 실행하여 데이터 수신을 기다린다.

4. 소켓 닫기 : close()

데이터 송수신이 필요없게 되면, 소켓을 닫기 위해 close() API를 호출한다. 해당 소켓은 닫힌 이후에는 재사용이 불가능하다.


서버 소켓(Server Socket)

1. 서버 소켓 생성 : socket()

클라이언트와 동일하게 소켓을 생성한다.

2. 서버 소켓 바인딩 : bind()

bind의 사용되는 파라미터는 포트 번호 혹은 IP 주소 + 포트 번호이다. 시스템 상에서 많은 수의 프로세스가 동작하기에 서버에 접근할 수 있는 가상화된 포트를 지정해야 한다. 운영체제는 소켓들이 중복된 포트 번호를 사용하지 않도록, 내부적으로 포트 번호와 소켓 연결 정보를 관리하는데, bind() 호출과정에서 중복되는 포트 사용이 있으면 운영체제가 포트 할당을 거부하고, API는 에러를 리턴한다.

3. 클라이언트 연결 요청 대기 : listen()

서버 소켓에 포트 번호를 결합하고 나면, 서버 소켓을 통해 클라이언트의 요청을 받을 준비가 되었다. 클라이언트의 연결 요청이 수신될때까지 한없이 세월아 네월아 기다려야 한다. 대기 상태를 빠져나오는 경우도 있는데 이때는 요청이 수신되는 경우와, 에러가 발생하는 경우다.

클라이언트 연결 요청에 대한 정보는 시스템 내부적으로 관리되는 큐에 쌓이게 됨으로, 대기 중인 연결 요청을 큐로부터 꺼내와서, 연결을 수립하기 위해서는 accept() 를 호출하면 된다.

4. 클라이언트 연결 수립 : accept()

최종적으로 accept() 을 호출해서 소켓 간 연결이 수립이 된다. 여기서 한가지 독특한 부분은 accept() 가 호출되어 소켓 간 연결을 수립할 때 새로운 소켓을 만들어 연결하게 된다. 그럼 여기서 서버 소켓의 역할은? 단순히 클라이언트 연결을 대기하고 연결 수립 요청을 하고 소켓을 닫는 것이다.

5. 데이터 송수신 : send() / recv()

클라이언트와 동일하다.

6. 소켓 연결 종료 : close()

클라이언트와 동일하다. 하지만 서버 소켓의 경우 클라이언트와 연결을 수립했던 소켓뿐만 아니라 클라이언트의 연결을 대기하는 소켓 또한 닫아줘야 함으로 유의해야 한다.


소켓 프로그래밍

파이썬을 이용하여 클라이언트에서 소문자를 입력하면 서버에서 대문자 받고 대문자로 입력하면 소문자로 받는 프로그램 구현하기

Server

from socket import *
server = socket(AF_INET, SOCK_STREAM)# socket 객체 생성
#인자     1. AF : Address Family (주소체계를 의미) 2. socket type
server.bind(('', 8080))
#앞부분 ip, 뒷부분 port번호
#앞부분이 빈칸인이유 : 모든 인터페이스와 연결
server.listen(1)
# 1의 의미 : 1개의 동시접속을 허용
client, addr = server.accept()

print(str(addr), '에서 접속 확인.')
Data = client.recv(1024)
#1024바이트만큼의 데이터를 받는다
print('받은 데이터 : ', Data.decode('utf-8'))

client.send('성공'.encode('utf-8'))
print('메시지를 전송했습니다')

Client

from socket import *

client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1',8080))
#127.0.0.1 자기 자신을 8080번 포트로 연결하라!

print('연결 확인')
print('대문자 or 소문자를 입력하세요 : ')
SmallLetter = input()
if ord(SmallLetter)>=97 and ord(SmallLetter)<=122:
    SmallLetter=SmallLetter.upper()
else:
    SmallLetter=SmallLetter.lower()
client.send(SmallLetter.encode('utf-8'))

print('메시지 전송했습니다.')
Data = client.recv(1024)
print('대소문자 변환 성공 여부 : ', Data.decode('utf-8'))

클라이언트에서 대문자 R을 보내면 서버가 소문자 r로 받습니다.



서버에서 대문자 R을 보내면 클라이언트가 소문자 r로 받습니다.


0개의 댓글