정말 간단한 에코서버를 TCP소켓과, UDP소켓으로 구현해 보았다. 소켓 API를 어떻게 사용하는지 정도만 알아보는데 참고하면 좋을듯 하다.
TCP Socket
Client
#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#define BUF_SIZE 128
int main(int argc, char *argv[]) {
/*
* 1. local variable 선언
*
* s : socket descriptor
* server_addr : server 의 주소를 정의하는 구조체
* ip_addr : server 의 ip address
* buf : input, receiver buffer
*/
int sd;
struct sockaddr_in server_addr;
char *ip_addr = "127.0.0.1";
char buf[BUF_SIZE+1];
/*
* 2. socket 생성
*
* PF_INET : 인터넷
* SOCK_STREAM : TCP 타입의 socket
*/
if ((sd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
printf("can't create socket\n");
return -1;
}
/*
* 3. setsockopt 를 통한 socket 의 옵션 설정
*/
struct linger lingerOpt;
lingerOpt.l_onoff = 1;
lingerOpt.l_linger = 5;
setsockopt(sd, SOL_SOCKET, SO_LINGER, &lingerOpt, sizeof(lingerOpt));
/*
* 4. server_addr 초기화
*
* #include <string.h>
* void bzero(void *s, size_t n);
* byte string s의 첫바이트부터 n 바이트까지 0으로 채운다.
*/
bzero((char *) &server_addr, sizeof(server_addr));
/*
* 5. server 주소 체계 설정
*/
server_addr.sin_family = AF_INET;
/*
* 6. server ip address 설정
*
* #include <arpa/inet.h>
* in_addr_t inet_addr(const char *cp);
* "a.b.c.d"형태의 IPv4 주소 체계를 network byte order의 binary 주소로 변환한다.
* 주로, socket address family가 AF_INET 타입인 경우에 사용한다.
*/
server_addr.sin_addr.s_addr = inet_addr(ip_addr);
/*
* 7. server port 번호 설정
* #include <arpa/inet.h>
* uint32_t htonl (uint32_t hostlong);
* long 타입 데이터를 host 시스템의 byte order 에서 네트워크의 byte order 로 변경해주는 함수.
*/
server_addr.sin_port = htons(8080);
/*
* 8. server에 존재하는 socket과 연결 시도, 성공시 0 리턴
* 실패시 -1을 리턴하며, 전역변수 errno에 에러코드가 들어있게 된다.
*/
if (connect(sd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
printf("can't connect : %d\n", errno);
return -1;
}
/*
* 9. connect 를 통해 성공적으로 세션이 맺어지면, 이제 Server 와 소켓 통신이 가능하다.
*/
while(1) {
// 사용자 입력
printf("Input : ");
if (fgets(buf, BUF_SIZE, stdin)) {
buf[strlen(buf)-1] = '\0';
} else {
printf("fgets error\n");
break;
}
if(strcmp(buf, "exit") == 0) {
printf("tcp client close socket!\n");
break;
}
// 입력값 서버로 전송
if (send(sd, buf, strlen(buf), 0) < 0) {
printf("send error : %d\n", errno);
break;
}
// 서버로부터 데이터 수신
if (recv(sd, buf, sizeof(buf)-1, 0) < 0) {
printf("recv error : %d\n", errno);
break;
}
buf[BUF_SIZE] = '\0';
printf("Received Message : %s\n", buf);
}
//shutdown(sd, SHUT_WR);
close(sd);
}
Server
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>
#define BUF_SIZE 128
int main(int argc, char *argv[]) {
/*
* 1. local variable 선언
*
* server_sd : server socket 번호
* client_sd : client socket 번호
* len_out : client 로부터 받은 데이터의 길이
* port : socket 에 바인딩 될 서버의 port 번호
* buf : client 와의 통신에서 사용할 버퍼
* server_addr : server socket 주소정보를 담고 있는 구조체
* client_addr : client socket 주소정보를 담고 있는 구조체
* client_addr_len : client_addr 변수의 길이
*/
int server_sd, client_sd; // socket 번호
int len_out;
int port;
char buf[BUF_SIZE+1];
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len;
port = 8080;
/*
* 2. socket 생성
*/
if ((server_sd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
printf("can't open stream socket");
return 0;
}
/*
* 3. server_addr 초기화
*/
bzero((char *)&server_addr, sizeof(server_addr));
/*
* 4. server 주소 체계 설정
* 5. server ip address 설정
* 6. server port 번호 설정
*/
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(port);
/*
* 7. socket 과 socket 주소를 바인딩
*/
if (bind(server_sd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
printf("can't bind local address\n");
return 0;
}
/*
* 8. 소켓을 수동 대기모드로 세팅
*
* listen()의 두번째 파라미터는, 대기가능한 총 client connection 수이다.
* 설정된 값보다 많은 수의 연결이 왔을 때, 설정값 이후의 connection은 거절하여 클라이언트가 바로 알 수 있게 한다.
*/
listen(server_sd, 5);
/*
* 9. accept()에서 block되며, client의 연결을 기다린다.
*/
while(1) {
printf("Waiting connection request...\n");
client_addr_len = sizeof(client_addr);
client_sd = accept(server_sd, (struct sockaddr *)&client_addr, &client_addr_len);
if (client_sd < 0) {
printf("accept failed\n");
break;
}
/*
* 9. client와 연결되고, 세션이 확립 establish 된 상태
* recv, send를 통해 client와 소켓 통신
*/
printf("client connected\n");
while(1) {
bzero(buf, sizeof(buf));
len_out = recv(client_sd, buf, sizeof(buf), 0);
if (len_out < 0) {
printf("read error : %d\n", errno);
break;
} else if (len_out == 0) {
printf("client's close socket\n");
break;
}
printf("client : %s\n", buf);
write(client_sd, buf, len_out);
}
close(client_sd);
}
close(server_sd);
return 0;
}
UDP
Client
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#define PORT 8080
#define BUF_SIZE 128
int main() {
int sd;
char buff[BUF_SIZE];
struct sockaddr_in server_addr;
// 소켓 생성
if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
printf("can't create socket\n");
return -1;
}
// 소켓 주소 구조체 초기화
bzero((char *)&server_addr, sizeof(server_addr));
// 서버 소켓 주소 설정
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(PORT);
int n;
socklen_t len;
while(1) {
printf("Input : ");
if (fgets(buff, BUF_SIZE, stdin)) {
buff[strlen(buff)-1] = '\0';
} else {
printf("fgets error\n");
break;
}
// UDP server 를 종료한 후, sendto()를 실행하면 프로그램이 block 되는데, 어떻게 예외처리를 해야 할 까?
// sendto()가 block 되는 이유는 OS 마다 다르다고 한다.
if (sendto(sd, buff, strlen(buff), 0, (const struct sockaddr *) &server_addr, sizeof(server_addr)) == -1) {
printf("UDP server connection error : %s\n", strerror(errno));
break;
}
if (strcmp(buff, "exit") == 0) {
printf("UDP client exit\n");
break;
}
n = recvfrom(sd, (char *)buff, BUF_SIZE, MSG_WAITALL, (struct sockaddr *) &server_addr, &len);
buff[n] = '\0';
printf("Server(%s:%d) : %s\n", inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port) ,buff);
}
close(sd);
return 0;
}
Server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define PORT 8080
#define BUF_SIZE 128
int main() {
int server_sd;
char buff[BUF_SIZE];
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len;
// 소켓 생성
if ((server_sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
printf("can't open diagram socket\n");
return 0;
}
// 소켓 주소 구조체 초기화
bzero((char *)&server_addr, sizeof(server_addr));
bzero((char *)&client_addr, sizeof(client_addr));
// 서버 소켓 주소 설정
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(PORT);
// 서버 소켓에 주소 바인딩
if (bind(server_sd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0 ) {
printf("can't bind socket\n");
return 0;
}
int n;
client_addr_len = sizeof(client_addr); //len is value/resuslt
while(1) {
bzero(buff, sizeof(buff));
n = recvfrom(server_sd, (char *)buff, BUF_SIZE, MSG_WAITALL, ( struct sockaddr *) &client_addr, &client_addr_len);
buff[n] = '\0';
if (strcmp(buff, "exit") == 0) {
printf("UDP server stop\n");
break;
}
printf("Client(%s:%d) : %s\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buff);
sendto(server_sd, buff, strlen(buff), 0, (const struct sockaddr *) &client_addr, client_addr_len);
}
return 0;
}
'Network' 카테고리의 다른 글
Socket Programming - TCP 세션 수립 및 종료 (0) | 2020.11.30 |
---|---|
Socket Programming - Socket (0) | 2020.11.08 |