미션
1. 실시간으로 받은 내용을 output 해보자
2. buffer 크기 이상으로 받은 내용을 output 해보자
(hint : 무조건 한번에 출력해야하는건 아니다. 나눠서 출력해도 무방)
1번은 굉장히 쉽다. 버퍼가 일을 잘해주기 때문에 그 이상의 입력값도 알아서 인식해서 뱉어준다.
그런데 어려운건 [str의 끝은 0이어야 한다]는 규칙을 지키며 깔끔하게 짜는게 어려웠다.
전체 코드는 다음과 같다. 6으로 제한을 걸어둔 상태로 그 이상도 깔끔하게 나오게 해보기 위해 노력해보았다.
#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>
#define MSG_MAX 6
#define BUF_LEN 6
int main(void){
short cmd;
int r;
int opt = 1;
fputs("원하는 기능을 선택하세요(1 : 서버대기 2 : 클라이언트) : ", stdout);
fgets((char *)&cmd, 2, stdin);
if(cmd == '1') {
int server_sock;
struct sockaddr_in sockaddr;
/*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(5001);
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");
{
int client_sock;
struct sockaddr_in client_addr;
int socklen, recv_len = BUF_LEN;
int is_first;
char buf[BUF_LEN] = {0,};
char ch;
/* 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'){
/*fputs("메시지를 입력하세요 : ", stdout);
fgets(msg, MSG_MAX, stdin);
printf("%s\n", msg);*/
int client_sock;
struct sockaddr_in client_addr;
char buf[BUF_LEN];
int recv_len;
char msg[MSG_MAX];
/* 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(5001);
inet_pton(AF_INET, "172.16.3.80", &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");
memset(buf, 0, BUF_LEN);
memset(msg, 0, MSG_MAX);
fflush(stdout);
while(getchar() != '\n');
while (1){
fputs("메시지를 입력하세요 : ", stdout);
fflush(stdout);
do {
fgets(msg, MSG_MAX, stdin);
r = send(client_sock, msg, strlen(msg), 0);
} while (msg[r-1] != '\n');
}
close(client_sock);
}
else printf("1, 2만 입력하세요");
return 0;
}
배운점
1. Str 끝엔 항상 0이 와야한다.
이건 시큐어 코딩의 기본중에 기본. 대충 짜도 사실 돌아간다. 하지만 문자열 뒤에 sudo ls 가 붙어있다면?
처음에는 buffer 가 끝에 0이 안와도 괜찮은줄 알았다 그런데 그건 그냥 운이 좋아서 그런거였고
뒤에 문자열이 붙어있다면 이상하게 출력되는 거다
그래서 항상 버퍼를 쓸때는 0을 끝에 붙여주고 버퍼-1 개씩 끌어오게 설계해야 한다.
2. 조건문을 printf("%s%c", buf, ch == '\n' ? '\n' : ch); 로
ch의 값이 옳을때는 앞에거, 아닐때는 뒤에거.
if를 안쓰고도 간략 조건문을 쓸 수 있다.
3. memset을 char buf[BUF_LEN] = {0,}; 로
저렇게 하면 memset(char, 0, BUF_LEN) 를 선언할 때 간략히 할 수 있다.
4. 리팩토링
server의 규칙을 준수하며 받는 부분을 처음에 짰을 때는 굉장히 길었다..
while (1) {
memset(buf, 0, BUF_LEN);
recv_len = recv(client_sock, buf, BUF_LEN, 0);
if (recv_len < 0) { printf("client close\n"); break; }
else {
if (recv_len < BUF_LEN){
tmp_char = buf[recv_len - 1];
buf[recv_len - 1] = '\0';
printf("msg : %s%c", buf, tmp_char);
}
else if (recv_len == BUF_LEN) {
tmp_char = buf[recv_len - 1];
buf[recv_len - 1] = '\0';
printf("msg : %s%c", buf, tmp_char);
while (tmp_char != '\n') {
recv_len = recv(client_sock, buf, BUF_LEN, 0);
for (int i = recv_len; i < BUF_LEN; i++){
buf[i] = '\0';
}
tmp_char = buf[recv_len-1];
buf[recv_len-1] = '\0';
printf("%s%c", buf, tmp_char);
}
}
}
}
허나 겹치는부분을 줄이고 위에서 배운것들을 쓰니 많이 줄었다.
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);
}
}
'개발 > C' 카테고리의 다른 글
[C] buffer memory flush 하는법 (fgets) (0) | 2022.12.26 |
---|---|
[C] 형식 지정자 (ex. %d, %s, %o...) (0) | 2022.12.26 |
[C] fgets와 strcmp 를 같이 쓸때 주의할점 (0) | 2022.12.23 |
[C] scanf() fgets() fscanf() sscanf() 차이점 (0) | 2022.12.22 |
[네트워크] [C 소켓통신 #1] codelite 이용한 소켓통신 (send, receive flag 의미) (0) | 2022.12.21 |