假设在编写此代码后我有一个连接的套接字..
if ((sd = accept(socket_d, (struct sockaddr *)&client_addr, &alen)) < 0)
{
perror("accept failed\n");
exit(1);
}
如何在服务器端知道客户端已退出。
我的整个程序实际上做了以下事情..
如果你想看到整个代码......在这整个代码中。我还在努力解决另外一个问题,即每当我用Ctrl + C杀死一个客户端时,我的服务器突然终止..如果有人能提出问题是什么,那将是很好的
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <pthread.h>
/*CONSTANTS*/
#define DEFAULT_PORT 10000
#define LISTEN_QUEUE_LIMIT 6
#define TOTAL_CLIENTS 10
#define CHAR_BUFFER 256
/*GLOBAL VARIABLE*/
int current_client = 0;
int connected_clients[TOTAL_CLIENTS];
extern int errno;
void *client_handler(void * socket_d);
int main(int argc, char *argv[])
{
struct sockaddr_in server_addr;/* structure to hold server's address*/
int socket_d; /* listening socket descriptor */
int port; /* protocol port number */
int option_value; /* needed for setsockopt */
pthread_t tid[TOTAL_CLIENTS];
port = (argc > 1)?atoi(argv[1]):DEFAULT_PORT;
/* Socket Server address structure */
memset((char *)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; /* set family to Internet */
server_addr.sin_addr.s_addr = INADDR_ANY; /* set the local IP address */
server_addr.sin_port = htons((u_short)port); /* Set port */
/* Create socket */
if ( (socket_d = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
fprintf(stderr, "socket creation failed\n");
exit(1);
}
/* Make listening socket's port reusable */
if (setsockopt(socket_d, SOL_SOCKET, SO_REUSEADDR, (char *)&option_value,
sizeof(option_value)) < 0) {
fprintf(stderr, "setsockopt failure\n");
exit(1);
}
/* Bind a local address to the socket */
if (bind(socket_d, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
fprintf(stderr, "bind failed\n");
exit(1);
}
/* Specify size of request queue */
if (listen(socket_d, LISTEN_QUEUE_LIMIT) < 0) {
fprintf(stderr, "listen failed\n");
exit(1);
}
memset(connected_clients,0,sizeof(int)*TOTAL_CLIENTS);
for (;;)
{
struct sockaddr_in client_addr; /* structure to hold client's address*/
int alen = sizeof(client_addr); /* length of address */
int sd; /* connected socket descriptor */
if ((sd = accept(socket_d, (struct sockaddr *)&client_addr, &alen)) < 0)
{
perror("accept failed\n");
exit(1);
}
else printf("\n I got a connection from (%s , %d)\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
if (pthread_create(&tid[current_client],NULL,(void *)client_handler,(void *)sd) != 0)
{
perror("pthread_create error");
continue;
}
connected_clients[current_client]=sd;
current_client++; /*Incrementing Client number*/
}
return 0;
}
void *client_handler(void *connected_socket)
{
int sd;
sd = (int)connected_socket;
for ( ; ; )
{
ssize_t n;
char buffer[CHAR_BUFFER];
for ( ; ; )
{
if (n = read(sd, buffer, sizeof(char)*CHAR_BUFFER) == -1)
{
perror("Error reading from client");
pthread_exit(1);
}
int i=0;
for (i=0;i<current_client;i++)
{
if (write(connected_clients[i],buffer,sizeof(char)*CHAR_BUFFER) == -1)
perror("Error sending messages to a client while multicasting");
}
}
}
}
我的客户方是这个(在回答我的问题时可能无关紧要)
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
void error(char *msg)
{
perror(msg);
exit(0);
}
void *listen_for_message(void * fd)
{
int sockfd = (int)fd;
int n;
char buffer[256];
bzero(buffer,256);
printf("YOUR MESSAGE: ");
fflush(stdout);
while (1)
{
n = read(sockfd,buffer,256);
if (n < 0)
error("ERROR reading from socket");
if (n == 0) pthread_exit(1);
printf("\nMESSAGE BROADCAST: %sYOUR MESSAGE: ",buffer);
fflush(stdout);
}
}
int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
pthread_t read_message;
char buffer[256];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
bzero(buffer,256);
if (pthread_create(&read_message,NULL,(void *)listen_for_message,(void *)sockfd) !=0 )
{
perror("error creating thread");
}
while (1)
{
fgets(buffer,255,stdin);
n = write(sockfd,buffer,256);
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,256);
}
return 0;
}
答案 0 :(得分:14)
接受连接后,套接字上的recv()
将在特殊情况下返回0或-1。
成功完成后,recv() 应返回消息的长度 以字节为单位如果没有可用的消息 收到和同行有 执行有序关机,recv() 应返回0.否则,-1应为 返回并且errno设置为表示 错误。
因此,如果您的客户端正常退出,您将在recv()
处获得0。如果连接以某种方式丢失,您也可能得到-1并检查适当的errno会告诉您连接是否丢失了其他一些错误。请在recv(3) man page了解详情。
编辑:
我看到你正在使用read()
。仍然适用与recv()
相同的规则。
尝试write()
给您的客户时,您的服务器也可能失败。如果您的客户端断开连接write()
将返回-1,并且errno可能会设置为EPIPE
。此外,如果您不阻止/忽略此信号,SIGPIPE
信号将发送给您进程并杀死他。而且你没有看到,这就是当客户端按下Ctrl-C时服务器终止的原因。 Ctrl-C终止客户端,因此关闭客户端套接字并使服务器的write()
失败。
有关其他可能出错的详细解释,请参阅mark4o的答案。
答案 1 :(得分:10)
如果客户端程序退出,则客户端上的操作系统将关闭其套接字的末尾。当你收到TCP RST时(例如,因为你试图在客户端关闭后发送数据),当你调用recv()
时它将返回0,或者带有errno ECONNRESET
的-1。如果整个客户端计算机出现故障,或者网络断开连接,那么在这种情况下,如果服务器没有尝试发送任何内容,您可能不会收到任何内容;如果这对于检测很重要,您可以定期发送一些数据,或者使用SO_KEEPALIVE
设置setsockopt()
套接字选项,以强制它在长时间(小时)不活动后发送没有数据的数据包。如果没有收到确认,recv()
将返回-1,错误号为ETIMEDOUT
,如果有更多具体信息,则返回另一个错误。
此外,如果您尝试在已断开连接的套接字上发送数据,则默认情况下SIGPIPE信号将终止您的程序。通过将SIGPIPE信号操作设置为SIG_IGN(忽略),或者在支持它的系统(Linux)上使用send()
和MSG_NOSIGNAL
标志,可以避免这种情况。