에코 클라이언트 코드
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUFFER_SIZE 1024
void handle_error(char *);
int main(int argc, char *argv[])
{
int sockFd;
struct sockaddr_in serverAddress;
char buffer[BUFFER_SIZE];
int nRead;
if(argc != 3){
printf("Usage: %s <IP> <port>\n", argv[0]);
exit(1);
}
if((sockFd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
handle_error("socket() error.");
memset(&serverAddress, 0, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = inet_addr(argv[1]);
serverAddress.sin_port = htons(atoi(argv[2]));
if(connect(sockFd, (struct sockaddr *)&serverAddress, sizeof(serverAddress)) < 0)
handle_error("connect() error.");
for( ; ; ) {
fputs("Type a message (Q/q to quit): ", stdout);
fgets(buffer, BUFFER_SIZE, stdin);
if(!strcmp(buffer, "Q\n") || !strcmp(buffer, "q\n"))
break;
write(sockFd, buffer, strlen(buffer));
printf("Message sent to server: %ld bytes, %s", strlen(buffer), buffer);
nRead = read(sockFd, buffer, BUFFER_SIZE - 1);
buffer[nRead] = 0; // Necessary for printf()
printf("Message received from server: %d bytes, %s", nRead, buffer);
}
close(sockFd);
return 0;
}
커스템 헤더 mylib.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <errno.h>
ssize_t read_n(int, void *, size_t);
ssize_t write_n(int, const void *, size_t);
ssize_t read_line(int, void *, size_t);
커스텀 mylib.c
#include "mylib.h"
ssize_t read_n(int fd, void *vptr, size_t n)
{
size_t nleft;
ssize_t nread;
char *ptr;
ptr = vptr;
nleft = n;
while(nleft > 0) {
if((nread = read(fd, ptr, nleft)) < 0) {
if(errno == EINTR)
nread = 0;
else
return(-1);
} else if(nread == 0)
break;
nleft -= nread;
ptr += nread;
}
return(n - nleft);
}
ssize_t write_n(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = vptr;
nleft = n;
while(nleft > 0) {
if((nwritten = write(fd, ptr, nleft)) <= 0) {
if(errno == EINTR)
nwritten = 0;
else
return(-1);
}
nleft -= nwritten;
ptr += nwritten;
}
return(n);
}
ssize_t read_line(int fd, void *vptr, size_t maxlen)
{
ssize_t n, rc;
char c, *ptr;
ptr = vptr;
for(n = 1; n < maxlen; n++) {
again:
if((rc = read(fd, &c, 1)) == 1) {
*ptr++ = c;
if(c == '\n')
break; /* newline is stored, like fgets() */
} else if(rc == 0) {
if(n == 1)
return(0); /* EOF, no data read */
else
break; /* EOF, some data was read */
} else {
if(errno == EINTR)
goto again;
return(-1); /* error, errno set by read() */
}
}
*ptr = 0; /* null terminate like fgets() */
return(n);
}
먼저 echo_client2 에서 서버와 통신하는 코드 전문 내용입니다.
위의 주소 초기화 및 connect 부분은 생략하겠습니다.
먼저 미리 선언한 메시지 버퍼에 사용자 입력을 받아, 서버에 전송한 후,
Read 함수를 사용해, 내가 전송한 write에서 반환해주는 str_len만큼 전송받을때까지 무한으로 read를 실행시키고 있습니다.
Read_n으로 교체하기에 앞서, 기존 read와 read_n의 차이를 알아보겠습니다.
기존 read는 버퍼사이즈-1만큼 계속하여 데이터를 읽고 있습니다. 이는 언제 멈춰야 할지 기준 자체가 명확하지 않으며, 불필요한 데이터까지 읽을 가능성이 있습니다.
Read_n을 통하여 남은 바이트 수를 추적하여, read호출이 반환한 바이트 수를 누적해서 정해진 바이트 수를 다 채워질 때까지 반복합니다. 이러한 구조로 필요한 바이트 수만큼의 읽기를 진행합니다. 또한 EINRE처리를 통해 오류를 감지하여 수신에 유연한 핸들링이 가능하게 합니다.
read_n을 사용하여 변경한 코드입니다. 서버로부터 데이터를 수신할 때 read_n()을 사용하여 지정된 바이트(str_len)만큼 정확히 읽어옵니다. 이를 통해 부분적으로 수신된 데이터에 대해 여러 번 read()를 호출할 필요 없이 read_n()이 필요한 바이트를 모두 읽어줄 때까지 반복합니다.
'프로그래밍 > 2024 네트워크 프로그래밍' 카테고리의 다른 글
9. 구조체 헤더를 이용한 소켓통신 - 클라이언트와 헤더파일 살펴보기 (0) | 2025.01.14 |
---|---|
8.에코 서버,클라이언트 통신 살펴보기 (0) | 2025.01.08 |
6. Address conversion (0) | 2025.01.06 |
5. inet_addr 살펴보기 (0) | 2025.01.06 |
4. 빅 엔디안, 리틀 엔디안 (0) | 2025.01.06 |