1. socket
int socket(int family, int type, int protocol);
family : 인터넷 프로토콜 체계 명명
type : soket type
protocol : TCP, UPD 이런 것 중 어느 것을 사용 할 지
socket 사용 코드
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
void err_quit ( char *msg)
{
printf ("socket error\n");
printf ("errno=%d, %s\n",errno, strerror(errno) );
perror (msg);
exit (0);
}
int Socket(int domain, int type, int protocol)
{
int sd;
/* soket input
* 1st : 인터넷 프로토콜 버전
* 2st : 소켓 형태
*/
sd = socket(AF_INET, SOCK_STREAM, 0);
if ( sd < 0 )
{
err_quit ("socket()");
}
}
int main()
{
int fd;
int sd;
fd = open ("/etc/password", O_RDONLY, 0);
sd = Socket(AF_INET, SOCK_STREAM, 0);
if ( sd < 0 )
{
err_quit ("socket()");
}
printf ("fd=%d\n",fd);
printf ("sd=%d\n",sd);
return 0;
}
socket을 통해 구글 사이트 가져오기 ( client )
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
int main()
{
int fd;
int sd;
int nret;
char buf[1024];
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(80);
addr.sin_addr.s_addr = inet_addr("142.250.76.142");
/* network에서 사용 하는 초기화 함수 */
bzero (addr.sin_zero, 8);
sd = socket(AF_INET, SOCK_STREAM, 0);
printf ("sd = %d\n",sd);
/* 구글에 connect */
nret = connect( sd,(struct sockaddr*)&addr, sizeof(addr) );
printf ("connct nret = %d\n",nret);
write ( sd, "GET /\n\n", 7);
nret = read (sd, buf, sizeof(buf) );
printf ("read nret = %d\n",nret);
while ( nret = read ( sd, buf, sizeof(buf) ) )
{
write ( 1, buf , nret );
}
close(sd);
return 0;
}
echo 서버 만들기 ( Server )
/* server */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
int main()
{
int fd;
int sd;
int new_sd;
int nret;
char buf[1024];
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(7777);
addr.sin_addr.s_addr = inet_addr("001.001.01.001");
/* network에서 사용 하는 초기화 함수 */
bzero (addr.sin_zero, 8);
sd = socket(AF_INET, SOCK_STREAM, 0);
printf ("sd = %d\n",sd);
/* 생성된 소켓에 Local Prtocol Address(Ip+Port)를 지정 */
nret = bind(sd , ( struct sockaddr *) &addr, sizeof ( addr ));
printf ("nret = %d\n",nret);
/* 대기듕 */
nret = listen ( sd, 5);
printf ("nret = %d\n",nret);
while ( ( new_sd = accept(sd , NULL, NULL ) ) >= 0 )
{
/* complete queue에 대기중인 연결요청에 대해 새로운 소켓을 만듬 */
/* 대기중인 연결요청 항목이 없는 경우 block */
printf ("new_sd = %d\n",new_sd);
while ( nret = read(new_sd, buf, sizeof ( buf ) ) )
{
write ( new_sd , buf , nret );;
}
close (new_sd);
}
close (sd);
return 0;
}
/* client */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
int main()
{
int fd;
int sd;
int nret;
char buf[1024];
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(7777);
addr.sin_addr.s_addr = inet_addr("001.001.01.001");
/* network에서 사용 하는 초기화 함수 */
bzero (addr.sin_zero, 8);
sd = socket(AF_INET, SOCK_STREAM, 0);
printf ("sd = %d\n",sd);
nret = connect( sd,(struct sockaddr*)&addr, sizeof(addr) );
printf ("connct nret = %d\n",nret);
while (1)
{
nret = read (0, buf, sizeof(buf) ); /* 키보드입력을 받음 */
buf[nret-1] = 0;
if ( strcmp ( buf, "exit" ) == 0 )
break;
write ( sd, buf, nret );
nret = read ( sd , buf, sizeof ( buf ) );
write ( 1, buf, nret );
printf ("read nret = %d\n",nret);
}
close(sd);
return 0;
}
* bind
* listen
* accep
용도
- complete queue에 대기중인 연결요청에 대해 새로운 소켓을 만듬
- 대기중인 연결요청 항목이 없는 경우 block된다
왜 새로운 소켓을 만드는가?
- 연결용 소켓(listen socket)과 데이터 처리용 소켓이 따로 필요하기 때문.
- listen socket ( client의 정보가 지정되어 있지 않은 소켓 - daddr : 0, dport : 0)
- 새로운 client의 최초 socket의 경우 반드시 listen socket으로 처음 등장한다.
- 3 way handshaking을 하는 동안 daddr과 dport가 지정된 새로운 소켓이 생성 된다.
2. fork
fork - create a child process
단 부모와 동일한 프로그램 주소를 갖고 있다.
parent와 child 둘이 동시성을 갖을 수 있다.
return : child - 0, parent - 0보다 큰 값 ( = PID )
child process die, parent process live, child의 자원이 모두 해체되지 않음 -> zombie process
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
void my_sig(int signo)
{
int status;
int pid;
/* child가 10000개 이럴경우 너무 많은 child가 한번에
* 죽게 되면서 parent들을 다 죽이지 못함 */
/* 죽어야 하는 parent가 여럿일 경우를 위해 loop를 설정 */
//while ( ( pid = wait(&status) ) > 0 ) // 자식중에 살아있는 프로세스가 있다면 동시성을 잃게 됨
while( (pid = waitpid(-1, &status, WNOHANG)) > 0 ) //nonblocking 지원
printf("my_sig(%d), pid=%d\n", signo, pid);
}
int main()
{
int i,j;
int pid;
signal(SIGCHLD, my_sig);
for(i=0; i<5; i++)
{
pid = fork();
if (pid == 0 )
{
for(j=0; j<i+1; j++)
{
printf("\t\t\tchild\n");
sleep(1);
}
exit(3);
}
}
while(1)
{
printf("parent\n");
sleep(1);
}
return 0;
}
exit - 프로세스를 죽이고 프로세스의 exit_code에 값을 남긴다.
wait - 프로세스의 상태를 확인, child가 죽을 때까지 대기. 자식 프로세스가 종료되었다면 함수는 즉시 리턴되며, 자식이 사용한 모든 시스템자원을 해제한다.
3. 동시 접속 가능 서버 만들기 ( nonblock )
blocking : 특정이벤트가 있을 때까지 자신의 동작을 멈춤.
server로 치면 accept가 끝나고 read로 넘어가면 accept가 blocking 상태로 변하게 된다.
ex : A가 채팅 하고, B가 채팅 하는 것은 가능. 그러나 A가 여러번 채팅 하는 것이 불가능 하다. A가 채팅을 1회 하고, A는 blocking 되었기 때문에 동작을 멈춘 상태.
신규 client가 accept 하는 것이 불가한 상태. -> nonblock이 되어야 함. fcntl()를 활용!
이렇게 nonblock을 사용하는 경우 무척, 퍼포먼스가 떨어진다. -> multi process로 해결
[fork server 만들기]
multi process
자식 프로세스에서 한줄 read하고, 자식 프로세스를 바로 죽임 -> 반복, 마치 nonblocking 처럼 작동
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
void my_sig(int signo)
{
int status;
int pid;
while( (pid=waitpid(-1, &status, WNOHANG )) > 0 )
printf("my_sig(%d), pid=%d\n", signo, pid );
}
int main()
{
int fd;
int sd, new_sd;
int ret;
char buff[1024];
struct sockaddr_in addr;
struct sockaddr_in client_addr;
int addrlen;
int optval;
signal(SIGCHLD, my_sig);
addr.sin_family = AF_INET;
addr.sin_port = htons(7777);
addr.sin_addr.s_addr = inet_addr("001.001.01.001");
bzero( addr.sin_zero, 8 );
sd = socket(AF_INET, SOCK_STREAM, 0);
/* 서버가 비정상 종료 되어 포트가 busy 하더라도-> server가 time wait인 경우
* 서버 재기동시 무조건 기동되도록. */
optval=1;
setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
ret = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
if( ret < 0 )
{
perror("bind()");
exit(0);
}
ret = listen( sd, 10 );
addrlen = sizeof(client_addr);
while(1)
{
/* 클라이언트 추적을 위해 주소를 담을 공간을 인자로 넘겨줌 */
new_sd = accept( sd, (struct sockaddr *)&client_addr, &addrlen );
printf("새로운 클라이언트 접속 : ip=%s, port=%u\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
if(fork()==0)
{
/* 자식을 만들 후 그 안에서 소켓을 닫고
* 자식프로세스를 죽이는 것을 반복 */
close(sd);
while( ret = read(new_sd, buff, sizeof buff ) )
{
write( new_sd, buff, ret );
}
close(new_sd);
exit(0);
}
close(new_sd);
}
close(sd);
return 0;
}
[select version]
select ()
process가 kernel에게 명령하여 다수의 event 중 하나를 기다리다 지정된 시간이 지나가거나 사건이 발생하면 process를 깨우도록 함. fd_set이라는 구조체를 사용하며, FD_ISSET을 통해 발생한 이벤트를 판단한다. 원하는 이벤트가 발생하면 로직 진행.
#if 1
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/select.h>
int main()
{
int fd;
char buff[1024];
int ret;
fd_set readfds;
fd = open("myfifo", O_RDWR); // # mkfifo myfifo
while(1)
{
FD_ZERO(&readfds);
FD_SET(0, &readfds);
FD_SET(fd, &readfds);
select( fd+1, &readfds, 0, 0, 0);
if( FD_ISSET(0, &readfds) )
{
ret = read(0, buff, sizeof buff );
buff[ret-1] = 0;
printf("키보드 입력 : [%s]\n", buff );
}
if( FD_ISSET(fd, &readfds) )
{
ret = read(fd, buff, sizeof buff );
buff[ret-1] = 0;
printf("myfifo 입력 : [%s]\n", buff );
}
}
close(fd);
return 0;
}
#endif
#if 0
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd;
char buff[1024];
int ret;
fd = open("myfifo", O_RDWR); // # mkfifo myfifo
while(1)
{
ret = read(0, buff, sizeof buff );
buff[ret-1] = 0;
printf("키보드 입력 : [%s]\n", buff );
ret = read(fd, buff, sizeof buff );
buff[ret-1] = 0;
printf("myfifo 입력 : [%s]\n", buff );
}
close(fd);
return 0;
}
#endif
fd_set : 비트를 활용한 구조체
참고
$ man : 섹션 별로 자료를 갖고 있다.
$ man 1 open : 리눅스 명령어 open
$ man 2 open : 프로그램 open
pid : 프로세스 아이디, ppid : 프로세스의 parent 아이디
$kill -l : kill 의 값들을 볼 수 있다.
cat > myinfo : myinfo라는 곳에 키보드를 통해 실시간으로 데이터 입력이 가능.
'IT > Linux(Unix)' 카테고리의 다른 글
남들은 자주 안쓰지만 나는 자주쓰는 VI 꿀팁들 (0) | 2024.10.19 |
---|---|
리눅스 프로그래밍 - 커널 소스 분석하기 ( ftp 서버 구현, 쓰래드 다루기 ) (0) | 2022.08.03 |
리눅스 프로그래밍 - 커널 소스 분석 ( 네트워크 기초 ) (0) | 2022.07.20 |
[VI] Xshell 에러 해결 모음집 (0) | 2021.05.04 |