Select()를 이용해 채팅 서버-클라이언트(Standalone방식?) 프로그래밍 중 질문드립니다.
글쓴이: theageha / 작성시간: 금, 2008/05/09 - 5:37오후
/*
Student ID : 20031943
Name : 박성진
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSOCK 100
#define MAXLINE 1000
int tcpSockets[MAXSOCK]; // List of sockets
int connCount=0; // connection count
void removeClient(int i);
int getmax(int k);
void error_handling(char *command);
int main(int argc, char **argv)
{
int tcpServ_sock; // a TCP server Socket
int udpServ_sock; // a UDP server Socket
int tcpRcv_sock;
int udpCnt_sock;
int tcpCnt_sock;
fd_set reads, temps;
int fd_max, fd, tcpReq;
char msg[MAXLINE];
char rcvmsg[MAXLINE];
char command[MAXLINE];
char udpReq_msg[MAXLINE];
char udpRcv_msg[MAXLINE];
char tcpReq_msg[MAXLINE];
char closemsg[MAXLINE];
char *comm, *reqhost, *udpPort, *tcpPort, *host;
struct sockaddr_in tcpServ_addr;
struct sockaddr_in udpServ_addr;
struct sockaddr_in tcpReq_addr;
struct sockaddr_in udpReq_addr;
int tcp_byte_read, udpReq_addr_size, udp_byte_read, tcpReq_addr_size, close_byte_read, rcv_byte_read;
int i,j,k, testn;
if(argc != 4){
printf("Usage : %s <tcpport> <udpport> <userid>\n", argv[0]);
exit(1);
}
printf("Student ID :20031943\n");
printf("Name : Park Sung-jin\n");
if((tcpServ_sock=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))<0)
error_handling("TCP server socket opening is failed!\n");
if((udpServ_sock=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP))<0)
error_handling("UDP server socket opening is failed!\n");
memset(&tcpServ_addr, 0, sizeof(tcpServ_addr));
tcpServ_addr.sin_family=AF_INET;
tcpServ_addr.sin_addr.s_addr=htonl(INADDR_ANY); // need modifying !!!!!!!!!!!
tcpServ_addr.sin_port = htons(atoi(argv[1]));
if(bind(tcpServ_sock, (struct sockaddr*)&tcpServ_addr, sizeof(tcpServ_addr))<0)
error_handling("TCP server binding is failed!\n");
if(listen(tcpServ_sock, MAXLINE)<0)
error_handling("listening is failed!\n");
FD_ZERO(&reads);
FD_SET(fileno(stdin), &reads);
FD_SET(tcpServ_sock, &reads);
FD_SET(udpServ_sock, &reads);
fd_max = udpServ_sock;
printf("%s>\n", argv[3]);
while(1){
int nfound;
temps = reads;
<span>fd_max=getmax(tcpServ_sock);</span>
if(nfound = select(fd_max+1, &temps, 0, 0, NULL)<0)
error_handling("Select is failed!\n");
for(i=0; i<connCount; i++) //check;
FD_SET(tcpSockets[i], &temps);
if(FD_ISSET(fileno(stdin), &temps)) {
FD_CLR(fileno(stdin), &temps);
fgets(command, MAXLINE, stdin);
comm=strtok(command, " ");
reqhost=strtok(NULL, " ");
udpPort=strtok(NULL, "\n");
if(strncmp(comm, "@c", 2)==0){
if((udpCnt_sock=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP))<0)
error_handling("UDP Client socket opening is failed!\n");
sprintf(udpReq_msg, "Connect %s", argv[1]); //creat udp message
sendto(udpCnt_sock, udpReq_msg, strlen(udpReq_msg), 0,(struct sockaddr*)&udpServ_sock, sizeof(udpServ_addr));
close(udpCnt_sock);
}/*
else if(strncmp(comm, "@q", 2)==0){
if((udpCnt_sock=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP))<0)
error_handling("UDP Client socket opening is failed!\n");
memset(&tcpServ_addr, 0, sizeof(tcpServ_addr));
tcpServ_addr.sin_family=AF_INET;
tcpServ_addr.sin_addr.s_addr=htonl(INADDR_ANY); // need modifying !!!!!!!!!!!
tcpServ_addr.sin_port = htons(atoi(argv[2]));
sprintf(udpReq_msg, "@q"); //creat udp message
sendto(udpCnt_sock, udpReq_msg, strlen(udpReq_msg), 0,(struct sockaddr*)&tcpServ_sock, sizeof(tcpServ_addr));
close(udpCnt_sock);
exit(1);
}
*/
printf("%s>", argv[3]);
}
else if(FD_ISSET(udpServ_sock, &temps)){
FD_CLR(udpServ_sock, &temps);
printf("udpServ check");
memset(&udpServ_addr, 0, sizeof(udpServ_addr));
udpServ_addr.sin_family=AF_INET;
udpServ_addr.sin_addr.s_addr=htonl(INADDR_ANY); // need modifying !!!!!!!!!!!
udpServ_addr.sin_port = htons(atoi(argv[2]));
if(bind(udpServ_sock, (struct sockaddr*)&udpServ_addr, sizeof(udpServ_addr))<0)
error_handling("UDP server binding is failed!\n");
// NEED TO IMPLEMENT
// Other user sent a connection request via UDP message
udpReq_addr_size=sizeof(udpReq_addr);
udp_byte_read=recvfrom(udpServ_sock, udpRcv_msg, MAXLINE, 0, (struct sockaddr*)&udpReq_addr, &udpReq_addr_size);
strtok(udpRcv_msg, " ");
host=strtok(NULL, " ");
tcpPort=strtok(NULL, "\n");
memset(&tcpServ_addr, 0, sizeof(tcpServ_addr));
tcpServ_addr.sin_family=AF_INET;
tcpServ_addr.sin_addr.s_addr=htonl(atoi(host)); // need modifying !!!!!!!!!!!
tcpServ_addr.sin_port = htons(atoi(tcpPort));
if((tcpCnt_sock=socket(PF_INET, SOCK_STREAM,0))<0)
error_handling("TCP client socket opening is failed!\n");
if(connect(tcpCnt_sock, (struct sockaddr*)&tcpServ_addr, sizeof(tcpServ_addr))<0)
error_handling("TCP connection is failed\n");
}
else if(FD_ISSET(tcpServ_sock, &temps)) {
FD_CLR(tcpServ_sock, &temps);
tcpReq_addr_size=sizeof(tcpReq_addr);
if(tcpRcv_sock=accept(tcpServ_sock, (struct sockaddr*)&tcpReq_addr, &tcpReq_addr_size)<0)
error_handling("Accept is failed\n");
tcpSockets[connCount]=tcpRcv_sock;
connCount++;
// NEED TO IMPLEMENT
// Other user requested a TCP connection
}
// for messages from other users
else if(FD_ISSET(tcpCnt_sock, &temps)){
FD_CLR(tcpCnt_sock, &temps);
if((rcv_byte_read = recv(tcpCnt_sock,rcvmsg,MAXLINE, 0 ))<0)
error_handling("recieve error\n");
rcvmsg[rcv_byte_read] = '\0';
printf("%s : %s\n", argv[3], rcvmsg);
}
<span>for (fd = 0; fd <= fd_max ; fd++) {</span> /* look for other sockets that have data available */
if (FD_ISSET(fd, &temps)) {
FD_CLR(fd, &temps);
// NEED TO IMPLEMENT
// read the message from the socket fd
if((tcp_byte_read = recv(tcpSockets[fd],msg,MAXLINE, 0 )) <= 0) {
removeClient(i);
continue;
}
if(strstr(msg,"@q") != NULL) {
sprintf(closemsg,"Connection Closed %d\n", fd);
for(k=0;k<connCount;k++);
send(tcpSockets[k], closemsg, strlen(closemsg), 0);
removeClient(i);
continue;
}
/* 모든 채팅 참가자에게 메시지 방송 */
msg[tcp_byte_read] = '\0';
for(j = 0; j < connCount; j++)
send(tcpSockets[j],msg,tcp_byte_read,0);
printf("%s\n",msg);
// write the message to other neighbors
// print the message on the screen
}
}
}//while End
close(tcpServ_sock);
close(udpServ_sock);
}//main End
int getmax(int k)
{
int max = k;
int r;
for(r = 0; r < connCount ; r++) /* 채팅 참가자 수만큼 소켓 배열 탐색 */
if(tcpSockets[r] > max)
max = tcpSockets[r];
return max;
}
void error_handling(char *command){
fputs(command, stderr);
fputc('\n', stderr);
exit(1);
}
void removeClient(int i) /* i번째 참가자 탈퇴 */
{
close(tcpSockets[i]); /* 해당 소켓 닫기 */
if(i != connCount-1)
tcpSockets[i] = tcpSockets[connCount -1]; /* i번째 참가자와 맨 마지말 참가장의 위치 switch*/
connCount --; /* 채팅 참가자 수 1명 줄이기 */
}여기저기서 짜집기하구, 급하게 만들어서 보시기 좀 불편하실 수도 있겠네요;;
1. 중간에 fd_max 저렇게 해도 되나요? max=udpServ_sock; 이후에 fd_max=getMax(tcpServ_sock) 하게 되면 값이 바뀌지 않나요?
2. main문 맨 아래 for 루프에서 왜 fd=0 부터인지 모르겠습니다. 파일디스크립터가 3번부터 부여되는 것으로 알고 있는데..
게다가 이미 3번tcpServer, 4번udpServer 로 부여가 되었을텐데, 그럼 5번부터 돌아야하는거 아닌가요?
그래서 5번-최초의 tcpClient 사용자가 되겠죠-에 출력할 내용이 있으면 출력!;;
3.실제로 컴파일 해보면 udp클라이언트에서 udp 서버로 요청하는 부분부터 안넘어가집니다.
토큰으로 명령어 다 짤라내서 요청 메세지를 만들어 내는데, 그 메세지가 udpServer쪽으로 넘어갔는지 확인할 방법이 없네요.ㅠ
제가 코딩을 잘못한 것일까요?ㅠ
Forums:


