소켓으로 짜는 프로그램인데요...
글쓴이: cccc2002 / 작성시간: 목, 2003/09/04 - 9:06오전
A클라이언트로 부터 (데이타+특정 아이피)를 받아서 B서버는 특정아이피로 받은 데이터를 넘기는 프로그램을 짜고 있습니다.
여기서 A에서는 send로 반복해서 (다른 특정아이피하고 데이터)를 넘기면 B서버는 다시 받은 또 다른 특정아이피에게 데이터를 넘겨야 합니다.
그래서 B서버에 서버와 클라이언트를 같이 프로그램밍했는데
처음 한 컴퓨터에는 데이터가 넘어가는데 다음에 넘어온 아이피값의 컴퓨터로 넘기는 방법을 잘 모르겠습니다.
고수님들의 의견을 듣고싶습니다.
그럼....
A computer-->send(data,ip1) send(data,ip2) send(data,ip3).......;
b computer-->recv(data,ip1)해서 send(data)ip1에게
recv(data,ip2)해서 send(data)ip 2에게
recv(data,ip3)해서 send(data)ip3에게
#include <stdio.h> #include <fcntl.h> #include <stdlib.h> #include <signal.h> #include <sys/socket.h> #include <sys/file.h> #include <netinet/in.h> #include <string.h> #define MAXLINE 1024 #define MAX_SOCK 64 int getmax(int); void removeAdmin(int); struct client client_info[MAX_SOCK]; char *escapechar="exit"; int maxfdp1; int num_com=0; int i; int main(int argc,char *argv[]) { char rline[MAXLINE],my_msg[MAXLINE]; int command_nflag,command_nsize,option_nflag,option_nsize,ip_nflag,ip_nsize,send_size,recv_size; char command[256],option[256]; char client_ip[16]=""; char *connect_error="[error]"; int s,client_fd,server_fd,clilen; fd_set read_fds; struct sockaddr_in client_addr,server_addr,name_addr; if(argc !=3) { printf("%s port port\n",argv[0]); exit(0); } if((s=socket(PF_INET,SOCK_STREAM,0)) <0) { printf("Server: Can't open stream socket."); exit(0); } //setting server_addr bzero((char *)&server_addr,sizeof(server_addr)); server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr=htonl(INADDR_ANY); server_addr.sin_port=htons(atoi(argv[1])); if(bind(s,(struct sockaddr *)&server_addr,sizeof(server_addr))<0) { printf("Server:Can't bind local address.\n"); exit(0); } listen(s,1); maxfdp1=s+1; while(1) { FD_ZERO(&read_fds); FD_SET(s,&read_fds); for(i=0; i<num_com;i++) FD_SET(client_info[i].client_s,&read_fds); maxfdp1=getmax(s)+1; if(select(maxfdp1,&read_fds,(fd_set *)0,(fd_set *)0,(struct timeval *)0) <0) { printf("select error <= 0 \n"); exit(0); } if(FD_ISSET(s,&read_fds)) { clilen=sizeof(client_addr); client_fd=accept(s,(struct sockaddr *)&client_addr,&clilen); if(client_fd == -1) { printf("accept error\n"); exit(0); } client_info[num_com].client_s=client_fd; client_info[num_com].sin_addr.s_addr=client_addr.sin_addr.s_addr; num_com++; } if(FD_ISSET(client_fd,&read_fds)) { if((recv_size=recv(client_fd,rline,MAXLINE,0)) <= 0) { removeAdmin(i); continue; } if(strstr(rline,escapechar)!=NULL) { removeAdmin(i); continue; } memcpy(&command_nflag,rline,4); memcpy(&command_nsize,rline+4,4); memcpy(command,rline+8,command_nsize); memcpy(&option_nflag,rline+sizeof(int)*2 + command_nsize,4); memcpy(&option_nsize,rline+sizeof(int)*3 + command_nsize,4); memcpy(option,rline+sizeof(int)*4 + command_nsize,option_nsize); send_size=sizeof(int)*4 + command_nsize + option_nsize; memcpy(client_ip,rline + send_size ,4); rline[send_size]='\0'; ////////////////////////////////////// client 부분////////////////////////////////// if((server_fd=socket(AF_INET,SOCK_STREAM,0))<0) { perror("socket"); exit(1); } bzero((char *)&name_addr,sizeof(name_addr)); name_addr.sin_family=AF_INET; name_addr.sin_addr.s_addr=inet_addr(client_ip); name_addr.sin_port=htons(atoi(argv[2])); if(connect(server_fd,(struct sockaddr *)&name_addr,sizeof(struct sockaddr_in))<0){ send(client_fd,connect_error,sizeof(connect_error),0); printf("Connect failed server_ip: %s\n",client_ip); }else if(send(server_fd,rline,send_size,0)<0){ perror("send error"); exit(1); } }close(server_fd); } } void removeAdmin(int i) { close(client_info[i].client_s); if( i !=(num_com -1)) { client_info[i].client_s=client_info[num_com -1].client_s; } num_com--; printf("<=== Admin log out ===>\n"); } int getmax(int k) { int max=k; int r; for(r=0;r<num_com;r++) { if(client_info[r].client_s >max) max=client_info[r].client_s; } return max; }
Forums:
recv_size 를 사용하는 곳이 안보입니다.
프로그램을 자세히 보지는 못했지만, 받은 데이타를 그대로 넘기고 싶으면, recv_size 만큼 보내야 할 것 같은데, recv_size 를 사용하는 곳이 안보입니다.
recv 사이즈만큼 보내는것은 아니구요..
받은 데이터중 뒤부분 아이피부분을 잘라서 앞부분만 send_size로 보내는거거든요....그래서 받은 만큼 그대로 보내는 것이 아니라 받은 데이터중 뒤 부분 아이피를 읽어서 그 아이피로 클리아언트를 생성해서 생성한 클라이언트에게 아이피부분을 자른 데이터를 넘겨야 합니다.
A에서는 send로 계속해서 데이터를 같은 형태의 데이터로 넘기면 B서버에서는 값을 읽어서 뒤부분의 아이피로 다시 클라이언트를 생성해서 또다른 특정아이피에게 데이터를넘기는 거라서...................
recv() 부분이 잘 못 되어 있습니다.
recv() 부부을 보면 무조건 MAXLINE 만큼 읽도록 되어 있는데, 실제로
데이타를 처리하는 부분을 보면 8바이트 헤더에서 데이타 크기를 읽어
오게 되어 있습니다.
TCP는 Stream 방식이기 때문에 보내는 쪽에서 MAXLINE 만큼씩 보내지
않고, 가변길이로 보낸다면 다음 메시지를 가져오게 되어 데이타가 어긋날
수 있습니다.
따라서, 처음에 8바이트를 읽은 후, 데이타크기를 판단해서 나머지 크기만큼
읽도록 수정해야 할 것 같습니다.
A쪽에서는 항상 1024로 보내고 B에서는 데이터를 1024만큼읽는 그런
A쪽에서는 항상 1024로 보내고 B에서는 데이터를 1024만큼읽는 그런 방식을 사용했습니다. 문제는 받은 데이터중 뒤부분의 아이피를 읽고나서 새로운 클라이언트를 생성해 그 클라이언트가 받은 아이피값을 뺀 나머지 데이터를 (데이터로 받은 마지막 부분의 아이피 서버)다른 서버로 넘기는 겁니다. ....
고정 길이로 보낸다고 해도 문제가 있습니다.
말씀 드렸듯이 TCP는 스트림 방식이기 때문에, 송신측에서는 고정 크기로 보내도 수신측에서는 가변길이로 수신될 수 있습니다.
예를 들어, 사용자가 1024 바이트씩 보낸다고 해도, OS 에서 데이타를 버퍼링 하고 있다가 일정 시간 후에 더이상 입력이 없으면, 한번에 보낼 수 있는 최대값을 내부적으로 1500으로 규정해 두었다면, 1500 바이트씩 보낼 수도 있습니다.
그런 경우, 수신측에서는 첫번째는 1024바이트를 수신할 수 있지만, 두번째는
476 바이트만 수신하게 되므로, 수신측에서는 원하는 크기만큼 받을 때 까지 반복해서 데이타를 수신하도록 해야 합니다.
그리고, 헤더에서 데이타의 크기를 읽어서 send() 함수에서 사용하고 있는데, 보내는 측과 받는 측의 바이트 오더가 같은지 확인하여 변환이 필요할 수도 있습니다.
만일, 고정크기 데이타를 보내길 원하신 다면, 그리고 수신측의 처리를 간결하게 구현하고 싶으시다면, UDP로 구현해 보시는 것도 괜찮을 것이라 생각합니다.
답변감사합니다. 그리고 다음 인용한 부분은 어떤식으로 해야할?
구조적으로 ..
스트림 방식의 프로그램을 할 때에는 항상 가변길이라는 것을 염두에 두시고..
select() 함수에서 깨어 났을때, 다음의 코드처럼 한나의 메시지만 처리해서는 안됩니다.
while() 루프를 돌면서 수신된 모든 메시지를 처리한 후 select() 함수를 호출 하도록 수정해야 합니다. 이 때, recv()에서 block 되지 않도록 MSG_PEEK 옵션을 주어서 다음 메시지가 있는지 확인한뒤, 데이타를 꺼내오면 됩니다.
그리고, 위의 예제에서는 client 모듈에서 매번 새로운 connection을 맺도록 되어있는데, 처리속도가 상당히 늦어질 수 있으므로 일정한 시간 이상 connection을 유지한 후 해제하도록 하는 로직도 필요할 것 같습니다.
데이터는 같지만 접속해야할 아이피가 달라지기 때문에..
데이터는 같지만 접속해야할 아이피가 달라지기 때문에 항상 새로운 소켓이 필요합니다.
A->B->(C,D,E,F,G.....)A:클라이언트 B:서버+클라이언트 CDEF는 서버로 구성됨.
C,D,E,F,G 각각의 ip는 A가 가지고 있기때문에 A가 데이터랑 접속할 모든 아이피를 B서버에 보내면 B서버는 CDEF...에 접속할 새로운 클랑이언트 소켓을 만들어야 합니다...
그래서 매번 새로운 connection이 필요하지요..
여러개의 IP에 대한 connection 을 유지해야 합니다.
만일 트래픽이 얼마 되지않는 다면 위의 코드와 같이 구현해도 문제 없습니다.
그러나, 트래픽이 많은 상황이라면 여러개의 셔션이라도 유지할 수 있는게 좋을 것이라고 생각 합니다.
댓글 달기