원본 도서
http://www.kyobobook.co.kr/product/detailViewKor.laf?mallGb=KOR&ejkGb=KOR&barcode=9788996094036
간단한 구조를 먼저 파악해야 전체적인 내용을 파악할 때 유리하다.
1단계: 소켓 생성 => socket 함수 호출
2단계: IP주소와 PORT번호 할당 => bind 함수 호출
3단계: 연결 요청 가능 상태로 변경 => listen 함수 호출
4단계: 연결요청에 대한 수락 => accept 함수 호출
줄 안맞는거 굉장히 불편... 밑에 테이블은 너무 귀찮...
I. 네트워크란
네트워크는 서로 다른 두 컴퓨터의 연결되어 데이터를 주고 받을 수 있게 한 것이다.
II. 소켓(socket)
네트워크 상에서 연결된 두 컴퓨터의 데이터 송 수신에 이용할 수 있는 소프트웨어적 장치로 일종의 규격화에 해당한다.
! 공부를 위한 작성이다보니 내용이 이후 챕터에서 반복될 수 있습니다.
windows 환경에서는 헤더들을 찾을 수 없다는 오류가 발생합니다 wsl 혹은 linux 환경에서 진행해야 오류가 발생하지 않습니다.
소켓의 생성을 담당하는 함수, 송수신자의 전화기처럼 없으면 송수신 자체가 안됀다.
이후 chapter2에서 더 자세한 내용이 나옵니다.
#include<sys/socket.h>
반환
if 성공 : 파일 디스크립터 반환
if 실패 : -1 반환
파라미터
int socket(domain, int type, int protocol)
int domain : 사용할 프로토콜
int type : TCP, UDP등 사용할 통신 방식
int protocol : 두 컴퓨터간 통신에 사용되는 프로토콜의 전달, 적절한 flag를 사용하여 전달한다.
생성된 소켓의 목적지를 묶어주는 함수
bind함수의 경우는 각 파라미터 모두가 소켓 프로그래밍에서 다 중요한 내용이므로 차후 더 자세하게 다룹니다. 네트워크 공부가 선행되지 않은 상태에서 간료화된 설명으론 이해가 힘들 가능성이 높습니다
이후 chapter3에서 더 자세하게 나옵니다.
헤더
반환
if 성공 : 0 반환
if 실패 : -1 반환
파라미터
int bind(int sockfd, struct sockaddr* myaddr, socklen_t addrlen)
int sockfd : 주소정보(IP+PORT)를 할당할 소켓의 파일 디스크립터
struct sockaddr* myaddr : 할당하고자 하는 주소정보를 지니는 구조체 변수의 주소 값
socklen_t addrlen : 두번째 인자로 전달된 구조체 변수의 길이정보
연결요청을 위해 통신을 개통하는 함수, 쉽게 말해, 가게가 오픈했으니 손님을 기다리는 상태와 유사하다. 아직 손님은 안온 것이다.
#include<sys/socket.h>
반환
if 성공 : 0 반환
if 실패 : -1 반환
파라미터
int listen(int sockfd, int backlog)
int sockfd : int socket()에서 초기화된 소켓 전달
int backlog : 연결 가능한 큐의 대기 수
연결요청에 대한 수락
#include<sys/socket.h>
반환
if 성공 : 파일 디스크립터 반환
if 실패 : -1 반환
파라미터
int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen)
int sockfd : int socket()에서 초기화된 소켓을 전달
struct sockaddr* addr : 소켓을 구성하는 구조체
socklen_t* addrlen : 소켓에 제공할 주소를 전달하는 파라미터
시작하기 전에 했던 내용과 완전히 일치한다.
두번봐도 세번봐도 좋다. 생각보다 공부과정에서 자주 잊어버렸다.
1단계: 소켓 생성 | socket 함수 호출 |
2단계: IP주소와 PORT번호 | bind 함수 호출 |
3단계: 연결 요청 가능 상태로 변경 | listen 함수 호출 |
4단계: 연결요청에 대한 수락 | accept 함수 호출 |
!주의사항
visual studio 환경으로 진행하면 외부 의존성 환경을 UI단위로 조정하라고(설정을 통해 조정) 책에서 가이드 하지만 본인은 작성중
실행 환경
Visual Studio 2022
개발자 PowerShell로 빌드 및 실행
#pragma comment(lib, "ws2_32.lib")
위와 같은 코드를 작성하여 쉽게 처리했다.
#include<winsock2.h>
반환
if 성공: 0 반환
if 실패: -1 반환
파라미터
WORD mVersionRequested : 프로그래머가 사용하는 winsock의 버전정보 전달
LPWSADATA lpWSAData : WSADATA라는 구조체 변수의 주소값 전달
MAKEWORD(1,2) -> 주버전 1, 부버전 2
MAKEWORD(2,2) -> 주버전 2, 부버전 2
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
void error_handling(char *message);
int main(int argc, char *argv[]){
int serv_sock;
int clnt_sock;
struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size;
char message[] = "Hello World!";
if(argc != 2){
printf("Useage : <port>\n", argv[0]);
exit(1);
}
serv_sock=socket(PF_INET, SOCK_STREAM, 0);
//여기서 소켓이 생성된다.
if(serv_sock == -1){
error_handling("socket() error");
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_addr.sin_port=htons(atoi(argv[1]));
if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr))==-1)
error_handling("bind() error");
if(listen(serv_sock, 5)==-1)
error_handling("listen() error");
clnt_addr_size=sizeof(clnt_addr);
clnt_sock=accept(serv_sock, (struct sockaddr*) &clnt_addr, &clnt_addr_size);
if(clnt_sock==1)
error_handling("accept() error");
write(clnt_sock, message, sizeof(message));
close(clnt_sock);
close(serv_sock);
return 0;
}
void error_handling(char *message){
fputs(message, stderr);
fputc('\n',stderr);
exit(1);
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
void error_handling(char *message);
int main(int argc, char *argv[]){
//argc: main함수에 전달되는 데이터의 길이
//argv: main함수에 전달되는 실질적인 데이터, 동적 배열의 포인터로 제공된다.
int sock;
struct sockaddr_in serv_addr;
char message[30];
int str_len;
if(argc!=3){
printf("Useage : %s <IP> <port>\n", argv[0]);
exit(1);
}
sock=socket(PF_INET, SOCK_STREAM, 0);
if(sock==-1)
error_handling("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
serv_addr.sin_port=htons(atoi(argv[2]));
if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
error_handling("connect() error");
str_len=read(sock, message, sizeof(message)-1);
if(str_len==-1)
error_handling("read() error");
printf("Message from server : %s\n", message);
close(sock);
return 0;
}
void error_handling(char *message){
fputs(message, stderr);//fputs는 문자열 배열을 출력할 때 사용한다
fputc('\n', stderr); // fputc는 파일 입출력시에 사용한다.
exit(1);
}
#pragma comment(lib, "ws2_32.lib")
#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[] ="HelloWorld";
if(argc!=2){
printf("Useage : %s <port>\n", argv[0]);
exit(1);
}
if(WSAStartup(MAKEWORD(2,2), &wsaData)!=0)
ErrorHandling("WSAStartup() error!");
//소켓 라이브러리를 초기화하고 있다.
hServSock=socket(PF_INET, SOCK_STREAM, 0);
//소켓을 생성하고 IP주소와 PORT번호를 할당하고 있다.
if(hServSock==INVALID_SOCKET)
ErrorHandling("bind() error");
memset(&servAddr, 0, sizeof(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);
//send 함수 호출을 통해서 accept
closesocket(hClntSock);
closesocket(hServSock);
WSACleanup();
//종료전 소켓 라이브러리 해제
return 0;
}
void ErrorHandling(char* message){
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
/*
memset(void* ptr, int value, size_t num) 함수
메모리를 특정 값으로 세팅하는 함수
ptr : 세팅하고자 하는 메모리의 시작 주소, 포인터의 위치
value : 메모리에 할당하고자 하는 실질적 데이터, int이지만 저장시 unsigned char로 저장된다
num : 길이, 바이트 단위
*/
#pragma comment(lib, "ws2_32.lib")
#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(2,2), &wsaData))
ErrorHandling("WSAStartup() error!");
hSocket=socket(PF_INET, SOCK_STREAM, 0);
if(hSocket==INVALID_SOCKET)
ErrorHandling("socket() error");
memset(&servAddr, 0, sizeof(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)-1,0);
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);
}
windows 기준으로 실행합니다.