티스토리 뷰

책) TCP/IP 윈도우 소켓 프로그래밍 5장

 

TCP 서버 - 클라이언트 모델에서 데이터를 전송하는 방식은 다양한 방식이 있다. 

1. 고정 길이 데이터 전송

2. 가변 길이 데이터 전송

3. 고정 + 가변 길이 데이터 전송

4. 송신자 데이터 전송후 접속 종료, 수신자는 recv()함수의 리턴값이 0이 될때까지 읽는다.

 

While(1){

    소켓 수신 버퍼에서 1바이트 데이터를 읽는다.

    읽은 데이터가 '\n'이 아니면 응용 프로그램 버퍼에 저장.

    읽은 데이터가 '\n'이면 루프 빠져나옴.

}

응용 프로그램 버퍼에 데이터를 사용한다.

//DummyClient
#pragma warning(disable:4996)
#pragma comment(lib, "ws2_32.lib")
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>

#define MAXLINE 50
#define PORTNUM 5000

void err_quit(const char* msg) {
    LPVOID lpMsgBuf;
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL, WSAGetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR)&lpMsgBuf, 0, NULL);
    printf("[%s] %s", msg, (char*)lpMsgBuf);
    LocalFree(lpMsgBuf);
}


int main()
{
    WSADATA wsaData;
    SOCKET sockfd;
    int addrlen;
    char buf[MAXLINE];

    WSAEVENT sEvent;
    struct sockaddr_in sockaddr;

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        printf("dlload error\n");
        return 1;
    }

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == INVALID_SOCKET)
    {
        printf("socket create error\n");
        return 1;
    }

    ZeroMemory(&sockaddr, sizeof(sockaddr));
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_port = htons(PORTNUM);
    sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    if (::connect(sockfd, (SOCKADDR*)&sockaddr, sizeof(sockaddr))) err_quit("Connet");

    const char* testdata[] = {
        "안녕하세요",
        "반가워요",
        "오늘따라 할 이야기가 많을 것 같네요.",
        "저도 그렇네요" };

    for (int i = 0; i < 4; i++) {
        int len = strlen(testdata[i]);
        strncpy(buf, testdata[i], len);
        buf[len++] = '\n';

        int retVal = send(sockfd, buf, len, 0);
        if (retVal == SOCKET_ERROR) {
            err_quit("Send");
            break;
        }
        printf("[TCP 클라이언트] %d바이트를 보냈습니다.\n", retVal);
    }

    printf("소켓 통신 종료");
    closesocket(sockfd);
    WSACleanup();
    return 0;
}

//DummyServer
#pragma warning(disable:4996)
#pragma comment(lib, "ws2_32.lib")
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>

#define MAXLINE 512
#define PORTNUM 5000

void err_quit(const char* msg) {
    LPVOID lpMsgBuf;
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL, WSAGetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR)&lpMsgBuf, 0, NULL);
    printf("[%s] %s", msg, (char*)lpMsgBuf);
    LocalFree(lpMsgBuf);
}
int _recv_ahead(SOCKET s, char* p) {
    __declspec(thread) static int nbytes = 0;
    __declspec(thread) static char buf[1024];
    __declspec(thread) static char* ptr;

    if (nbytes == 0 || nbytes == SOCKET_ERROR) {
        nbytes = recv(s, buf, sizeof(buf), 0);	// 속도 저하 방지를 위해 데이터를 크게 읽은 후
        					// 하나씩 반환해주는 함수
        if (nbytes == SOCKET_ERROR) return SOCKET_ERROR;
        else if (nbytes == 0) return 0;
        ptr = buf;
    }
    --nbytes;
    *p = *ptr++;
    return 1;
}
int recvline(SOCKET s, char* buf, int maxlen, int flag) {
    int n, nbytes;
    char c, * ptr = buf;

    for (n = 1; n < maxlen; n++) {
        nbytes = _recv_ahead(s, &c);
        if (nbytes == 1) {
            *ptr++ = c;
            if (c == '\n')
                break;
        }
        else if (nbytes == 0) {
            *ptr = 0;
            return n - 1;
        }
        else
            SOCKET_ERROR;
    }
    *ptr = 0;
    return n;
}
int main()
{
    WSADATA wsaData;
    SOCKET sockfd, clientfd;
    int addrlen;
    char buf[MAXLINE+1];

    WSAEVENT sEvent;
    struct sockaddr_in sockaddr,clientaddr;

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        printf("dlload error\n");
        return 1;
    }

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == INVALID_SOCKET)
    {
        printf("socket create error\n");
        return 1;
    }

    sockaddr.sin_family = AF_INET;
    sockaddr.sin_port = htons(PORTNUM);
    sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (::bind(sockfd, (SOCKADDR*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) err_quit("Bind");
    if (::listen(sockfd, 5) == SOCKET_ERROR) err_quit("Listen");

    while (1)
    {
        addrlen = sizeof(clientaddr);
        clientfd = ::accept(sockfd, (SOCKADDR*)&clientaddr, &addrlen);
        if (clientfd == INVALID_SOCKET) {
            err_quit("Accept");
            break;
        }
        printf("[TCP서버] 클라이언트 접속 IP : %s %d\n", 
            inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

        while (1) {
            int retVal = recvline(clientfd, buf, MAXLINE, 0);
            if (retVal == SOCKET_ERROR) {
                err_quit("Recv");
                break;
            }
            if (retVal == 0) break;
            
            buf[MAXLINE-1] = '0';
            printf("[TCP/%s : %d] %s\n", inet_ntoa(clientaddr.sin_addr),
                ntohs(clientaddr.sin_port), buf);
        }
        closesocket(clientfd);
        printf("소켓 통신 종료");
    }

    closesocket(sockfd);
    WSACleanup();
    return 0;
}
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함