개발/C

[C 소켓통신 #3] HTTP에 메시지를 보내고 받기

yeonlee 2022. 12. 30. 12:38

이번엔 HTTP를 통해 통신을 해보자.

도메인을 입력하면 적절한 메시지로 변환하여 서버에 쏴주고 그 대답을 듣는 내용이다.

추후엔 읽어들일때 buf length를 인식해 종말점을 계산해주는 기능을 추가하려 한다.

 

배운점

1. character pointer를 문자열로 바꾸는법

char *ip_char = "172.16.3.80";
char ip[IP_LEN];

for(int i = 0; i < 16; i++) {
    ip[i] = ip_char[i];
}

 

2. 구조체를 포인터로 수정하는법

int a;
int *b = &a;

*b = 10

struct addr c;
struct addr *d = &c;
c.ip
d->ip

구조체는 . 으로 접근해서 수정.

하지만 포인터는 . 으로 접근할수 없어서 만든것이 ->

 

 

 

전체 소스코드

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#define PORT_LEN 8
#define IP_LEN 32
#define MSG_MAX 6
#define BUF_LEN 6
#define URL_LEN 128
#define PORT 80

int main(void){
    char cmd_str[4];
    short cmd;
    char tmp;
    int r;
    int opt = 1;
    char port[PORT_LEN];
    char ip[IP_LEN];
    char URL[URL_LEN];
    char proto[IP_LEN];
    char path[IP_LEN];
    char **path_ptr = path;
    char domain[URL_LEN];
    int flag = 0;
    int flag_str = 0;
    char *ip_char;
    struct hostent *he;
    struct in_addr **addr_list;
    struct in_addr addr;
    
    memset(proto, 0, IP_LEN);
    memset(ip, 0, IP_LEN);
    memset(path, 0, IP_LEN);
    memset(URL, 0, IP_LEN);
    
    fputs("원하는 기능을 선택하세요(1 : 서버대기  2 : 클라이언트) : ", stdout);
    fgets(cmd_str, 4, stdin); 
    cmd = cmd_str[0];
    
    if (cmd == '1'){
        
        int server_sock;
        struct sockaddr_in sockaddr;
        int client_sock;
        struct sockaddr_in client_addr;
        int socklen, recv_len = BUF_LEN;
        int is_first;
        char buf[BUF_LEN] = {0,};
        char ch;
        
        fputs("포트를 입력하세요 : ", stdout);
        fgets(port, sizeof(port) , stdin);
        port[strlen(port)-1] = '\0';
        printf("포트넘버 %s로 통신합니다\n", port);
        
        /*create server socket*/
        server_sock = socket(AF_INET, SOCK_STREAM, 0);
        if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, (socklen_t)sizeof(opt)) < 0) printf("socket set opt failed");
        
        /*set server socket address*/
        memset(&sockaddr, 0, sizeof(struct sockaddr_in));
        sockaddr.sin_family = AF_INET;
        sockaddr.sin_port = htons((unsigned short int)atoi(port));
        sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        printf("socket address\n");
        
        /* bind */
        r = bind(server_sock, (struct sockaddr *)&sockaddr, sizeof(struct sockaddr_in));
        if (r < 0) { printf("bind failed Error code: %d\n", errno);  return -1;}
        printf("bind\n");

        /* listen */
        r = listen(server_sock, 5);
        if (r < 0) { printf("listen failed\n");  }
        printf("listen\n");
        {
            /* accept */
            memset(&client_addr, 0, sizeof(struct sockaddr_in));
            client_sock = accept(server_sock, (struct sockaddr *)&client_addr, (socklen_t *)&socklen);
            if (client_sock < 0) { printf("S accept failed\n"); return -1; }
            printf("S accept\n");
            
            while (1) {
                is_first = 1;
                ch = 0;
                while (ch != '\n') {
                    memset(buf, 0, BUF_LEN);
                    recv_len = recv(client_sock, buf, BUF_LEN, 0);
                    if (recv_len < 0) { printf("client close\n"); break; }
                    
                    ch = buf[recv_len - 1];
                    buf[recv_len - 1] = 0;
                    
                    if (is_first) { printf("msg : "); is_first = 0; }
                    printf("%s%c", buf, ch == '\n' ? '\n' : ch);
                }
            }
        }
        close(server_sock);
    }
    else if (cmd == '2'){
        
        int client_sock;
        struct sockaddr_in client_addr;
        char buf[512];
        int recv_len;
        char msg[MSG_MAX];
        char message[512], response[4096];
        int bytes, sent, received, total;
        int send_len, bufrecv_len;
        char buf_recv[512];
        
        memset(buf_recv, 0, 512);
        memset(msg, 0, MSG_MAX);
        memset(message, 0, 512);
        
        while (1){
            fputs("도메인을 입력하세요 : ", stdout);
            fgets(domain, sizeof(domain) , stdin);
            
            
            if (domain[0] == 'h') {
                strncpy(URL, domain, URL_LEN);
                
                for (int i = 0; i < strlen(URL); i++){
                    if (URL[i] == ':') flag++;
                    if (flag == 0) proto[i] = URL[i];
                    if (flag == 1) break;
                }
                for (int i = strlen(proto); i < strlen(URL); i++){
                    if (flag == 3) {
                        ip[flag_str] = URL[i];
                        flag_str++;
                    }
                    if (URL[i] == '/') flag++;
                    if (flag == 4) break;
                }
                flag_str = 0;
                for (int i = strlen(proto) + strlen(ip) + 2; i < strlen(URL); i++){
                    path[flag_str] = URL[i];
                    flag_str++;
                }    
                
                path[strlen(path)-1] = '\0';
                printf("프로토콜은 %s, 도메인은 %s, path는 %s 입니다. \n", proto, ip, path);
                memset(domain, 0, strlen(domain));
                strncpy(domain, ip, strlen(ip));
            }
            port[strlen(port)-1] = '\0';
            domain[strlen(domain)-1] = '\0';
            
            if (domain[0] == 'w') {
                strncpy(URL, domain, URL_LEN);
                
                for (int i = 0; i < strlen(URL); i++){
                    if (URL[i] == '/') flag++;
                    if (flag == 0) ip[i] = URL[i];
                    if (flag == 1) break;
                }
                for (int i = strlen(ip); i < strlen(URL); i++){
                    path[flag_str] = URL[i];
                    flag_str++;
                }
                printf("도메인은 %s, path는 %s 입니다. \n", ip, path);
                memset(domain, 0, strlen(domain));
                strncpy(domain, ip, strlen(ip));
            }
            
            
            if((he = gethostbyname(domain)) == NULL) {
                fprintf(stderr, "%s는 등록되지 않은 서버명입니다.\n", domain);
                return -1;
            }
            
            printf("포트넘버 %s", port);
            he = gethostbyname(domain);
            printf("ip 주소 타입은 %d ", he->h_length);
            printf("공식명칭은 %s ", he->h_name);
            ip_char = inet_ntoa(*(struct in_addr*)he->h_addr);
            for(int i = 0; i < 16; i++){
                ip[i] = ip_char[i];
            }
            
            printf("IP주소는 %s입니다\n", ip);
            
            
            {
                /* client socket */
                client_sock = socket(AF_INET, SOCK_STREAM, 0);
                opt = 1;
                
                setsockopt(client_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
                if (client_sock < 0) { printf("C socket failed\n");  return -1;}
                printf("C socket\n");
                
                /* client_sockaddr */
                memset(&client_addr, 0, sizeof(struct sockaddr_in));
                client_addr.sin_family = AF_INET;
                client_addr.sin_port = htons(PORT);
                inet_pton(AF_INET, ip, &client_addr.sin_addr.s_addr);
                printf("C socket address\n");
                
                /* connect */
                r = connect(client_sock, (struct sockaddr *)&client_addr, sizeof(struct sockaddr_in));
                if (r < 0) { printf("C connect failed errno : %d\n", errno); return -1; }
                printf("C connect\n");
                
                /*rearrange message */
                if (strlen(path) == 0){
                    path_ptr[0] = 0x2F;
                }
                char *msg1 = "GET \0";
                char *msg2 = " HTTP/1.1\nHOST: \0";
                char *msg3 = "\nUser-Agent: curl/7.68.0\nAccept: */*\n\n\0";
                char str1[strlen(msg1)+1], str2[strlen(msg2)+1], str3[strlen(msg3)+1];
                memset(str1, 0, strlen(msg1)+1);
                memset(str2, 0, strlen(msg2)+1);
                memset(str3, 0, strlen(msg3)+1);
                for (int j = 0; j < strlen(msg1); j++){
                    str1[j] = msg1[j];
                }
                for (int j = 0; j < strlen(msg2); j++){
                    str2[j] = msg2[j];
                }
                for (int j = 0; j < strlen(msg3); j++){
                    str3[j] = msg3[j];
                }
                memcpy(message, str1, strlen(str1));
                memcpy(message+strlen(str1), path, strlen(path));
                memcpy(message+strlen(str1)+strlen(path), str2, strlen(str2));
                memcpy(message+strlen(str1)+strlen(path)+strlen(str2), domain, strlen(domain));
                memcpy(message+strlen(str1)+strlen(path)+strlen(str2)+strlen(domain), str3, strlen(str3));
                printf("<<request msg>>\n%s", message);

                
                /* request/response */
                send_len = send(client_sock, message, strlen(message), 0);
                if (send_len < 1) printf("C send failed\n");
                printf("<<response msg>>\n");
                while(1) {
                    recv_len = recv(client_sock, buf_recv, 512, 0);
                    if (recv_len < 1) {
                        printf("C recv ended\n");
                        break;
                    }
                    printf("%s", buf_recv);
                }
                close(client_sock);
            }
        }
    } else printf("1, 2만 입력하세요");
        
    
	return 0;
}