我正在尝试用C编写多会话聊天服务器。我从一个终端托管服务器,并从其他终端telnet到它。 在VMWare播放器上使用ubuntu 13.04。
这是怎么回事:
我将循环从3开始递增到fdmax,使用sd(监听器)接受新连接,newsd表示新的套接字描述符。
当我在一个窗口中打印'hi'时,它会在所有窗口中打印,包括我输入的窗口。此外,还会出现大量随机垃圾。 我只想要我输入的内容(我如何摆脱垃圾>),以及所有窗口,除了我输入的那个!
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<netdb.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<sys/select.h>
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET)
{
return &(((struct sockaddr_in*) sa)->sin_addr);
}
return &(((struct sockaddr_in6*) sa)->sin6_addr);
}
int main(int argc, char **argv)
{
//ptr used for traversal, serv used for the linked list of struct addinfos , hints for the getaddrinfo function
struct addrinfo *ptr, hints, *serv;
int max_cli, dat, x, i;
struct sockaddr_storage cli_addr;
socklen_t addr_size;
char cli_ip[INET_ADDRSTRLEN];
char inc[256]; //one command line is 80 characters
memset(inc, 0, strlen(inc));
int sd, newsd;
fd_set master;
fd_set read_fds;
char value[256];
FD_ZERO(&master);
FD_ZERO(&read_fds);
//argv[1]-server ip argv[2]-server port argv[3]-maximum client number
int fdmax;
int opt = 1;
/*if(argc!=4)
{
printf("Please re-enter data. Data insufficient\n");
exit(1);
}
if(atoi(argv[2])<1025)
{
printf("Reserved port. Please try again\n");
exit(1);
}*/
max_cli = atoi(argv[3]);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
/* Verify the inputs and generate linked list of possible IPs to use*/
if (sd = getaddrinfo(argv[1], argv[2], &hints, &serv))
{
fprintf(stderr, "Error calling getaddrinfo %s\n", gai_strerror(sd));
exit(1);
}
for (ptr = serv; ptr != NULL ; ptr = ptr->ai_next)
{
void *addr;
if (ptr->ai_family == AF_INET)
{
struct sockaddr_in *ipv4 = (struct sockaddr_in *) ptr->ai_addr;
addr = &(ipv4->sin_addr);
}
inet_ntop(ptr->ai_family, addr, value, sizeof value);
//printf("%s\n",value);
//Form connection with one of the IP addresses
sd = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
if (sd < 0)
continue;
setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);
if (bind(sd, ptr->ai_addr, ptr->ai_addrlen) < 0)
{
close(sd);
continue;
}
break; //Indicates one working socket found and bound
} //end for
if (ptr == NULL )
{
fprintf(stderr, "Bind failed\n");
exit(2);
}
freeaddrinfo(serv);
if (listen(sd, 15) == -1)
{
printf("Error occurred while listening\n");
exit(3);
}
/* Socket found, bound and now listening for active connections*/
FD_SET(sd, &master);
fdmax = sd; //Latest active socket descriptor
while (1)
{
read_fds = master; //Copy the master list so that the original list doesn't get damaged
if (select(fdmax + 1, &read_fds, NULL, NULL, NULL ) == -1)
{
perror("Select failed.\n");
exit(4);
}
for (i = 3; i <= fdmax; i++)
{
//printf("i");
//printf("entered for loop\n");
if (FD_ISSET(i,&read_fds)) //new connection->false, existing one->true
{
// printf("Started reading descriptors!\n");
if (i == sd) //primary connection,exists, accept new file descriptor
{ //printf("Read first connection!\n");
addr_size = sizeof cli_addr;
newsd = accept(sd, (struct sockaddr *) &cli_addr, &addr_size);
printf("Accepted new connection socket %d\n", newsd);
FD_SET(newsd, &master);
if (newsd == -1)
{
perror("accept");
}
if (newsd > fdmax)
{
fdmax = newsd;
}
printf("%d %d\n", newsd, fdmax);
continue;
}
else if (i != sd) //existing connection, so accept data
{
if (dat = recv(i, &inc, sizeof inc, 0) <= 0)
{
if (dat == 0)
{
printf(" Socket %d has quit the chatroom", i);
}
if (dat < 0)
{
perror("Error on Receive");
}
// char *s=&inc;
//printf("%d\n %s",dat);
close(i);
FD_CLR(i, &master);
}
//Nothing wrong with the input from client i. Broadcast!
else
{
for (x = 3; x <= fdmax; x++)
{
if (FD_ISSET(x,&master))
{
if (x != sd)
{
//send(x,&inc,sizeof inc,0);
if (send(x, &inc, sizeof inc, 0) < 0)
{
perror("Send");
}
}
}
}
}
}
}
/*else// new connection
{ break;
printf("SERVERBOT: new connection from %s on socket %d\n",inet_ntop(cli_addr.ss_family,get_in_addr((struct sockaddr*)&cli_addr),cli_ip, INET6_ADDRSTRLEN),newsd);
}////change this to 'username' has joined the room*/
}
}
return 0;
}
答案 0 :(得分:1)
首先,当您发送收到的数据时,使用sizeof
运算符,它会为您提供数组的总大小。您应该发送与收到的字节数一样多的字节,这通常会更少。使用recv
的返回值来了解实际接收的字节数。一般来说,C数组没有动态大小,你必须自己跟踪它。
然后关于垃圾,你可能打印缓冲区内容而不终止'\0'
字符。因此,在打印或使用其他字符串函数之前添加(确保缓冲区中有1个字节的额外空间!),或者使用接受最大字符串大小的打印函数,以防终止nul。
答案 1 :(得分:0)
首先你的发送必须使用dat作为长度而不是sizeof(inc)