반응형

* Server Code

 

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#define BUFSIZE 1024
#define PORT_NUM 7000


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")

void ErrorHandling(const char* message);


int main(int argc, char** argv) {

    WSADATA wsaData;
    SOCKET servSock, clntSock;   //SOCKET은 사실 UINT_PTR 형이다.
    SOCKADDR_IN servAddr, clntAddr;


    char message[BUFSIZE]; //Message Buffer
    int strLen;
    int fromLen, nRcv;

    int len = 0;

    
    printf("Port Num: 7000");


    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
        ErrorHandling("Load WinSock 2.2 DLL Error");

    servSock = socket(PF_INET, SOCK_STREAM, 0);
    if (servSock == INVALID_SOCKET)
        ErrorHandling("Socket Error");

    memset(&servAddr, 0, sizeof(SOCKADDR_IN));
    servAddr.sin_family = AF_INET;
    servAddr.sin_port = PORT_NUM;
    servAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(servSock, (struct sockaddr*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)
        ErrorHandling("Bind Error");

    if (listen(servSock, 2) == SOCKET_ERROR)
        ErrorHandling("Listen Error");

    fromLen = sizeof(clntAddr);


    clntSock = accept(servSock, (struct sockaddr*)&clntAddr, &fromLen);    //// 클라이언트의 접속 요청을 수락
    if (clntSock == INVALID_SOCKET) {
        ErrorHandling("Accept Error");
    }
    else {
        printf("%s Connection Complete!\n", inet_ntoa(clntAddr.sin_addr));
        printf("Start ...\n");
    }
    closesocket(servSock);



    FILE* image;
    image = fopen("C:/Users/94hsk/Desktop/recv.jpg", "wb");
   
    while ((len = recv(clntSock, message, sizeof(message) - 1, 0)) != 0)
    {
        fwrite(message, sizeof(char), sizeof(message) - 1, image);
        if (feof(image))break;
    }

    fclose(image);
    closesocket(clntSock);
    WSACleanup();

    printf("Close Connection..\n");

    return 0;
}

void ErrorHandling(const char* message) {
    WSACleanup();
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

 

 

 

*Client Code

 

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS

#include <winsock2.h>
#include <stdio.h>

#include <stdlib.h>
#include <conio.h>


#include <WS2tcpip.h>


#pragma comment(lib, "ws2_32.lib")
//#include<arpa/inet.h>



#define BUFSIZE 1024

void ErrorHandling(const char* message);

int main(int argc, char** argv) {

    SOCKET clntSock;    // 소켓 생성
    WSADATA wsaData;
    SOCKADDR_IN servAddr;

    char sAddr[15];
    int sPort;
    int nRcv;
    unsigned int Addr;
    char message[BUFSIZE];
    char message2[BUFSIZE];

    struct hostent* host;

    FILE* fp;
    size_t len;

    fp = NULL;
    fp = fopen("C:/Users/94hsk/Desktop/lena.jpg", "rb");


    if (fp  == NULL)
        ErrorHandling("Image Read Fail");

    printf("Server Address : ");       gets_s(sAddr, sizeof(sAddr));   /// gets_s함수는 입력을 받는 함수로
    printf("Port Number : ");       gets_s(message, sizeof(message));   // 해당 크기만큼의 문자열을 받아 저장한다.
    sPort = atoi(message);  // 문자열을 정수 타입으로 변경해주는 함수이다.
                            // 왜 변경하지?
                            // message에 문자가 들어가면 바로 0을 반환
                            // 문자가 숫자로 시작할 시 숫자 반환
                            // 숫자+문자의 경우 문자 나오기 전까지 숫자 반환
                            // 문자+숫자의 경우 0 반환
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)  // 소캣 사용 전 초기화를 진행하고 초기화 정보를 wasData에 저장
        ErrorHandling("Load WinSock 2.2 DLL Error");    // 초기화 성공하면 0 반환

    clntSock = socket(AF_INET, SOCK_STREAM, 0); // socket(int domain, int Type, int protocol);
                                                // 소캣을 생성한다.
                                                // AF_INET 체계는 주소체계를 사용하며 IPv4인터넷 프로토콜 사용

    if (clntSock == INVALID_SOCKET)
        ErrorHandling("Socket Error");

    memset(&servAddr, 0, sizeof(servAddr)); // memset 함수는 어떤 메모리의 시작점부터 연속된 범위를 어떤 값으로
                                            // 모두 지정하고 싶을때 사용하는 함수
                                // 채우고자 하는 메모리의 시작 주소 : servAddr
                                // 메모리에 채우고자 하는 값 : 0
                                // 채우고자 하는 메모리의 크기 : sizeof(servAddr)
    
    //// SOCKADDR_IN 구조체의 객체 servAddr
    //// 위의 구조체는 IPv4 주소를 저장하는 구조체이다.
    //// 주소 체계, 포트 정보, IPv4 주소 등을 저장한다.
    
    
    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.s_addr = inet_addr(sAddr);
    
    //// SOCKADDR_IN 구조체의 구조체변수 servAddr에 대해 설정을 진행한다.
    //// sin_addr 값에는 IP주소를 나타내는 32비트 정수 탑이의 구조체가 들어간다.
    //// 즉 입력받은 iP주소를 통해 통신을 설정해주는 부분이다.
    void* dst;
    int check;


    //servAddr.sin_port = htons(sPort);
    servAddr.sin_port = (sPort);


    if (connect(clntSock, (struct sockaddr*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR) {
        ErrorHandling("Connection Error");
    }
    else {
        printf("Connect OK!\nStart...\n");
    }


    while (1)
    {
        printf("sending\n");
        len = fread(message2, sizeof(char), BUFSIZE-1, fp);
        send(clntSock, message2, len, 0);
        if (feof(fp))break;
    }
    printf("Sending Done! \n");
    shutdown(clntSock, SD_SEND);

    len = recv(clntSock, message2, BUFSIZE - 1, 0);
    message2[len] = 0;
    puts(message2);

    fclose(fp);

    closesocket(clntSock);
    WSACleanup();           /// 소캣 종료
    printf("Close Connection..\n");

    return 0;
}


void ErrorHandling(const char* message) {
    WSACleanup();
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

 

반응형

'C++' 카테고리의 다른 글

C++ 내부정의, 외부정의, 헤더파일  (0) 2020.01.09
객체지향언어  (0) 2020.01.08
반응형

보통 클래스를 선언하고 내용을 작성할때 

 

class Car
{
public:
	int speed;
    ..
    int getSpeed()
    {
    	return speed;
    }
    ..
}

위의 형태로 speed라는 멤버변수를 선언하여주고 getSpeed라는 함수를 이용하여 speed의 값을

받아오도록 했다.

이러한 형태를 함수의 내부정의라고 한다.

 

하지만 위와 같은 경우는 함수의 코드 내용이 길어지게 되면 코드를 이해하는데 굉장히 번거로워 질 수 있다.

 

따라서 우리는 멤버함수의 외부정의를 통하여 코드를 좀 더 가독성있게 정리할 수 있다.

 

class Car
{
private:
	int speed;
    int gear;
    string color;
public:
	int getSpeed();
    void setSpeed(int s);
};

int Car::getSpeed()
{
	return speed;
}

void Car::setSpeed(int s)
{
	speed = s;
}

위와 같은 형태를 함수의 외부정의라고 한다.

 

처음에 보여줬던 코드에서 getSpeed()와 setSpeed(int s) 함수를 외부로 꺼내온 형태를 확인할 수 있다.

 

또한 클래스를 생성할 때 헤더파일을 통해 코드를 간단히 할 수 있다.

 

 

우클릭 -> 추가 -> 클래스   를 통해 클래스를 만들어 줄 수 있다.

 

다음와 같이 클래스 추가 창이 뜨게되면 Car 클래스를 생성해 주도록 한다.

이 과정을 통해 Car라는 클래스가 생성되며 Car.h 라는 헤더파일과 Car.cpp 라는 파일이 생성되게 된다.

 

여기서 Car.h의 헤더파일에는 Car 클래스를 사용하는데 필요한 Car 클래스 선언이 들어간다.

그리고 Car.cpp 에는 멤버함수들의 몸체가 들어가게 된다.

 

 

헤더파일은 간단하게 위와 같다.

멤버변수인 speed 와 gear을 선언하여 주었고 멤버함수은 getSpeed()와 setSpeed(int s)의 함수를

선언만 해주었다.

 

함수의 내용과 정의는 헤더파일 부분에 포함되어 있지 않다.

 

 

위의 사진은 Car.cpp 파일의 내용이다.

헤더파일에서의 멤버함수와 멤버변수를 사용하기에 Car.h 파일을 include 해준다.

그리고 멤버함수의 내용들을 작성해준다.

 

이렇게 함으로서 헤더파일에는 선언의 내용만을 담게되고 cpp파일에는 정의 부분을 담게 되기에

코드를 이해하는데 한결 쉬워질 수 있다.

 

그리고 main문을 위와 같이 작성하여 주면 차의 속도가 출력되는 것을 확인할 수 있다.

 

반응형

'C++' 카테고리의 다른 글

TCP/IP 통신으로 이미지 파일 전송  (0) 2020.10.12
객체지향언어  (0) 2020.01.08
반응형

C++의 가장 큰 특징을 흔히 객체지향언어 라고 표현한다.

 

객체는 어떠한 상태나 특징값을 말한다.

 

예를 들어 자동차 객체의 경우 속성(특징값)은 차종, 색상, 기어, 연비  등이 될 것이다.

자동차 객체의 상태는 출발하기, 멈추기, 감속하기, 가속하기 등이 될 것이다.

 

객체는 어떠한 상태나 특징값을 갖고있다.

 

예를 들어 자동차 객체의 경우 속성(특징값)은 차종, 색상, 기어, 연비 등이 될 것이다.

자동차 객체의 상태는 출발하기, 멈추기, 감속하기, 가속하기 등이 될 것이다.

객체에 값을 보내주는 과정 즉 멤버함수를 호출하는 과정을 "메시지를 전달한다" 고 표현한다.

 

그리고 같은 종류의 객체는 다양하게 존재할 수 있다.

즉 위의 과정에서 객체를 자동차에 비유하였고 상태와 속성을 갖는다고 얘기하였다.

하지만 자동차는 이 세상에 다양하게 존재한다.

경차, 승용차, 승합차, 트럭, 버스...

따라서 이러한 자동차를 모두 만들 수 있는 자동차의 설계도면을 클래스라고 볼 수 있을 것이다.

 

다르게 설명해보면 자동차라는 클래스 안에 승용차, 경차, 승합차 등의 객체가 존재한다고 볼 수 있을 것이다.

 

구조체화 클래스가 헷갈릴 수 있다.

 

영상처리를 할때 사용된 iplimage 구조체를 예를 들자.

iplimage 구조체에는 이미지의 픽셀값을 받아오는 변수, 사이즈를 받아오는 변수, 채널을 받아오는 변수들이

포함되어 있었다.

다시말해 구조체를 통해 속성(특징값) 들을 받아올 수 있었다.

하지만 클래스는 여기에 함수(상태) 가 더해지게 된다.

 

 

#include <iostream>
#include <string>
using namespace std;

class Car{
public:
	int speed;
    int gear;
    string color;
    
    void speedUp(){
    speed += 10;
    }
    
    void speedDown(){
    speed -= 10;
    }
};

int main()
{
	Car myCar;
    
    myCar.speed=100;
    myCar.gear=3;
    myCar.color="red";
    
    myCar.speedUp();
    myCar.speedDown();
    
    return 0;
}

 

위의 내용을 보면 Car이라는 클래스를 만들어 주었고 myCar이라는 객체를 만들어주었다.

그리고 이에 따라 Car의 속성(speed, gear, color)을 지정해주었고 상태또한 멤버함수 호출을 통해 진행하였다.

 

그리고 클래스 내의 변수에 대하여 접근제어를 설정해 주게 되는데 클래스의 멤버변수나 멤버함수에 대하여

Public 으로 설정할지, private으로 설정할지를 결정하는 과정이다.

 

public으로 설정된 멤버변수나 함수는 외부에서 접근이 가능하지만 private의 경우 클래스 내부에서만 접근이 가능하다.

하지만 외부에서도 private 멤어변수를 설정할 수 있도록 할 수 있는데 이때 사용되는 것이 접근자와 설정자이다.

 

 

class Car{
private:
	int speed;
    int gear;
    string color;
    
public:
	int getSpeed(){
    return speed;
    }
    
    void setSpeed(int s){
    speed=s;
    }
    .
    .
    .
}

위의 내용을 확인해보면  private멤버로 speed와 gear을 설정해주지만

public멤버함수로 speed값을 설정해주고 값을 받아오는 과정을 진행할 수 있다.

 

그럼 왜 이렇게 설정자와 접근자로 나누는가.

 

가장 중요한 이유는 정보 은릭으로 클래스의 인터페이스와 구현 부분을 분리하는 것이 좋기 때문이다.

예를들어 gear의 변수를 advanced_gear 로 이름을 변경하고자 할때

접근자를 사용하지 않았다면 모든 gear를 수정해야하지만 접근자를 사용하여 getGear() 을 사용하여 기어의 값을 받아오도록 코딩이 되어있다면 이 부분만 수정해주면 될 것이다.

반응형

'C++' 카테고리의 다른 글

TCP/IP 통신으로 이미지 파일 전송  (0) 2020.10.12
C++ 내부정의, 외부정의, 헤더파일  (0) 2020.01.09

+ Recent posts