클라이언트와 서버 모두 동시(concurrent) 모드로 동작 가능
클라이언트 동시성
서버의 동시성
계열(family)
IPv4(IF_INET), IPv6(IF_INET6) 도메인 프로토콜 등과 같은 프로토콜 그룹 정의
유형(type)
4가지 소켓유형인 SOCK_STREAM(TCP용), SOCK_DGRAM(UDP용), SOCK_SEQPACKET(SCTP용), SOCK_RAW(IP서버 직접 이용)
프로토콜
타입에서 정해서 보통 0으로 함
로컬(local) 소켓주소
소켓주소는 IP주소와 포트 번호의 조합
원격지(remote) 소켓 주소
원격지 소켓주소 정의
IP 주소와 포트 번호의 조합을 이해해야한다.
프로세스 생성하기 위한 함수들
int socket(int family, int type, int protocol)
int bind(int sickfd, const struct sockaddress* localAddress, socklen_t addrLen);
int connect(int sockfd, const struct sockaddress* remoteAddress, socklen_t addrLen);
int listen(int sockfd, int backlog);
int accept(int sockfd, const struct sockaddress* clientAddr, socklen_t* addrLen);
pid_t fork(fork);
int send(int sockfd, const void* sendbuf, int nbytes, int flags);
int recv(int sockfd, void* recvbuf, int nbytes, int flags);
int sendto(int sockfd, const void* buffer, int nbytes, int flags struct sockaddr* destinationAddress, socklen_t addrLen);
int recvfrom(int sockfd, void* buffer, int nbytes, int flags struct sockaddr* ourceAddress, socklen_t* addrLen);
int close(int sockfd);
uint16_t htons (uint16_t shortValue);
uint32_t htonl (uint32_t longValue);
uint16_t ntohs (uint16_t shortValue);
uint32_t ntohl (uint32_t longValue);
void* memset(void* destination, int chr, size_t len);
void* memcpy(void* destination, const void* source, size_t nbytes);
int* memcmp(const void* ptrl, const void* ptr2, size_t nbytes);
int inet_pton (int family, const char* stringAddr, void* numericAddr);
char* inet_ntop(int family, const void* numericAddr, char* stringAddr, int len);
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <arpa/innet.h>
#include <sys/wait.h>
//TCP echo client program
#include "headerFiles.h"
int main()
{
//Declaration and definition
int sd; //Socket descriptor
int n; //Number of bytes received
int bytesToRecv; //Number of bytes to recive
char sendBuffer[256]; //send buffer
char recvBuffer[256]; //Recive buffer
char* movePtr; //a pointer the received buffer
struct sockaddr_in serverAddr; //Server address
//Create socket
sd = socket(PF_INET,SOCK_STREAM,0);
//Create server socket address
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(80);
inet_pton(AF_INET, "192.168.200.144", &serverAddr.sin_addr);
//Connect
connect(sd,(struct sockaddr*)&serverAddr, sizeof(serverAddr));
//Send and receive data
while (1) {
/* 메시지 입력 전송*/
fputs("전송할 메시지를 입력하세요(q to quit) : ", stdout);
fgets(sendBuffer, 256, stdin);
if (!strcmp(sendBuffer, "q\n"))
break;
write(sd, sendBuffer, strlen(sendBuffer));
/* 메시지 수신 출력 */
n = read(sd, sendBuffer, 255);
sendBuffer[n] = 0;
printf("서버로부터 전송된 메시지 : %s \n", sendBuffer);
}
close(sd);
exit(0);
}
//Echo server program
#include "headerFiles.h"
int main(void)
{
//Declartion and definition
int listensd;
int connectsd;
int n;
int bytesToRecv;
int processID;
char buffer[256];
char* movePtr;
struct sockaddr_in serverAddr;
struct sockaddr_in clientAddr;
int clAddrLen;
//Create listen socket
listensd = socket(PF_INET, SOCK_STREAM,0);
//bind listen socket to the local address and port
//서버의 주소를 담을 구조체 변수를 0으로 초기화
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
//htonl 호스트 바이트 순서의 데이터를 long int형 네트워크 바이트 순서로 바꿔주는 함수
//INADDR_ANY는 서버가 사용하는 IP주소를 알아서 채워준다.
//만약 안쓰면 주소정보를 찾아서 넣어줘야하고 IP가 바뀌면 코드 자체를 수정해야한다.
//INADDR_ANY)
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(80);
//지정된 소켓에 대해 지역 주소 및 포트번호를 채움
bind(listensd,(struct sockaddr*)&serverAddr, sizeof(serverAddr));
/*Listen to connection request
클라이언트로부터 들어오는 연결들을 허용
클라이언트 접속 기다린다. listen()함수가 동작하지 않았는데
해당 소켓으로 클라가 접속을 해오면 접속해온 클라의 connect()
함수는 실패
즉, 해당 소켓을 이용해 접속을 받을 준비가 되어있다 의미
*/
listen(listensd, 5);
//Handle the connection
for(;;)
{
/* 클라이언트는 해당 소켓을 이용해 메시지를 주고 받는다
* 서버는 accept()함수를 호출 이 함수는 듣고 있는
* 소켓의 포트번호로 들어오는 연결요구 있을 때까지 블록
* 즉,서버 소켓에 결합된 주소와 포트로 들어오는
* 연결들을 기다리면서 블록
* 서버 소켓에 listen()도 호출되어야 한다.
* 클라이언트 중 연결이 도착하여 TCP핸드세이크 성공하면
* 새로운 소켓이 반환되고 클라이언트 소켓 식별자에 반환
* 값을 넣어 사용
*/
connectsd = accept(listensd,(struct sockaddr*)&clientAddr, &clAddrLen);
/*클라 접속때마다 fork통해 child process 생성해 echo발생
* 한클라 연결설정후 해당 클라 종료할때까지 for문 내에 묶여있음, 즉 또다른 클라 동시 통신 불가
* fork() 호출한 프로세스와 똑같은 자원이 메모리에 그대로 복사
*/
processID = fork();
//자식서버일 때
if(processID == 0)
{
//리스닝 소켓 닫아준다.
close(listensd);
bytesToRecv = 256;
movePtr = buffer;
/*recv()는 전달받은 데이터의 바이트수를 반환
* 받은 메시지 크기를 받을 n에 크기를 전달
* movePtr라는 버퍼에 데이터 넣는다.
*/
while((n=recv(connectsd, movePtr, bytesToRecv,0))>0)
{
//상대가 전달할 데이터가 더 있는지
send(connectsd, movePtr, n,0);
}
exit(0);
}
close(connectsd);
}
}
//UDP echo client program
#include "headerFiles.h"
int main(void)
{
int sd; //socket descriptor
int ns; //number of bytes send
int nr; //number of bytes received
char buffer[256]; //data buffer
struct sockaddr_in serverAddr; //socket address
int addrlen = sizeof(serverAddr);
sd=socket(PF_INET, SOCK_DGRAM,0);
memset(&serverAddr, 0 ,sizeof(serverAddr));
serverAddr.sin_family =AF_INET;
serverAddr.sin_port = htons(80);
inet_pton(AF_INET, "192.168.200.144",&serverAddr.sin_addr);
fgets(buffer,256,stdin);
ns = sendto (sd,buffer,strlen(buffer),0,
(struct sockaddr*)&serverAddr, sizeof(serverAddr));
nr = recvfrom(sd, buffer, strlen(buffer),0,(struct sockaddr*)&serverAddr, &addrlen);
buffer[nr] = 0;
printf("Received from server: %s",buffer);
close(sd);
exit(0);
}
//UDP echo server program
#include "headerFiles.h"
int main(void)
{
int sd; //Socket descriptor
int nr; //Number of bytes receiced
char buffer[256]; //data buffer
struct sockaddr_in serverAddr;
struct sockaddr_in clientAddr;
int clAddrLen;
sd = socket(PF_INET, SOCK_DGRAM, 0);
memset(&serverAddr, 0 ,sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(80);
bind(sd,(struct sockaddr*)&serverAddr, sizeof(serverAddr));
for(;;)
{
nr = recvfrom(sd,buffer,256,0,(struct sockaddr*)&clientAddr, &clAddrLen);
sendto(sd,buffer,nr,0, (struct sockaddr*)&clientAddr, sizeof(clientAddr));
}
}