1. getmax 함수가
1. getmax 함수가 어떻게 되어 있는지 모르겠지만 현재 설정된 파일 디스크립트 보다 큰 값이
들어올 경우에 업데이트 해주는거 같으니 상관없을 거 같네요. 당연히 connection이 추가 되서
파일 디스크립트가 추가 되면 fd_max 도 증가 시켜줘야 겠지요..
2. fd 가 0번 부터 되는건 코드 보시면 아실수 있으실 텐데요.
보시면 0번도 세팅합니다. (0: input, 1: output, 2: error)
자세힌 모르겠으나 사용자가 입력하는 명령어에 대해서도 처리를 해줄려고 저리 했나 보네요.
3. udp는 connectionless 였던가 아무튼 tcp 랑 다르게 보내면 땡입니다. 수신측에서 잘 받았는지
안받았는지.. 신경 안쓰죠..
허접한 답변이지만 도움이 됬으면 하네요.. 그럼 수고하십시오.^^;;
앗 감사합니다.^^
설명해주신 부분은 이해가 되었습니다.
그래서 sendto가 리턴하는 것을 이용해서 메세지가 날라갔는지 확인해보니 -1, 즉 실패하더라구요;;
왜그런지 모르겠습니다.ㅠㅠ 혹시 알고 계시면 답변 부탁드립니다.
혹시 몰라서 주소 구조체 셋팅을 다시 했습니다. 그런데도 안돼네요..
if(strncmp(comm, "@c", 2)==0){ if((udpCnt_sock=socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP))<0) error_handling("UDP Client socket opening is failed!\n"); sprintf(udpReq_msg, "Connect %s", argv[1]); //creat udp message memset(&udpServ_addr, 0, sizeof(udpServ_addr)); udpServ_addr.sin_family=AF_INET; udpServ_addr.sin_addr.s_addr=htonl(atoi(reqhost)); udpServ_addr.sin_port = htons(atoi(udpPort)); if((testn=sendto(udpCnt_sock, udpReq_msg, strlen(udpReq_msg), 0,(struct sockaddr*)&udpServ_addr , sizeof(udpServ_addr)))<0); error_handling("UDP message sending error"); close(udpCnt_sock); }----------------------------------------------------------------------------
삼인행 필유아사언(三人行 必有我師焉) - 세 사람이 길을 가면 그 중에 반드시 스승이 있다.
----------------------------------------------------------------------------
삼인행 필유아사언(三人行 必有我師焉) - 세 사람이 길을 가면 그 중에 반드시 스승이 있다.
크아아악 누가 좀 도와주세요..제발..ㅠㅠ
진짜...이 부분 왜 안되는지도저히 모르겠어요..ㅠㅠ
윗쪽에서 이미 udpServ_addr에 대한 주소 구조체를 초기화 시켜줘서 그런걸까요?
혹시 몰라서 udpServ2_addr을 따로 만들고, 서버소켓도 하나 더 만들어서 테스트 했는데도 안돼요..
계속 sending error 메시지만 나오구.....udp는 서버안키고 그냥 전송해도 전송까지는 되어야 되는거 아닌가요?
도착하는걸 확인안한다고 했으니...전송까지라도 되야되는데..ㅠ
----------------------------------------------------------------------------
삼인행 필유아사언(三人行 必有我師焉) - 세 사람이 길을 가면 그 중에 반드시 스승이 있다.
----------------------------------------------------------------------------
삼인행 필유아사언(三人行 必有我師焉) - 세 사람이 길을 가면 그 중에 반드시 스승이 있다.
댓글 달기