안 쓰던 블로그

C언어 TCP/IP socket 서버와 클라이언트 연결하기 본문

Network

C언어 TCP/IP socket 서버와 클라이언트 연결하기

proqk 2016. 10. 23. 22:35
반응형
[socketserver.cpp]

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
58
59
60
61
62
63
64
65
66
67
#include <stdio.h>
#include <WinSock2.h>
 
#pragma comment(lib, "wsock32.lib")
 
int main() {
    SOCKET socketdescriptor, csocketdescriptor; //socket descriptor
    WSADATA WSAdata; //winsock data
    struct sockaddr_in socketin; //socket struct
    struct sockaddr_in client_addr;
    int size = sizeof(client_addr);
 
    if (WSAStartup(WINSOCK_VERSION, &WSAdata) != 0) { //winsock version check
        printf("WSAStartup failed, %d\n", WSAGetLastError());
        return 0;
    }
 
    socketdescriptor = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //create TCP/IP socket
 
    if (socketdescriptor == INVALID_SOCKET) {
        printf("Create socket failed, %d\n", WSAGetLastError()); //if socket() get error
        WSACleanup(); //finish WS2_32.DLL
        return 0;
    }
 
    socketin.sin_family = AF_INET; //I will use TCP/IP or UDP protocol
    socketin.sin_port = htons(10000); //I will use FTP server port
    socketin.sin_addr.s_addr = htonl(ADDR_ANY); //I will access with this(all clinet) IP Address
 
    if (bind(socketdescriptor, (struct sockaddr*)&socketin, sizeof(socketin)) == SOCKET_ERROR) {
        printf("bind failed, %d\n", WSAGetLastError());
        closesocket(socketdescriptor);
        WSACleanup();
        return 0;
    }
 
    if (listen(socketdescriptor, SOMAXCONN) != 0) {
        printf("listen mode failed, %d", WSAGetLastError());
        closesocket(socketdescriptor);
        WSACleanup();
        return 0;
    }
 
    printf("Waiting to connect client access... \n");
 
    csocketdescriptor = accept(socketdescriptor, (struct sockaddr*)&client_addr, &size);
 
    if (csocketdescriptor == INVALID_SOCKET) {
        printf("access accept failed, %d", WSAGetLastError());
        closesocket(socketdescriptor);
        WSACleanup();
        return 0;
    }
 
    printf("Connect Successful\n");
 
    if (closesocket(socketdescriptor) != 0 || closesocket(csocketdescriptor) != 0) {
        printf("Remove socket failed, %d", WSAGetLastError());
        WSACleanup();
        return 0;
    }
 
    if (WSACleanup() != 0) {
        printf("WSACleanup failed, %u", WSAGetLastError());
        return 0;
    }
}
cs



TCP/IP에서 사용하는 함수들은 WinSock2.h 헤더에 포함되어 있다. 윈도우즈 기반의 소켓 통신을 하기 위해서는 이 헤더가 필요하다. 

2번째 줄에서 winsock.h과 WinSock2.h가 보일텐데 둘은 버전 차이로 WinSock.h에 Winsock.h의 대부분이 포함되어 있으므로 보통 2번을 사용한다.

지금 자신이 사용하고 있는 버전은 이렇게 확인할 수 있다.


1
puts(WSAdata.szDescription);
cs


4번째 줄에서 TCP/IP에서 사용하는 WSA~나 bind, listen 함수 등은 C Runtime Library에 있지 않기때문에 #pragma로 wsock32.lib 라이브러리를 불러온다.


소켓은 크게 열기, 보내기(받기), 닫기로 구성된다.

열기: socket(); 함수로 소켓을 개방한다. 위 코드에서는 이 부분에 해당한다.


18
socketdescriptor = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //create TCP/IP socket
cs


socket(); 함수는 다음과 같이 사용한다.


1
SOCKET __stdcall socket(int af, int type, int protocol)
cs

AF_INET는 TCP/IP에서 사용하는 프로토콜 체계고, SOCK_STREAM 또한 TCP/IP 통신을 뜻한다.
UDP에서는 socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)로 사용한다.


26~28번째 줄에서 프로토콜, 포트 번호, 접근 허용 범위를 설정한다.


서버의 핵심인 bind()와 listen()이 30~42줄에 있다. 두 함수는 클라이언트로부터 접속이 있을 때까지 기다린다.

접속 시도는 큐에 저장되고, accept()함수가 큐에서부터 데이터를 꺼내오는 역할은 한다.

csocketdescriptor라는 소켓 디스크립터가 연결된 클라이언트와 데이터를 주고받는다.


받기: 이 코드는 단순 연결만 하는 것이므로 아직 없다.


닫기: 57번째 줄에서 서버용 소켓 socketdescriptor와 클라이언트와 연결된 csocketdescriptor를 모두 닫고 WSA구조체들의 정보를 해제한다.



[socketclient.cpp]


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
#include <stdio.h>
#include <WinSock2.h>
    
#pragma comment(lib, "wsock32.lib")
 
int main() {
    SOCKET socketdescriptor; //socket descriptor
    WSADATA WSAdata; //winsock data
    SOCKADDR_IN socketin; //socket struct
    
    if (WSAStartup(WINSOCK_VERSION, &WSAdata) != 0) { //winsock version check
        printf("WSAStartup failed, %d\n", WSAGetLastError());
        return 0;
    }
 
    socketdescriptor = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //create TCP/IP socket
 
    if (socketdescriptor == INVALID_SOCKET) {
        printf("Create socket failed, %d\n", WSAGetLastError()); //if socket() get error
        WSACleanup(); //finish WS2_32.DLL
        return 0;
    }
 
    socketin.sin_family = AF_INET; //I will use TCP/IP or UDP protocol
    socketin.sin_port = htons(10000); //I will use FTP server port
    socketin.sin_addr.s_addr = inet_addr("127.0.0.1"); //I will access with this IP Address
 
    if (connect(socketdescriptor, (struct sockaddr*)&socketin, sizeof(socketin)) != 0) { //try to connect server
        printf("Can't connect, %u\n", WSAGetLastError());
        closesocket(socketdescriptor);
        WSACleanup();
        return 0;
    }
 
    if (closesocket(socketdescriptor) != 0) {
        printf("remove socket failed, %d", WSAGetLastError()); //if socket can't finish
        WSACleanup();
        return 0;
    }
 
    if (WSACleanup() != 0) {
        printf("WSACleanup failed, %u", WSAGetLastError());
        return 0;
    }
 
    printf("Connect Successful\n");
}
cs


기본 틀은 서버와 비슷하다.


26번째 줄에 inet_addr("127.0.0.1")로 바뀌었는데 127.0.0.1로 접속을 하겠다는 뜻이다.

서버에서 쓰인 htonl(ADDR_ANY)는 모든 IP주소로부터 접근을 허용하겠다는 것이다.

서버에서도 inet_addr을 사용하면 특정 IP주소의 접근만 허용하게 할 수 있다.


28번째 줄에서는 connect();함수로 서버와 연결을 한다.

connect();는 이렇게 사용한다.


1
int __stdcall connect(SOCKET s, const sockaddr *name, int namelen)
cs

서버용 소켓, 소켓의 주소로 서버에 접속을 요청한다. 


--

혹시 주석에 문법적 오류가 있더라도 못본척ㅎㅎ



반응형
Comments