我正在制作一个tcp多客户端服务器聊天室。我的TCP多客户端服务器和客户端的代码随附。我无法为我的程序添加另一个功能,即客户端删除。显示空闲客户端可以从服务器列表中删除。例如,如果客户端空闲30秒,则应由服务器删除。
请帮助我。这是服务器程序
/* to compile me in Linux, type: gcc -o server t1.c -lpthread */
/* t1.c - code for server program that uses TCP/IP */
/****SERVER CODE****/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h> //for sockets
#include <sys/types.h>
#include <string.h> //for string operations
#include <netinet/in.h> //Internet Protocol family sockaddr_in defined here
#include <pthread.h> // for the cosy POSIX threads
#include <arpa/inet.h> // for inet_ntoa() function
#include <unistd.h> //NULL constant defined here
#include <signal.h> //for ctrl+c signal
#define BACKLOG 100 // connections in the queue
#define MAXDATALEN 256 //max size of messages to be sent
#define PORT 2012 //default port number
/*Note: The port argument is optional. If no port is specified,
* the server uses the default given by PORT.*/
struct Node /*structure to handle all clients*/
{
int port;
char username[10];
struct Node *next;
};
typedef struct Node *ptrtonode;
typedef ptrtonode head;
typedef ptrtonode addr;
void sendtoall(char *,int new_fd); /*send chat msgs to all connected clients*/
void Quitall( ); /*send msg to all if server shuts down*/
head MakeEmpty( head h ); /*clearing list*/
void Delete( int port, head h ); /*delete client values on client exit*/
void Insert(int port,char*,head h,addr a);/*inserting new client */
void DeleteList( head h ); /*clearing list*/
void Display( const head h ); /*list all clients connected*/
void *Quitproc( ); /*signal handler*/
void *server(void * arg); /*server instance for every connected client*/
void zzz();
char username[10]; /*size of username*/
int sf2;
head h; /*variable of type struct head*/
char buffer[MAXDATALEN];
/******main starts ***********/
int main(int argc, char *argv[]) {
int sockfd,new_fd; /*variables for socket*/
int portnum; /*variable for port numb if provided*/
struct sockaddr_in server_addr; /*structure to hold server's address */
struct sockaddr_in client_addr; /*structure to hold client's address */
int cli_size,z; /*length of address */
pthread_t thr; /*variable to hold thread ID */
int yes=1;
addr a; /*variable of type struct addr*/
printf("\n\t*-*-*-*SERVER STARTED*-*-*-*\n");
/*=optional or default port argument=*/
if( argc == 2 )
portnum = atoi(argv[1]);
else
portnum = PORT; //if port number not given as argument then using default port
printf("PORT NO.:\t%d\n",portnum);
h = MakeEmpty( NULL ); //frees the list
/*=set info of server =*/
server_addr.sin_family=AF_INET; /* set family to Internet */
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* set IP address */
server_addr.sin_port=htons(portnum);
printf("IP ADDRESS:\t%s\n",inet_ntoa(server_addr.sin_addr));
/*=creating socket=*/
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1){
printf("server- socket() error"); // debugging
exit(1);
}else
printf("socket\t\tcreated.\n");
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,sizeof(int)) == -1) {
printf("setsockopt error"); // debugging
exit(1);
}else printf("reusing\t\tport\n");
/*=binding socket=*/
if(bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))==-1){
printf("binding failed\n"); // debugging
exit(1);}
else
printf("binding\t\tsuccess.\n\n");
printf("\t\tPRESS CTRL+z TO VIEW ONLINE CLIENTS\n\n");
/*=socket on listening mode=*/
listen(sockfd, BACKLOG);
printf("waiting for clients......\n");
if (signal(SIGINT,(void *)Quitproc)==0) //signal handler
if(signal(SIGTSTP, zzz)==0) //signal handler
while(1){
cli_size=sizeof(struct sockaddr_in); //cli_size necessary as an argument for pthread_create
new_fd = accept(sockfd, (struct sockaddr *)&client_addr,&cli_size); //accepting connection from client
a =h ;
/*=sign in with name=*/
bzero(username,10);
if(recv(new_fd,username,sizeof(username),0)>0);
username[strlen(username)-1]=':';
printf("\t%d->%s JOINED chatroom\n",new_fd,username);
sprintf(buffer,"%s IS ONLINE\n",username);
Insert( new_fd,username, h, a ); //inserting newly accepted client socked fd in list
a = a->next;
/*=notify all clients about newly joining clients=*/
a = h ;
do{
a = a->next;
sf2 = a->port;
if(sf2!=new_fd)
send(sf2,buffer ,sizeof(buffer),0);
} while( a->next != NULL );
printf("server got connection from %s & %d\n\n",inet_ntoa(client_addr.sin_addr),new_fd); // debugging
struct Node args; //struct to pass multiple arguments to server function
args.port=new_fd;
strcpy(args.username,username);
pthread_create(&thr,NULL,server,(void*)&args); //creating thread for every client connected
pthread_detach(thr);
} /*while end*/
DeleteList(h); //deleting all clients when server closes
close(sockfd);
}/********main ends***********/
/*******ALL FUNCTIONS DEFINED BELOW*********/
/* ==========Server function for every connected Client =========*/
void *server(void * arguments){
struct Node *args=arguments;
char buffer[MAXDATALEN],ubuf[50],uname[10]; /* buffer for string the server sends */
char *strp;
char *msg = (char *) malloc(MAXDATALEN);
int ts_fd,x,y;
int sfd,msglen;
ts_fd = args->port; /*socket variable passed as arg*/
strcpy(uname,args->username);
addr a;
/*=sending list of clients online=*/
a =h ;
do{
a = a->next;
sprintf( ubuf," %s is online\n",a->username );
send(ts_fd,ubuf,strlen(ubuf),0);
} while( a->next != NULL );
/*=start chatting=*/
while(1){
bzero(buffer,256);
y=recv(ts_fd,buffer,MAXDATALEN,0);
if (y==0)
goto jmp;
/*=if a client quits=*/
if ( strncmp( buffer, "quit", 4) == 0 ){
jmp: printf("%d ->%s left chat deleting from list\n",ts_fd,uname);
sprintf(buffer,"%s has left the chat\n",uname);
addr a = h ;
do{
a = a->next;
sfd = a->port;
if(sfd == ts_fd)
Delete( sfd, h );
if(sfd != ts_fd)
send(sfd,buffer,MAXDATALEN,0);
}while ( a->next != NULL );
Display( h );
close(ts_fd);
free(msg);
break;
}
/*=sending message to all clients =*/
printf("%s %s\n",uname,buffer);
strcpy(msg,uname);
x=strlen(msg);
strp = msg;
strp+= x;
strcat(strp,buffer);
msglen=strlen(msg);
addr a = h ;
do{
a = a->next;
sfd = a->port;
if(sfd != ts_fd)
send(sfd,msg,msglen,0);
} while( a->next != NULL );
Display( h );
bzero(msg,MAXDATALEN);
}//end while
return 0;
}// end server
/*=====empties and deletes the list======*/
head MakeEmpty( head h )
{
if( h != NULL )
DeleteList( h );
h = malloc( sizeof( struct Node ) );
if( h == NULL )
printf( "Out of memory!" );
h->next = NULL;
return h;
}
/*======delete list=======*/
void DeleteList( head h )
{
addr a, Tmp;
a = h->next;
h->next = NULL;
while( a != NULL )
{
Tmp = a->next;
free( a );
a = Tmp;
}
}
/*===============inserting new clients to list==========*/
void Insert( int port,char *username, head h, addr a )
{
addr TmpCell;
TmpCell = malloc( sizeof( struct Node ) );
if( TmpCell == NULL )
printf( "Out of space!!!" );
TmpCell->port = port;
strcpy(TmpCell->username,username);
TmpCell->next = a->next;
a->next = TmpCell;
}
/*========displaying all clients in list==================*/
void Display( const head h )
{
addr a =h ;
if( h->next == NULL )
printf( "NO ONLINE CLIENTS\n" );
else
{
do
{
a = a->next;
printf( "%d->%s \t", a->port,a->username );
} while( a->next != NULL );
printf( "\n" );
}
}
/*===========client deleted from list if client quits================*/
void Delete( int port, head h ){
addr a, TmpCell;
a = h;
while( a->next != NULL && a->next->port != port )
a = a->next;
if( a->next != NULL ){
TmpCell = a->next;
a->next = TmpCell->next;
free( TmpCell );
}
}
/*======handling signals==========*/
void *Quitproc(){
printf("\n\nSERVER SHUTDOWN\n");
Quitall( );
exit(0);
}
/*===============notifying server shutdown===========*/
void Quitall(){
int sfd;
addr a = h ;
int i=0;
if( h->next == NULL ) {
printf( "......BYE.....\nno clients \n\n" );
exit(0);
} else {
do{
i++;
a = a->next;
sfd = a->port;
send(sfd,"server down",13,0);
} while( a->next != NULL );
printf("%d clients closed\n\n",i);
}
}
void zzz(){
printf("\rDISPLAYING ONLINE CLIENTS\n\n");
Display(h);
}
/*=====================================================================================================*/
Here is the client program
/* To compile me in Linux type: gcc -o client t2.c -lpthread*/
/* t2.c - code for client that uses TCP */
/***CLIENT CODE*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> //for string operations
#include <unistd.h> //NULL constant defined here
#include <sys/types.h>
#include <sys/socket.h> //for sockets
#include <netinet/in.h> //Internet Protocol family sockaddr_in defined here
#include <pthread.h> // for the cosy POSIX threads
#include <signal.h> //for ctrl+c signal
#define MYPORT 2012 /* default port number */
#define MAXDATALEN 256
int sockfd;
int n,x; /*variables for socket*/
struct sockaddr_in serv_addr; /* structure to hold server's address */
char buffer[MAXDATALEN];
char buf[10];
void *quitproc();
void* chat_write(int);
void* chat_read(int);
void *zzz();
/***************main starts************/
int main(int argc, char *argv[]){
pthread_t thr1,thr2; /* variable to hold thread ID */
if( argc != 2 ){
printf("help:u need to put server ip\n");
exit(0);
}
/*=============socket creating==============*/
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
printf ("client socket error\n");
else
printf("socket\t\tcreated\n");
/*===============set info===================*/
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(MYPORT);
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
/*=========for username=====================*/
bzero(buf,10);
printf("\nENTER YOUR NAME::");
fgets(buf,10,stdin);
__fpurge(stdin);
buf[strlen(buf)-1]=':';
/*=============client connect to server============*/
if(connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr))==-1)
{
printf("client connect error\n");
exit(0);
}
else
printf("%s connected to server\n",buf);
printf("\rYOU JOINED AS- %s",buffer-1);
send(sockfd,buf,strlen(buf),0);
pthread_create(&thr2,NULL,(void *)chat_write,(void *)sockfd); //thread creation for writing
pthread_create(&thr1,NULL,(void *)chat_read,(void *)sockfd); //thread creation for reading
pthread_join(thr2,NULL);
pthread_join(thr1,NULL);
return 0;
} /*main ends*/
/*======================================================================*/
/*======reading continously from socket=============*/
void* chat_read(int sockfd)
{
if (signal(SIGINT,(void *)quitproc)==0)
if(signal(SIGTSTP, (void *)zzz)==0)
while(1)
{
n=recv(sockfd,buffer,MAXDATALEN-1,0);
if(n==0){
printf("\nDUE TO SOME UNEXPECTED REASONS SERVER HAS BEEN SHUTDOWN\n\n");
exit(0);
}
if(n>0){
printf("\n%s ",buffer);
bzero(buffer,MAXDATALEN);
}
}//while ends
}
/*======writing continously to socket=============*/
void* chat_write(int sockfd)
{
while(1)
{
printf("%s",buf);
fgets(buffer,MAXDATALEN-1,stdin);
if(strlen(buffer)-1>sizeof(buffer)){
printf("buffer size full\t enter within %d characters\n",sizeof(buffer));
bzero(buffer,MAXDATALEN);
__fpurge(stdin);
}
n=send(sockfd,buffer,strlen(buffer),0);
if(strncmp(buffer,"quit",4)==0)
exit(0);
bzero(buffer,MAXDATALEN);
}//while ends
}
/*======handling signals==========*/
void *quitproc(){ //handling ctrl+d
printf("\rPLEASE TYPE 'quit' TO EXIT\n");
}
void *zzz(){ //handling ctrl+z
printf("\rPLEASE TYPE 'quit' TO EXIT\n");
}
/*=================================================================*/
答案 0 :(得分:0)
您可以尝试在服务器端调用setsockopt()时使用套接字选项SO_RCVTIMEO和SO_SNDTIMEO。因此,如果TCP堆栈在指定的超时时间内处于非活动状态,则TCP堆栈将终止此套接字上的任何连接。
可能/应该是这样的情况,即对server()函数中的recv()的阻塞调用(即服务器的客户端线程)将因错误而解除阻塞,但我记得确切的行为可能是OS-依赖。 否则,您需要循环检查任何打开的fd是否可以从另一个监视线程“到达”,例如,通过发送一个小状态消息。 (那个线程的循环时间当然必须高于配置的超时,否则你可以通过循环发送消息来保持连接处于活动状态。)
一旦检测到连接已关闭,您可以在服务器的客户端线程终止之前执行任何必要的错误/清除处理。
如果您还想从客户端启动连接关闭,请使用shutdown()函数,该函数还应告知服务器上相应的(阻塞)recv()调用以解除阻塞并显示错误。
答案 1 :(得分:0)
我看到你正在使用阻塞套接字。
在线程函数server
中,您在while(1)
在客户端套接字上执行recv
。如果recv
返回零或客户端发送“退出”消息,则关闭套接字ts_fd
并通知其他客户端。
您可以使用SO_RCVTIMEO
功能(使用setsockopt
设置套接字选项)来暂停recv
次呼叫。如果recv
没有数据可以在指定时间内提供给应用程序,recv
将返回EGAGAIN或EWOULDBLOCK。您可以将此视为客户端处于空闲状态并关闭连接。
struct timeval tv;
tv.tv_sec = 30; /* 30 Secs Timeout */
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval));