TCP로 메시지 수신 중 메시지 유실

idrukawa의 이미지

제가 구조체로 정의한 메지시(size 1024)를 TCP로 주고 받는 두 프로세스가 있습니다.

snd : 1024bytes 메시지를 usleep(1000)마다 send하는 프로세스(sequence number도 넘김니다.)
rcv : 1024bytes * 1024 짜리 버퍼에 recv() 할때마다 count도 하고 받은 메시지의 seq도 출력하는 프로세스
sig : rcv 프로세스에게 sleep(1)마다 SIGSTOP와 SIGCONT를 반복해서 보내는 프로세스

이렇게 3개의 프로세스가 있는데요 rcv에서 확인해보면 seq와 count가 다릅니다. 즉, 메시지가 유실이 된다는 건데요. TCP는 메시지 유실이 없는걸로 알고 있는데... 왜 메시지 유실이 생길까요? 버퍼사이즈도 충분하다고 생각하고 있고 더 키워도 보았는데 유실은 생깁니다...

제 짧은 생각으로는 SIGSTOP동안 버퍼에 메시지를 담지 못하는듯 하는데요(그러면 snd가 보내지 말아야 한다고 생각했지만... 실제로 유실이 발생하네요 ㅠ)
SIGSTOP과 메시지 유실 관련이 있나요?
해법도 알려주시면 너무 감사합니다.^^

-----------------------------------------
rcv.cpp

void create_server_socket_select(server_info_t *serv_info)
{
int client[MAX_CLIENT]; // array of client socket fd
int maxfd, maxi;
int nready;
int i;
fd_set fd_array, init_array; // fd[0] ... fd[maxfd], init_array exist for initialize

int bind_result, write_result, msg_len, addr_len;
int socket_fd, connect_fd;
char buf[MSG_LEN];
struct sockaddr_in server_addr, client_addr;
pid_t childpid;

struct msg_s *msg;
int cnt = 0;

socket_fd = socket(AF_INET, SOCK_STREAM, 0); // create socket for listen
if(socket_fd < 0){
printf("server: can't create socket.\n");
exit(-1);
}

memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; // set IPv4
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // transform ip address to big endian
server_addr.sin_port = htons(serv_info->port_number); // transform port number to big endian
// printf("serv_info->port_number: %d\n", serv_info->port_number);

bind_result = bind(socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if(bind_result < 0){
printf("server: bind error!!\n");
exit(-1);
listen(socket_fd, LISTENQ); // socket_fd is listen socket

maxfd = socket_fd; // initialize
maxi = -1; // index into client[] array
for(i=0 ; i client[i] = -1; // -1 indicates available entry
}
FD_ZERO(&init_array); // all bit off(0) in init_array
FD_SET(socket_fd, &init_array); // turn on(1) bit for socket_fd번째 in init_array

while(1){
fd_array = init_array; // structure assignment
nready = select(maxfd+1, &fd_array, NULL, NULL, NULL);

if(FD_ISSET(socket_fd, &fd_array)){
addr_len = sizeof(client_addr);
connect_fd = accept(socket_fd, (struct sockaddr *)&client_addr, &addr_len); // connect_fd is connect socket
if(connect_fd < 0){
printf("server: accept error!!\n");
exit(-1);
}
printf("connection new client (connect_fd: %d)...\n", connect_fd);

for(i=0 ; i if(client[i] < 0){
client[i] = connect_fd; // save descriptor in fd_array
break;
}
}

if(i == MAX_CLIENT){
printf("too many clients\n");
}
FD_SET(connect_fd, &init_array); // add new descriptor to set
if(connect_fd > maxfd){
maxfd = connect_fd; // for select
}
if(i > maxi){
maxi = i; // max index in client[] array
}
if(--nready <= 0){
continue; // no more readable descriptor
}

}

for(i=0 ; i<=maxi ; i++){ // check all clients for data
connect_fd = client[i];
if(connect_fd < 0){
continue;
}

if(FD_ISSET(connect_fd, &fd_array)){
memset(buf, '\0', sizeof(buf));
msg_len = read(connect_fd, buf, MSG_LEN);

if(msg_len == 0){
close(connect_fd);
FD_CLR(connect_fd, &init_array);
client[i] = -1;
}
// what you want to do
else{
msg = (struct msg_s *)buf; // msg start pointer
printf("%d\n", msg->seq);
cnt++;
printf("cnt : %d\n", cnt);
}
if(--nready <= 0){
break; // no more readable descriptor
}
}
}
}

return;
}

-----------------------------------------
snd.cpp

#include
#include
#include
#include
#include
#include
#include

#define BUFF_SIZE 4056
#define MAX_MSG 10000

struct msg_s{
int seq;
char msg[16];
};

int main( int argc, char **argv)
{
int client_socket;

struct msg_s msg;
struct sockaddr_in server_addr;

char buff[BUFF_SIZE+5];

client_socket = socket( PF_INET, SOCK_STREAM, 0);
if( -1 == client_socket)
{
printf( "socket 생성 실패\n");
exit( 1);
}

memset( &server_addr, 0, sizeof( server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons( 4000);
server_addr.sin_addr.s_addr= inet_addr( "127.0.0.1");

if( -1 == connect( client_socket, (struct sockaddr*)&server_addr,
sizeof( server_addr) ) )
{
printf( "접속 실패\n");
exit( 1);
}

for(int i = 0 ; i < MAX_MSG ; i++){
msg.seq = i;
send( client_socket, &msg, sizeof(struct msg_s) + 1, 0); // +1: NULL까지 포함해서 전송
printf("send: %d\n", msg.seq);
usleep(1000);
}

return 0;
}

ssehoony의 이미지

MSG_LEN 값이 얼마 인지에 따라 작동 방식이 다를 텐데요.

send를 1024씩 여러번 했다고
recv가 1024씩 여러번 되는 건 아니라는거 아시나요?

send를 1024로 두번 한 후에 recv를 하면 한번에 2048로 수신 될 수 있습니다.

idrukawa의 이미지

그러면 메시지 유실이 안일어났을 수도 있다는 것인가요?

첫번째 코드가 rcv.cpp인데요

memset(buf, '\0', sizeof(buf));
msg_len = read(connect_fd, buf, MSG_LEN);

MSG_LEN만큼만 read 하도록 코딩했는데
한꺼번에 2048을 읽어 들이나요?

gilgil의 이미지

msg_len의 길이를 확인해 보세요.

msg_len = read(connect_fd, buf, MSG_LEN);

디버깅하지 말고 트레이스로 Log로 찍어 보세요.
이상한 값이 나올 겁니다.

일반적으로 TCP 커넥션이 맺어 질 때 MSS는 1460바이트이기 때문에
send를 1024, 1024를 하더라도
수신에서는 1460, 588 로 수신될 수 있습니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.