Socket에 대하여 (4) - TCP

code++·2023년 10월 17일

TCP

1. TCP Socket descriptor 와 UDP Socket descriptor는 동일 하게 취급

#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <iostream>
using namespace std;

int main(){
    int s1= socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
    cout<<" UDP SOCKET ID:"<<s1<<endl;

    int s2 = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    cout<<" TCP SOCKET ID:"<<s2<<endl;
    close(s1);

    int s3 = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    cout<<" TCP SOCKET ID:"<<s3<<endl;

    close(s2);
    close(s3);

    return 0;
    }
  • 같은 descriptor 일지라도 완전히 다른 소켓이므로 다르다.

    2. 동시에 열 수 있는 descriptor 수

  • TCP에서 동시에 열수 있는 descriptor수는 1024

  • descriptor 수 조정 방법

    -$ ulimit -n 1023 // 1023으로 descriptor수 증가

    3. 연결 하지 않고 socket에 데이터 쓰기

    int s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
       cout<<" TCP SOCKET ID:"<< s <<endl;
       
       char buf[1024];
       int r = send(s,buf,sizeof(buf),MSG_NOSIGNAL);
  • connect() 함수 없이 데이터를 쓴 경우 Broken Pipe(EPIPE) 로 실패.
    os가 강제로 프로세스 종료

  • 하지만, MSG_NOSIGNAL flag 가 종료 되는 것을 방지.

    4. 연결을 닫은 socket에 데이터 쓰기

    int s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
     cout<<" TCP SOCKET ID:"<< s <<endl;
     
     close(s);
     
     char buf[1024];
     int r = send(s,buf,sizeof(buf),MSG_NOSIGNAL);
    
  • 소켓을 닫고 데이터를 보내려고하면 Bad file descripotr 에러 발생

  • socket descriptor가 종료 되어있기 때문이다.(존재 하지 않음.)

    5. TCP 연결 후 데이터 전송 및 수신

#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>

#include <unistd.h>
#include <iostream>

using namespace std;

int main(){
    int s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if(s < 0){
        cerr<<"socket() failed: "<< strerror(errno)<<endl;
        return 1;
    }

    struct sockaddr_in sin;
    memset(&sin,0,sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr("127.0.0.1");
    sin.sin_port = htons(10000);

    //TCP connect()
    if(connect(s,(struct sockaddr *)&sin, sizeof(sin)) < 0){
        cerr<<"connect() failed: "<< strerror(errno)<<endl;
        return 1;
    }

    char buf[1024];//데이터 보내기 1024bytes를 보낸 거임.
    int r = send(s,buf,sizeof(buf),MSG_NOSIGNAL);
    if(r < 0){
        cerr<<"send() failed: "<< strerror(errno)<<endl;}
    else if(r == 0){
            cout<<"socket closed"<<endl;
    }
    else { 
        cout<<"Sent : "<<r<<"bytes"<<endl;
    }
    
    r = recv(s,buf,sizeof(buf),0); //데이터 수신하기

    if(r < 0){
        cerr<<"recv() failed: "<< strerror(errno)<<endl;}
        else if(r == 0){
        cerr<<"Socket Closed"<< endl; //recv() ==0 일때 연결 끊김상태
        }
        else{ 
        cout<<"received : "<<r<<"bytes"<<endl;
        }
    close(s);
    return 0;
}

6. TCP active socket, passive socket

  • active socket : 연결 맺어진 상태

    클라이언트 : connect() 성공 뒤 socket
    서버 : accept() 성공뒤 socket

  • passive socket : 연결 맺기 전 대기상태

    서버측 : listen() 성공뒤 socket

#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>

#include <unistd.h>
#include <iostream>

using namespace std;

int main(){
    int passiveSock = socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    sin.sin_port = htons(10000); //server 주소 포트번호


//bind() -> 클라이언트 i
    if(bind(passiveSock,(struct sockaddr *) &sin,sizeof(sin)) < 0) {
        cerr<< "bind() failed: "<< strerror(errno)<<endl;
        return 1;
    }

//listen() passive socket
    if(listen(passiveSock , 10) < 0){
        cerr<<"listen() failed: "<<strerror(errno)<<endl;
        return 1;
    }

//accept() activer socket
    memset(&sin,0,sizeof(sin));
    unsigned int sin_len = sizeof(sin);
    int clientSock = accept(passiveSock,(struct sockaddr *) &sin, &sin_len);

    if(clientSock <0){
        cerr<< "accept() failed: "<<strerror(errno)<<endl;
        return 1;
    }

//받을 데이터 버퍼 크기
    char buf[65536];
    int numrecv = recv(clientSock,buf,sizeof(buf),0); //recv()
    //recv() == 0 이면 연결이 끊긴다
    if(numrecv == 0){
            cout<<"Socket closed"<<clientSock<< endl;
        }
        else if(numrecv < 0){
            cerr<<"recv() failed: "<<strerror(errno)<<endl;
        }
        else { 
        cout<<"received : "<<numrecv<<"bytes , cliendsock"<< clientSock <<endl;
        }

        int offset = 0;
        while(offset < numrecv){
            int numSend = send(clientSock, buf+offset, numrecv - offset,0);

            if(numSend < 0){
                cerr<< "send() failed: "<<strerror(errno)<<endl;
            }else{
                cout<< "Sent: "<<numSend <<endl;
                offset += numSend;
            }
        }
        close(clientSock);
        close(passiveSock);
}
  • UDP vs TCP

  • UDP 서버 소켓 개수 -> 1개

  • TCP 서버 소켓 개수 -> 새로운 클라이언트 접속 passive socket, 클라이언트와 통신할 수 있는 actvie socket은 클라이언트 개수 만큼 필요.

  • UDP 보다 TCP 가 훨씬 많은 os 부하 발생.

profile
일상

0개의 댓글