프로그래밍/C

(C언어) 소켓 프로그램

길가다주운노트 2016. 7. 9. 18:13

네트워크 프로그래밍

소켓이라는 것을 기반으로 프로그래밍을 하기 때문에 소켓 프로그래밍이라고도 함.

네트워크로 연결된 둘 이상의 컴퓨터 사이에서의 데이터 송수신 프로그램의 작성을 의미함.


소켓에 대한 간단한 이해

네트워크의 연결 도구

운영체제에 의해 제공이 되는 소프트웨어적인 장치


소켓의 비유와 분류

TCP 소켓은 전화기에 비유될 수 있음

소켓은 socket 함수의 호출을 통해서 생성됨

단 전화를 거는 용도의 소켓과 전화를 수신하는 용도의 소켓 생성 방법에는 차이가 있다.

1
2
3
4
#include<sys/socket.h>
 
int socket(int domain, int type, int protocol);
 
cs

소켓의 생성

=> 성공시 파일 디스크립터, 실패 시 -1 반환


소켓의 주소 할당 및 연결

전화기에 전화번호가 부여되듯이 소켓에도 주소정보가 할당

소켓의 주소정보는 IP와 PORT번호로 구성됨

1
2
3
#include<sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);
 
cs

주소의 할당

=> 성공시 0, 실패 시 -1 반환


연결요청이 가능한 상태의 소켓

연결요청이 가능한 상태의 소켓은 걸려온느 전화를 받을 수 있는 상태에 비유할 수 있다.

전화를 거는 용도의 소켓은 연결요청이 가능한 상태의 소켓이 될 필요가 없다. 이는 걸려오는 전화를 받는 용도의 소켓에서 필요한 상태이다.

1
2
3
#include<sys/socket.h>
int listen(int sockfd, int backlog);
 
cs

연결요청 가능한 상태로 변경

=> 성공시 0, 실패시 -1 반환

소켓에 할당된 IP와 Port번호로 연결요청이 가능한 상태가 됨


연결요청의 수락

걸려오는 전화에 대해서 수락의 의미로 수화기를 드는 것에 비유 할수 있다.

연결요청이 수락되어야 데이터의 송수신이 가능

수락된 이후에 데이터 송수신이 양방향성으로 됨

1
2
3
#include<sys/socket.h>
int accept(int sockfd, int backlog);
 
cs


연결을 요청하는 소켓의 구현

리스닝 소켓과 달리 구현의 과정이 매우 간단

'소켓의 생성'과 연결의 요청'으로 구분


소켓을 사용하기 위한 연결

Server

Client

소켓 생성(socket)

IP/PORT (bind)

요청대기(listen)

수락(accept)

Read/Write

종료(close)

소켓 생성(Socket)

연결 요청(Connect)

Read/Write

종료(close)


소켓의 종류

 TCP

 UDP

동기식 연결 방식

데이터가 손실되지 않음

데이터가 순서대로 전달됨

비동기식 연결방식

데이터 손실 발생할 수 있음

속도가 빠름(상대적) 





간단한 예제 (C언어)

서버 : 포트를 열고 대기한 상태에서 클라이언트가 접속하면 Hello World를 전송

클라이언트 : 서버의 IP와 PORT번호를 입력하여 서버에 접근하자마자 Read하고 받은 데이터를 출력

서버와 클라이언트 프로젝트를 각각 만들어야함


프로젝트에 라이브러리 추가

프로젝트 오른쪽 클릭 -> 속성

링커 -> 입력으로 이동

추가 종속성 -> 편집 누르기


ws2_32.lib 를 적기


Hello_Client_win.c (클라이언트 소스)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
void ErrorHandling(char* message);
 
int main(int argc, char* argv[])
{
    WSADATA wsaData;
    SOCKET hSocket;
    SOCKADDR_IN servAddr;
 
    char message[30];
    int strLen;
 
    if(argc!=3)
    {
        printf("Usage : %s <IP> <port>\n", argv[0]);
        exit(1);
    }
 
    if(WSAStartup(MAKEWORD(22), &wsaData) != 0)
        ErrorHandling("WSAStartup() error!");  
    
    hSocket=socket(PF_INET, SOCK_STREAM, 0);
    if(hSocket==INVALID_SOCKET)
        ErrorHandling("hSocketet() error");
    
    memset(&servAddr, 0sizeof(servAddr));
    servAddr.sin_family=AF_INET;
    servAddr.sin_addr.s_addr=inet_addr(argv[1]);
    servAddr.sin_port=htons(atoi(argv[2]));
    
    if(connect(hSocket, (SOCKADDR*)&servAddr, sizeof(servAddr))==SOCKET_ERROR)
        ErrorHandling("connect() error!");
 
    strLen=recv(hSocket, message, sizeof(message)-10);
    if(strLen==-1)
        ErrorHandling("read() error!");
    printf("Message from server: %s \n", message);  
 
    closesocket(hSocket);
    WSACleanup();
    return 0;
}
 
void ErrorHandling(char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}
cs


Hello_server_win.c (서버 소스)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
void ErrorHandling(char* message);
 
int main(int argc, char* argv[])
{
    WSADATA    wsaData;
    SOCKET hServSock, hClntSock;        
    SOCKADDR_IN servAddr, clntAddr;        
 
    int szClntAddr;
    char message[]="Hello World!";
 
    if(argc!=2
    {
        printf("Usage : %s <port>\n", argv[0]);
        exit(1);
    }
  
    if(WSAStartup(MAKEWORD(22), &wsaData)!=0)
        ErrorHandling("WSAStartup() error!"); 
    
    hServSock=socket(PF_INET, SOCK_STREAM, 0);
    if(hServSock==INVALID_SOCKET)
        ErrorHandling("socket() error");
  
    memset(&servAddr, 0sizeof(servAddr));
    servAddr.sin_family=AF_INET;
    servAddr.sin_addr.s_addr=htonl(INADDR_ANY);
    servAddr.sin_port=htons(atoi(argv[1]));
    
    if(bind(hServSock, (SOCKADDR*) &servAddr, sizeof(servAddr))==SOCKET_ERROR)
        ErrorHandling("bind() error");  
    
    if(listen(hServSock, 5)==SOCKET_ERROR)
        ErrorHandling("listen() error");
 
    szClntAddr=sizeof(clntAddr);        
    hClntSock=accept(hServSock, (SOCKADDR*)&clntAddr,&szClntAddr);
    if(hClntSock==INVALID_SOCKET)
        ErrorHandling("accept() error");  
    
    send(hClntSock, message, sizeof(message), 0);
    closesocket(hClntSock);
    closesocket(hServSock);
    WSACleanup();
    return 0;
}
 
void ErrorHandling(char* message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}
 
cs



서버,클라이언트 둘다 실행시 main 에 매개변수를 줘야 실행이 됨


Cmd를 이용한 main문에 매개변수 전달 (프로젝트 빌드 후)

서버

실행 파일 + 포트번호 매개변수 입력


클라이언트 (출력결과)

IP번호 (현재는 같은 컴퓨터에서 실행해서 로컬호스트인 127.0.0.1 입력)와 서버에서 연 PORT번호 입력