C语言中的多会话聊天服务器
我正在尝试用C语言编写一个多会话聊天服务器。我从一个终端托管服务器,并从其他终端向其发送telnet。 在VMWare player上使用ubuntu 13.04 发生的情况是:C语言中的多会话聊天服务器,c,network-programming,C,Network Programming,我正在尝试用C语言编写一个多会话聊天服务器。我从一个终端托管服务器,并从其他终端向其发送telnet。 在VMWare player上使用ubuntu 13.04 发生的情况是: 我将循环从3递增到fdmax,以使用sd(侦听器)接受新的连接,newsd表示新的套接字描述符 当我在一个窗口中打印“hi”时,它会在所有窗口中打印,包括我键入的窗口。此外,许多随机垃圾不断出现。 我只希望我键入的内容出现(如何清除垃圾>),并且出现在除我键入的窗口之外的所有窗口中 #include<sys/so
我将循环从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;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
void*get\u in\u addr(结构sockaddr*sa)
{
如果(sa->sa_族==AF_INET)
{
return&(((struct sockaddr_in*)sa)->sin_addr);
}
返回((结构sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc,字符**argv)
{
//ptr用于遍历,serv用于struct addinfo的链表,getaddrinfo函数的提示
结构addrinfo*ptr,提示,*serv;
int max_cli,dat,x,i;
结构sockaddr\u存储cli\u addr;
袜子尺寸;
char cli_ip[INET_ADDRSTRLEN];
char inc[256];//一个命令行包含80个字符
memset(inc,0,strlen(inc));
国际sd,新闻D;
fd_集主控器;
fd_设置读取_fds;
字符值[256];
FD_零和主设备;
FD_零(&read_fds);
//argv[1]-服务器ip argv[2]-服务器端口argv[3]-最大客户端数
int-fdmax;
int opt=1;
/*如果(argc!=4)
{
printf(“请重新输入数据。数据不足\n”);
出口(1);
}
if(atoi(argv[2])ai_next)
{
无效*地址;
如果(ptr->ai_系列==AF_INET)
{
*ipv4中的struct sockaddr_=(struct sockaddr_*)ptr->ai_addr;
地址=&(ipv4->sin_addr);
}
inet_ntop(ptr->ai_系列、地址、值、大小值);
//printf(“%s\n”,值);
//与其中一个IP地址建立连接
sd=插座(ptr->ai_系列,ptr->ai_插座类型,ptr->ai_协议);
如果(sd<0)
继续;
setsockopt(sd、SOL_插槽、SO_REUSEADDR和opt、opt大小);
如果(绑定(sd,ptr->ai_addr,ptr->ai_addrlen)<0)
{
关闭(sd);
继续;
}
break;//表示找到并绑定了一个工作套接字
}//结束
如果(ptr==NULL)
{
fprintf(stderr,“绑定失败\n”);
出口(2);
}
freeaddrinfo(serv);
如果(听(sd,15)=-1)
{
printf(“侦听时出错\n”);
出口(3);
}
/*套接字已找到、绑定并正在侦听活动连接*/
FD_集(sd和master);
fdmax=sd;//最新的活动套接字描述符
而(1)
{
read_fds=master;//复制主列表,使原始列表不会损坏
如果(选择(fdmax+1,&read_-fds,NULL,NULL,NULL)=-1)
{
perror(“选择失败。\n”);
出口(4);
}
对于(i=3;i为假,现有一个->真
{
//printf(“开始读取描述符!\n”);
如果(i==sd)//主连接存在,则接受新的文件描述符
{//printf(“读取第一个连接!\n”);
地址大小=cli地址的大小;
newsd=accept(sd,(struct sockaddr*)和cli\u addr,以及addr\u size);
printf(“已接受的新连接套接字%d\n”,newsd);
FD_集(新闻和主文件);
如果(newsd==-1)
{
佩罗(“接受”);
}
如果(新闻>fdmax)
{
fdmax=newsd;
}
printf(“%d%d\n”,newsd,fdmax);
继续;
}
否则,如果(i!=sd)//现有连接,则接受数据
{
如果(dat=recv(i,&inc,sizeof inc,0)首先,在发送接收到的数据时,使用sizeof
运算符,它给出了数组的总大小。发送的字节数应该与接收到的字节数相同,通常会更少。使用recv
的返回值可以知道实际接收到的字节数。一般来说,C数组没有动态大小,您必须保留你自己追踪一下
然后关于垃圾,您可能打印缓冲区内容而不终止'\0'
字符。因此,可以添加该字符(确保缓冲区中有1字节的额外空间!)在打印或使用其他字符串函数之前,或者使用一个打印函数,该函数在缺少终止nul时接受字符串的最大大小。作为开始,您的发送必须使用dat作为长度,而不是sizeof(inc)没有看到来源-不太可能。@viraptor我很抱歉:|刚刚打印出来it@goldilocks,谢谢!疏忽。你有什么问题,你想让消息显示在所有的连接中,还是只显示在不发送消息的连接中?什么是“随机垃圾”?我不能向上投票,但这对我来说很好。谢谢。顺便说一句,你不喜欢我吗“\0”?修正了斜杠。是的,升级需要一点声誉,不用担心。