首先,我想做的是一个多客户端的聊天服务。我读过数千篇与它相关的文章,但大多数都是用线程实现的,没有什么能帮到我,我需要用叉子来做。
我的服务器支持多个客户端的连接。每次客户端请求连接时,服务器都会执行以下操作:
叉子执行以下操作:
就目前而言,只有3种选择:
这么说的话,问题是我根本不能注意到客户端已经准备好了一条信息。执行的流程是:
我知道这对任何人来说都是漫长的,但是如果你能的话,我会很高兴得到一些帮助,请。
下面是客户端和服务器脚本。
注释: server.c与-lptrhead和-lrt一起编译,用于绑定共享内存库。
注意:正如您所看到的,服务器从get_tcp_listen函数中正确地获得了套接字,无需担心这一点。
我应该如何处理这个问题?谢谢!
client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "socketlib.h" // FOR BINDINGS, GET SOCKET, AND STUFF
#define SERVER_PORT "50000"
#define CLIENT_PORT "50001"
#define MAXDATASIZE 256
#define MAXTIMESIZE 30
#define NICKSIZE 25
#define MAXCLIENT 10
#define MAXMSG 1024
typedef struct{
char nick[NICKSIZE]; // NICK
char ip[NI_MAXHOST]; // IP
char port[NI_MAXSERV]; // PORT
char connTime[MAXTIMESIZE]; // TIMESTAMP
int connected; // STATE
pid_t pidConn; // PROCESS PID
char *msg; // MSG BUFFER
}connData;
int main (int argc, char **argv) {
// GET SOCKET
int sockfd;
if ((sockfd = get_tcp_connect(argv[1], SERVER_PORT, CLIENT_PORT)) == -1) {
fprintf(stderr, "Error client: client_connect\n");
exit(1);}
///////////////////////////////////////////////////////////////////////
time_t ltime;
ltime = time(NULL);
char timeStamp[MAXTIMESIZE];
strcpy(timeStamp,ctime(<ime));
printf("\n%s\n", timeStamp);
// GET MY IP : PORT
char ip_num[NI_MAXHOST];
char port_num[NI_MAXSERV];
get_socket_addr(sockfd, ip_num, port_num);
get_peer_addr(sockfd, ip_num, port_num);
///////////////////////////////////////////////////////////////////////
// WELLCOME MSG FROM SERVER
char *well = (char*) malloc(MAXDATASIZE); int numbytes;
if ((numbytes = recv(sockfd, well, MAXDATASIZE-1, 0)) == -1) {
fprintf(stderr, "Error client: recv WELLCOME\n");
exit(1);
}
well[numbytes] = '\0'; printf("%s\n", well); free(well);
//////////////////////////////////////////////////////////////////////
// SEND NICK TO SERVER
char nick[NICKSIZE];
printf("\nEnter your NickName (25 chars): "); scanf("%s",nick);
if(send(sockfd, nick, NICKSIZE, 0) == -1){
fprintf(stderr,"Error client: send NICK\n");
exit(1);
}
///////////////////////////////////////////////////////////////////////
// GET CONNECTED USERS LIST FROM SERVER
int cantClients = 0; // FIRST: QUANTITY OF USERS
if (recv(sockfd, &cantClients, sizeof(int), 0) == -1) {
fprintf(stderr, "Error client: recv CANT CLIENTs\n");
exit(1);
}
connData *tmpCl = (connData *) malloc(sizeof(connData)*MAXCLIENT);
if (recv(sockfd, tmpCl, sizeof(connData)*MAXCLIENT, 0) == -1) {
fprintf(stderr, "Error client: recv ARRAY CLIENTS\n");
exit(1);
}
printf("\n****\tConnected Users\t****\n");
int i;
for(i = 0; i < cantClients; i++){
if(tmpCl[i].connected == 1){
printf("\nNick: %s\n", tmpCl[i].nick);
printf("IP: %s\n", tmpCl[i].ip);
printf("PORT: %s\n", tmpCl[i].port);
printf("Time: %s", tmpCl[i].connTime);
printf("Connected: %d\n", tmpCl[i].connected);
printf("PID: %d\n", tmpCl[i].pidConn);
printf("**********************************\n");
}
} free(tmpCl);
///////////////////////////////////////////////////////////////////////
// THE CLIENT PROCESS WAITS UNTIL THE USER TYPES A COMMAND
char *comm = (char*)malloc(MAXDATASIZE);
printf("\nEnter one option: ");
printf("\n\t-> clientlist TO SEE THE LIST OF CONNECTED CLIENTS\n");
printf("\t-> sendchat TO SEND A MESSAGE\n");
printf("\t-> quit_ TO QUIT CHAT\n>> ");
scanf("%s",comm);
int exitvar = 0;
while(exitvar == 0){
// PARA TRAER DATOS DEL SERVIDOR, ENVIO EL COMANDO, Y ME QUEDO ESPERANDO
if(send(sockfd, comm, MAXDATASIZE-1, 0) == -1){
fprintf(stderr,"Error client: send\n");
exit(1);
}
if(strcmp(comm,"clientlist") == 0){
// GET CONNECTED USERS LIST FROM SERVER
connData *tmpCl = (connData *) malloc(sizeof(connData)*MAXCLIENT);
if (recv(sockfd, tmpCl, sizeof(connData)*MAXCLIENT, 0) == -1) {
fprintf(stderr, "Error client: recv ARRAY CLIENT\n");
exit(1);
}
printf("\n****\tConnected Users\t****\n"); int i;
cantClients = (unsigned) sizeof(*tmpCl) / (unsigned) sizeof(connData);
for(i = 0; i < MAXCLIENT; i++){
if(tmpCl[i].connected == 1){
printf("\nNick: %s\n", tmpCl[i].nick);
printf("IP: %s\n", tmpCl[i].ip);
printf("PORT: %s\n", tmpCl[i].port);
printf("Time: %s", tmpCl[i].connTime);
printf("Connected: %d\n", tmpCl[i].connected);
printf("PID: %d\n", tmpCl[i].pidConn);
printf("**********************************\n");
}
} free(tmpCl);
}else if(strcmp(comm,"sendchat") == 0){
printf("To whom you want to talk?... ");
char *chatNick = (char *) malloc(NICKSIZE);
fgets(chatNick, NICKSIZE, stdin);
fgets(chatNick, NICKSIZE, stdin);
if((strlen(chatNick)>0) && (chatNick[strlen(chatNick)-1] == '\n') ){
chatNick[strlen(chatNick)-1] = '\0';
}
if(send(sockfd, chatNick, NICKSIZE, 0) == -1){
fprintf(stderr, "Error client: send CHAT NICK\n");
}
printf("Type your message...\n");
char *chat_msg = (char *) malloc(MAXMSG);
fgets(chat_msg,MAXMSG,stdin) ;
if((strlen(chat_msg)>0) && (chat_msg[strlen(chat_msg)-1] == '\n') ){
chat_msg[strlen(chat_msg)-1] = '\0';
}
if(send(sockfd, chat_msg, MAXMSG, 0) == -1){
fprintf(stderr, "Error client: send CHAT\n");
}
free(chatNick);
free(chat_msg);
}else{
char *buf = (char*) malloc(MAXDATASIZE); int numbytes;
if ((numbytes = recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
fprintf(stderr, "Error client: recv\n");
exit(1);
}
buf[numbytes] = '\0'; printf("-> %s\n", buf);
free(buf);
}
if(strcmp(comm, "quit_") != 0){
free(comm); comm = (char*)malloc(MAXDATASIZE);
printf("\nWhats next?... "); scanf("%s",comm);
}else{
close(sockfd);
exitvar = 1;
}
}
return 0;
}server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include "socketlib.h"
#include <semaphore.h>
#define SERVER_PORT "50000"
#define BACKLOG 10
#define MAXDATASIZE 256
#define NICKSIZE 25
#define MAXCLIENT 10
#define MAXTIMESIZE 30
#define MAXMSG 1024
// ESTRUCTURA QUE MANEJARA LA LISTA DE CLIENTES
typedef struct{
char nick[NICKSIZE]; // NICK
char ip[NI_MAXHOST]; // IP
char port[NI_MAXSERV]; // PORT
char connTime[MAXTIMESIZE]; // TIMESTAMP
int connected; // STATE
pid_t pidConn; // PROCESS PID
char *msg; // MSG BUFFER
}connData;
// NOT ZOMBIE PROCESSES
void sigchld_handler(int s) {
while (waitpid(-1, NULL, WNOHANG) > 0);
}
connData *client;
int *id;
int main (int argc, char **argv) {
// THE ARRAY OF CLIENTS IS SHARED BETWEEN THE PROCESSES
int smid = shm_open("shm1", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
ftruncate(smid, sizeof(connData)*MAXCLIENT);
// JUST FOR MAXCLIENT 10 CLIENTS AT THE MOMMENT
client = mmap(NULL, sizeof(connData)*MAXCLIENT, PROT_READ | PROT_WRITE, MAP_SHARED, smid, 0);
sem_t *sem; sem = sem_open("sem1", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, 1);
// THE ARRAY INDEX IS ALSO SHARED
int smid2 = shm_open("shm2", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
ftruncate(smid2, sizeof(int));
id = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, smid2, 0);
sem_t *sem2; sem2 = sem_open("sem2", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, 1);
sem_wait(sem2);
*id = 0;
sem_post(sem2);
// CONN CONFIG
struct sigaction sa;
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
fprintf(stderr, "Error server: sigaction\n");
exit(1);
}
int sockfd; // LISTENER
if ((sockfd = get_tcp_listen(SERVER_PORT, BACKLOG)) == -1) {
fprintf(stderr, "Error get_tcp_listen\n");
exit(1);
}
printf("server: waiting for connections...\n");
char ip_num[NI_MAXHOST];
char port_num[NI_MAXSERV];
get_socket_addr(sockfd, ip_num, port_num);
//////////////////////////////////////////////////////////////////
while (1) {
// BLOCKS UNTIL SOMEONE REQUEST CONN
int new_fd;
if ((new_fd = accept(sockfd, NULL, NULL)) == -1) {
fprintf(stderr, "Error server: accept\n");
continue;}
////////////////////////////////////////////////////////
// IP:PORT OF JUST CONNECTED USER
get_socket_addr(new_fd, ip_num, port_num);
get_peer_addr(new_fd, ip_num, port_num);
printf("server: got connection from: %s, %s\n", ip_num, port_num);
////////////////////////////////////////////////////////
// TIMESTAMP OF USER CONN
time_t ltime; ltime = time(NULL);
char timeStamp[MAXTIMESIZE]; strcpy(timeStamp,ctime(<ime));
////////////////////////////////////////////////////////////////////////
// WELLCOME MESSAGE SENT TO THE CLIENT
char *well = (char*) malloc(MAXDATASIZE);
if (send(new_fd, "Wellcome to the Chat Service!!\n", MAXDATASIZE-1, 0) == -1) {
fprintf(stderr, "Error sending WELLCOME\n");
} free(well);
///////////////////////////////////////////////////////////////
// SAVES IN THE ARRAY OF CLIENTS, THE DATA OF THE CLIENT THAT JUST CONNECTED
int idTmp1;
sem_wait(sem2);
idTmp1 = *id;
sem_post(sem2);
if(sem_wait(sem) == 0){
strcpy(client[idTmp1].ip, ip_num); // IP
strcpy(client[idTmp1].port, port_num); // PORT
strcpy(client[idTmp1].connTime, timeStamp); // TIMESTAMP
client[idTmp1].connected = 1;
}else{
fprintf(stderr, "Error SEM_WAIT\n");
}
sem_post(sem);
sem_wait(sem2); (*id)++; sem_post(sem2);
//////////////////////////////////////////////////////////////
// FORKS A PROCESS TO DEAL WITH THE JUST CONNECTED USER
if (fork() == 0) {
close(sockfd); // CLOSES THE FATHERS SOCKET
int numbytes = 0;
// SAVES THE NICK IN THE ARRAY
char userNick[NICKSIZE];
if(( numbytes = recv(new_fd, userNick, NICKSIZE, 0)) == -1){
fprintf(stderr,"Error rcv\n");
} userNick[numbytes-1] = '\0';
int idTmp2;
sem_wait(sem2);
pid_t pidAct = getpid(); // PID OF THE NEW CREATED FORK
idTmp2 = *id; // ID OF THE USER
idTmp2--;
strcpy(client[idTmp2].nick,userNick);
client[idTmp2].pidConn = pidAct;
idTmp2 = *id;
sem_post(sem2);
//////////////////////////////////////////////////////////////
// SENDS THE LIST OF CONNECTED CLIENTES
if (send(new_fd, id, sizeof(int), 0) == -1) {
fprintf(stderr, "Error send ID\n");
}
if (send(new_fd, client, sizeof(connData)*MAXCLIENT, 0) == -1) { // SEND THE WHOLE LIST
fprintf(stderr, "Error send LIST\n");
}
//////////////////////////////////////////////////////////////
// THE FORK WAITS SOME COMMAND OF THE USER
char *comm = (char*)malloc(MAXDATASIZE);
if( (numbytes = recv(new_fd, comm, MAXDATASIZE-1, 0)) == -1){
fprintf(stderr,"Error rcv COMMAND\n");
}
comm[numbytes] = '\0';
// THE FORK ENTERS IN A LOOP WAITING COMMANDS
int wait = 0;
while(wait == 0){
if(strcmp(comm,"clientlist") == 0){
if (send(new_fd, client, sizeof(connData)*MAXCLIENT, 0) == -1) {
fprintf(stderr, "Error send CLIENT LIST\n");
}
}else if(strcmp(comm,"sendchat") == 0){
char *chatNick = (char *) malloc(NICKSIZE); // WAIT FOR THE CLIENT TO TALK TO
if( (numbytes = recv(new_fd,chatNick, NICKSIZE, 0)) == -1){
fprintf(stderr,"Error server rcv CHAT NICK\n");
} chatNick[numbytes-1] = '\0';
char *chatmsg = (char *)malloc(MAXMSG); // WAIT FOR MSG
if((numbytes = recv(new_fd, chatmsg, MAXMSG, 0)) == -1){
fprintf(stderr,"Error server rcv CHAT\n");
} chatmsg[numbytes-1] = '\0';
int client_id;
sem_wait(sem2);
for(client_id = 0; client_id < *id; client_id++){
if(strcmp(client[client_id].nick, chatNick) == 0){
if(client[client_id].msg != NULL){
free(client[client_id].msg);
}
client[client_id].msg = (char * )malloc(MAXMSG); // COPY THE MESSAGE TO THE DESIRED USER
strcpy(client[client_id].msg, chatmsg);
printf("\nTHE MESSAGE TO: %s IS %s\n", client[client_id].nick, client[client_id].msg);
}
}
sem_post(sem2);
/*
HERE I HAVE THE NICK, SAY, 'client1' OF THE CLIENT TO WHICH I WANT TO TALK.
THE MSG NOW ITS IN HIS MSG BUFFER LIKE ABOVE.
HOW CAN I NOTICE THE FORKED PROCESS HANDLING THE CONNECTION of 'client1'
TO READ THE MESSAGE ?
*/
free(chatmsg);
free(chatNick);
}else if(strcmp(comm,"quit_") == 0){
if (send(new_fd, "Byee!!", MAXDATASIZE-1, 0) == -1) {
fprintf(stderr, "Error send EXIT\n");
}
wait = 1; // FOR EXIT AND CLOSE THE SOCKET
}else{
if (send(new_fd, "Invalid option!", MAXDATASIZE-1, 0) == -1) {
fprintf(stderr, "Error send INVALID\n");
}
}
if(wait == 0){
// WHEN THE FORKED PROCESS HAS FULFILL THE USERS REQUEST, IT JUST WAITS FOR OTHER REQUEST
free(comm); comm = (char*)malloc(MAXDATASIZE);
if((numbytes = recv(new_fd, comm, MAXDATASIZE-1, 0)) == -1){
fprintf(stderr,"Error rcv REQUEST\n");
} comm[numbytes] = '\0';
}
}
if(munmap(client,sizeof(connData)*MAXCLIENT) != 0){ printf("ERROR FREEING MEM\n");}
sem_unlink("sem1"); shm_unlink("shm1");
printf("Connection ended with %d \n", new_fd);
close(new_fd); exit(0);
}
printf("Keep waiting connections.....\n");
close(new_fd); // SOCKET DEL ACCEPT, DEL CLIENTE QUE SE HABIA CONECTADO
//////////////////////////////////////////////////////////////////////////////
}
if(munmap(client,sizeof(connData)*MAXCLIENT) != 0){ printf("ERROR FREEING MEM\n");}
sem_unlink("sem1");
shm_unlink("shm1");
return 0;
}发布于 2015-07-16 05:16:23
“你快完成了,”埃米利亚诺说,“这也是我曾经被卡住的地方。”
好吧,你有一些选项可以告诉其他进程关于消息。
1.您总是寻找自己的消息缓冲区(这很糟糕,占用了大量的CPU,也是个坏主意)。
Process C1总是在共享内存中查找C1、它自己的、缓冲区和检查是否有新消息,并发送给客户端。
2.使用信号(优于1)
客户端C2向C1发送消息。
将C2消息存储在共享内存上的C1缓冲区中。(如果我正确理解您的共享内存结构)
进程C2向C1发送一个信号,通知我已将消息放置在缓冲区中。(在这种情况下,您需要知道哪个pid处理哪个客户端)
从进程获得信号后,C1检查它的缓冲区并发送给它的客户端。
编辑:,似乎您在信号方面遇到了问题。这里有一个简单的片段,它显示信号发送/捕获。
recvsig.c
static void handler(int sig, siginfo_t *si, void *data)
{
printf("%s = %d\n", "Got a signal, signal number is ", sig);
//you can also code here what you want, after getting a signal
}
void init_signal()
{
struct sigaction act;
act.sa_sigaction = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
sigaction(SIGRTMIN + 1, &act, NULL);
}
void main(int argc, char **argv)
{
printf("%s %d\n", "PID", getpid());
init_signal();
while(1)
{
pause();
{
printf("%s\n", "Received a signal");
//code here anything after you got signal
}
}
return;
}sendsig.c
void main(int argc, char **argv)
{
int pid = atoi(argv[1]);
while(1)
{
sleep(5);
{
kill(pid, SIGRTMIN+1);
printf("%s %d\n", "Sent a signal to ", pid);
}
}
return;
}在您的程序中,在每个分叉子进程之后调用init_signal()。确保您管理所有分叉进程+连接客户端的pid列表。
并使用kill()发出正确的pid信号。
发布于 2015-07-16 05:05:55
首先,我要指出,叉()和共享内存不是创建聊天服务器的最好或最简单的方法;如果您有此选项,我建议以不同的方式(例如,通过单个进程中的多个线程,甚至一个线程并选择())。
然而,假设您需要使用这种方法(例如,因为它是在课堂作业中规定的).您缺少的是IPC通知机制,即进程C2注意到process C1有一些数据供C2处理的方法(正如您所说的)。
有几种方法可以实现通知.快速和肮脏的方法(这可能足以完成类分配)只是让每个进程轮询共享内存区域;例如,让每个进程每100毫秒检查它在共享内存区域中的部分,并查看自上次以来是否有任何变化。在这样的场景中,C1可能会将一些新数据写入共享内存区域,然后在完成写入时在共享内存区域中增加一个整数值。下次C2醒来检查整数时,它会注意到整数的值与上一次检查时的值不同,并以此作为它要处理的共享内存区域中有新数据的线索。(附带注意:在使用共享内存时,您应该以某种方式序列化对共享内存区域的访问,否则您可能会遇到竞争条件,例如,C2开始读取内存的同时,C1仍在编写它。其症状是系统在99.999%的时间内正确工作,但当时间“恰到好处”时,偶尔会做一些奇怪的/错误的事情)
但是,如果您想要一种不那么麻烦的通知方法(也就是说,它不消耗CPU 24/7周期,并且不会在每次通过服务器的行程中造成不必要的100 to延迟),那么您将需要选择一个通知机制。一种机制是使用(可以说是不正确的) 杀()系统调用向C2的进程发送一个UNIX;然后,C2需要安装一个信号处理程序,使它在接收到该类型的信号时做正确的事情。
但是,如果您想避免Unix信号(我试图避免它们,因为它们非常过时和丑陋),您将需要一种更温和的机制,在接收到IPC通知或接收到I/O数据时,可以唤醒C2进程,无论哪种情况先发生.简单的阻塞I/O调用机制是不够的,因为它只允许进程等待某一件事情,而不是两者都等待。获得这两个套接字的一个好方法是使用select()或poll()同时监视两个套接字:一个套接字是到客户端的TCP连接,另一个套接字是进程专门为接收IPC通知而设置的UDP套接字。每个分叉进程在特定端口上设置一个UDP套接字,当C1想唤醒C2时,C1通过发送()-ing将UDP数据包发送到C2的UDP端口。( UDP数据包的内容并不重要,因为它的唯一目的是使C2从select()返回,而且由于UDP套接字选择了准备读,所以C2知道从UDP套接字读取数据包,丢弃包,然后检查共享内存区域以获取新数据。
(当然,一旦您完成了所有这些工作,您可能会发现C1很容易将C2的数据简单地包含在UDP数据包本身中,这样C2就不需要使用潜在的共享内存区域,但这取决于您)
https://stackoverflow.com/questions/31445121
复制相似问题