Я работаю над программой многопользовательского чата Wi-Fi. Буду признателен за помощь в следующем:
- Из-за природы маршрутизаторов dhcp, неясно, какой адрес сервер будет получать каждый день. Клиент просматривает список всех возможных хостов, начиная с 192.168.1.1. Когда он сталкивается с несерверным устройством, он ожидает в течение произвольного промежутка времени, вплоть до минуты. Как я могу заставить его перестать ждать через секунду? Я пытался остановить его с помощью alarm () перед тем, как прочитать руководство по тревоге, так что это была пустая трата времени.
- Функция сервера
printUsers()
- единственная функция, выход которой иногда доставляется, иногда нет. Я бы хотел, чтобы кто-то объяснил, почему. - Я хотел бы получить отзыв о качестве кода. Под этим я подразумеваю такие вещи, как читаемость и удобство сопровождения кода, дизайн программы, стиль, обработка ошибок и т. Д. c.
Код сервера
#include <arpa/inet.h>
#include <ctype.h>
#include <ifaddrs.h>
#include <netdb.h> //adds symbolic network constants
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define BYE 0
#define ERROR -1
#define HOST_SIZE 4
#define MAX_CONN 11
#define MSG_LEN 255
#define NAME_LEN 30
#define SYS_MSG_LEN 18
int debug; //command-line argument
int mySock; //sustains value from assignment till the end
typedef struct pollfd pollfd;
void addName2msg(char name[], char msg[], char msgOut[]);
void addUser(char name[], int i, char msg[], pollfd *fds, int fdsInd);
void findMyIP(char myIP[]);
void handleArgs(int argc, char *argv[]);
void handleLeaver(int i, char names[][NAME_LEN], pollfd *fds, int*fdI);
void handleNewClient(pollfd *fds, int *fdsInd);
void printUsers(char names[][NAME_LEN], int i, int fd);
void sendAll(int i, char msgOut[], pollfd *fds, int fdsInd);
void sendErr(char *msg);
void sendMsg(int fd, char msg[], int len);
void startHost(char myIP[]);
void workLoop()
{
static pollfd fds[MAX_CONN];
int status, fdsInd = 0;
static char names[MAX_CONN+1][NAME_LEN];
char msg[MSG_LEN], msgOut[MSG_LEN+NAME_LEN];
fds[fdsInd].fd = mySock;
fds[fdsInd++].events = POLLIN;
while(1) {
if (debug)
puts("Waiting for an event to happen...");
if (poll(fds, fdsInd, -1) == -1)
sendErr("poll() failed");
if (debug)
puts("An event happened");
for (int i = 0; i < fdsInd; i++) {
if ((fds[i].fd == mySock) && (fds[i].revents & POLLIN)) {
handleNewClient(fds, &fdsInd);
} else if (fds[i].revents & POLLIN) { //handle message
status = recv(fds[i].fd, msg, MSG_LEN, 0);
if (debug)
printf("Recieved msg from %s, socket %d\n",
names[i], fds[i].fd);
if ((status == BYE) || (status == ERROR)) {
printf("Status is %d\n", status == BYE? BYE: ERROR);
handleLeaver(i, names, fds, &fdsInd);
continue;
}
if (*names[i] == '\0') { //no name? Save msg as name
addUser(names[i], i, msg, fds, fdsInd);
printUsers(names, i, fds[i].fd);
continue;
}
addName2msg(names[i], msg, msgOut);
sendAll(i, msgOut, fds, fdsInd);
}
}
}
}
int main(int argc, char *argv[])
{
char myIP[INET_ADDRSTRLEN];
handleArgs(argc, argv);
findMyIP(myIP);
startHost(myIP);
workLoop();
return 0;
}
//
void addName2msg(char name[], char msg[], char msgOut[]) {
int len;
strncpy(msgOut, name, NAME_LEN );
len = strlen(msgOut);
msgOut[len] = ':';
msgOut[len+1] = ' ';
strcat(msgOut, msg);
}
//
void addUser(char name[], int i, char msg[], pollfd *fds, int fdsInd)
{
int j;
char msgWelc[SYS_MSG_LEN + NAME_LEN] = "Welcome to chat, ";
char msgJoin[SYS_MSG_LEN] = " joined the chat\n";
for (j = 0; j < NAME_LEN-3; j++) {
if (msg[j] == '\n' || isspace(msg[j]))
break;
name[j] = msg[j];
}
name[j] = '\n';
name[j+1] = '\0';
strcat(msgWelc, name);
sendMsg(fds[i].fd, msgWelc, SYS_MSG_LEN+NAME_LEN);
name[j] = '\0';
strncpy(msgWelc, name, NAME_LEN);
strcat(msgWelc, msgJoin);
sendAll(i, msgWelc, fds, fdsInd);
}
//
void findMyIP(char myIP[])
{
struct ifaddrs *addrs;
struct sockaddr_in *pAddr;
getifaddrs(&addrs);
for (; addrs != NULL; addrs = addrs->ifa_next) {
pAddr = (struct sockaddr_in *) addrs->ifa_addr;
if (strstr(inet_ntoa(pAddr->sin_addr), "192.168.1") != NULL)
strncpy(myIP, inet_ntoa(pAddr->sin_addr), INET_ADDRSTRLEN);
}
freeifaddrs(addrs);
if (debug)
printf("Local IP is %s\n", myIP);
}
//
void handleArgs(int argc, char *argv[])
{
if (argc > 2) {
puts("error: one arg max");
exit(1);
}
if ((argc == 2) && (strcmp("-d", argv[argc-1]) != 0)) {
puts("error: Call with -d for debugging");
exit(1);
} else if (argc == 2)
debug = 1;
}
//
void handleLeaver(
int i, char names[][NAME_LEN], pollfd *fds, int *fdsInd)
{
int lastElem = *fdsInd - 1;
char trash[MSG_LEN];
char msgLeft[SYS_MSG_LEN] = " left the chat\n";
//notify all users
strncpy(trash, names[i], strlen(names[i])+1);
strcat(trash, msgLeft);
sendAll(i, trash, fds, *fdsInd);
if (debug) {
printf("Had: fds0:%d fds1:%d fds2:%d; fdsInd: %d\n",
fds[0].fd, fds[1].fd, fds[2].fd, *fdsInd);
printf("\tNames1: %s, Names2: %s\n", names[1], names[2]);
}
recv(fds[i].fd, trash, MSG_LEN, 0); //receive garbage
if (lastElem == i) {
fds[i].fd = 0;
fds[i].events = 0;
(*fdsInd)--;
names[i][0] = 0;
} else {
fds[i].fd = fds[lastElem].fd;
fds[i].events = fds[lastElem].events;
strncpy(names[i], names[lastElem], NAME_LEN);
fds[lastElem].fd = 0;
fds[lastElem].events = 0;
(*fdsInd)--;
names[lastElem][0] = 0;
}
if (debug) {
printf("Removed user\n");
printf("Have: fds0:%d fds1:%d fds2:%d; fdsInd: %d\n",
fds[0].fd, fds[1].fd, fds[2].fd, *fdsInd);
printf("\tNames1: %s, Names2: %s\n", names[1], names[2]);
}
}
//
void handleNewClient(pollfd *fds, int *fdsInd)
{
struct sockaddr clientInfo;
socklen_t addrSz;
int newFD;
char nameReq[SYS_MSG_LEN] = "Enter your name: ";
addrSz = sizeof(clientInfo);
newFD = accept(mySock, &clientInfo, &addrSz);
if (*fdsInd > MAX_CONN) { //kill extra users
close(newFD);
return;
}
fds[*fdsInd].fd = newFD;
fds[(*fdsInd)++].events = POLLIN;
if (debug)
puts("Accepted a client");
sendMsg(newFD, nameReq, SYS_MSG_LEN);
}
//
void printUsers(char names[][NAME_LEN], int i, int fd)
{
int len;
char msgStart[SYS_MSG_LEN] = "Online Users:\n";
sendMsg(fd, msgStart, SYS_MSG_LEN);
if (debug)
printf("Online users printed for %s, %d\n", names[i], fd);
for (int j = 0; j < MAX_CONN; j++) {
if ((names[j][0] != 0) && (strcmp(names[j], names[i]) != 0)) {
len = strlen(names[j]);
names[j][len] = '\n';
names[j][len+1] = '\0';
sendMsg(fd, names[j], NAME_LEN);
names[j][len] = '\0';
}
}
}
//
void sendAll(int i, char msgOut[], pollfd *fds, int fdsInd)
{
pollfd *author = &fds[i];
for (int j = 0; j < fdsInd; j++) {
if (fds[j].fd == author->fd)
continue;
if (fds[j].fd == mySock)
continue;
sendMsg(fds[j].fd, msgOut, MSG_LEN);
}
}
//
void sendErr(char *msg)
{
perror(msg);
exit(1);
}
//
void sendMsg(int fd, char msg[], int len)
{
int sent = 0;
if ((len == MSG_LEN) && (msg[len-1]!= '\0'))
msg[len-1] = '\0';
if ((len == MSG_LEN) && (msg[len-2]!= '\n'))
msg[len-2] = '\n';
while (sent < len) {
sent = send(fd, msg, len, 0);
if (sent == -1)
sendErr("Sending message failed");
else if (sent == len)
return;
}
}
//
void startHost(char myIP[])
{
struct addrinfo hints, *servinfo;
int yes = 1;
char port[] = "39071";
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; // any IPv*
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(myIP, port, &hints, &servinfo) != 0)
sendErr("Getaddinfo() failed");
if ((mySock = socket(servinfo->ai_family,
servinfo->ai_socktype, servinfo->ai_protocol)) == -1)
sendErr("Socket() failed");
if (-1 == setsockopt(
mySock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)))
sendErr("setsockopt() failed");
if (bind(mySock, servinfo->ai_addr, servinfo->ai_addrlen) == -1)
sendErr("Bind() failed");
if (listen(mySock, MAX_CONN) == -1)
sendErr("listen() failed");
if (debug) {
printf("Host socket is %d\n", mySock);
printf("Listening to port %s...\n", port);
}
}
Код клиента
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <netdb.h> //adds symbolic network constants
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define FDS_NUM 2
#define HOST_SIZE 4
#define MSG_LEN 255
#define MAX_CONN 11
enum { ERROR = -1, OFF = 0, SERV_DOWN = 0 , FAIL = 0, SUCCESS = 1 };
int debug; //command-line argument
int mySock; //received in prepareNet() and kept till the end
//int catchSignal(int sig, void (*handler)(int));
void chat();
//void doNoth(int sig) { sig = sig; return; }
void exit_closedConn();
int findHost(struct addrinfo *servinfo[]);
void handleArgs(int argc, char *argv[]);
void prepareNet(struct addrinfo *servinfo[]);
void sendErr(char *msg);
int main(int argc, char *argv[])
{
struct addrinfo *servinfo[MAX_CONN];
handleArgs(argc, argv);
prepareNet(servinfo);
//if (catchSignal(SIGALRM, doNoth) == -1)
// sendErr("Sigaction failed");
if (!findHost(servinfo))
sendErr("No hosts found");
chat();
return 0;
}
//
int catchSignal(int sig, void (*handler)(int))
{
struct sigaction new;
new.sa_handler = handler;
sigemptyset(&new.sa_mask);
new.sa_flags = 0;
return sigaction(sig, &new, NULL);
}
//
void chat()
{
struct pollfd pds[FDS_NUM];
int status;
char msgIn[MSG_LEN];
char usrMsg[MSG_LEN];
pds[0].fd = 0; //stdin
pds[1].fd = mySock;
pds[0].events = pds[1].events = POLLIN;
while (1) {
if (poll(pds, FDS_NUM, -1) == -1)
sendErr("Poll failed");
if (pds[0].revents & POLLIN) {
fgets(usrMsg, MSG_LEN, stdin);
if (send(pds[1].fd, usrMsg, MSG_LEN, 0) == -1)
sendErr("Sent failed");
} else if (pds[1].revents & POLLIN) {
if ((status = recv(pds[1].fd, msgIn, MSG_LEN, 0)) == ERROR)
sendErr("recv failed");
else if (status == SERV_DOWN)
exit_closedConn();
printf("%s", msgIn);
fflush(stdout);
}
}
}
//
void handleArgs(int argc, char *argv[])
{
if (argc > 2)
sendErr("1 argument max");
if ((argc == 2) && (strcmp("-d", argv[argc-1]) != 0))
sendErr("input -d for debugging");
else if (argc == 2)
debug = 1;
}
//
void exit_closedConn()
{
puts("Server closed connection. Exiting...");
exit(1);
}
//
int findHost(struct addrinfo *servinfo[])
{
puts("Searching for a server...");
for (int i = 1; i < MAX_CONN; i++) {
if (debug)
printf("Trying for 192.168.1.%d...\n", i);
//alarm(1);
if (connect(mySock, servinfo[i]->ai_addr,
servinfo[i]->ai_addrlen) != -1) {
alarm(OFF);
printf("Connected to 192.168.1.%d\n",i);
return SUCCESS;
}
}
return FAIL;
}
//
void prepareNet(struct addrinfo *servinfo[])
{
struct addrinfo hints[MAX_CONN];
char port[] = "39071";
for (int i = 0; i < MAX_CONN; i++) {
char host[HOST_SIZE];
char ip[INET_ADDRSTRLEN] = "192.168.1.";
memset(&hints[i], 0, sizeof(hints[i]));
hints[i].ai_family = AF_UNSPEC; // any IPv*
hints[i].ai_socktype = SOCK_STREAM;
sprintf(host, "%d", i);
host[3] = '\0';
strncat(ip, host, HOST_SIZE-1);
if (getaddrinfo(ip, port, &hints[i], &servinfo[i]) != 0)
sendErr("Getaddinfo() failed");
}
if ((mySock = socket(servinfo[0]->ai_family,
servinfo[0]->ai_socktype, servinfo[0]->ai_protocol)) == -1)
sendErr("Socket() failed");
if (debug)
printf("Trying port %s using socket %d\n", port, mySock);
}
//
void sendErr(char *msg)
{
perror(msg);
exit(1);
}
ОБНОВЛЕНИЕ:
- Следуя советам Сикса и Бруно, я смог решить первую проблему. Вот подробности:
- программа создает сокет и пытается подключиться
- после установки второго сигнала тревоги
- обработчик сигнала alarm () является фиктивной функцией
- если соединение не установлено, программа закрывает сокет
Вопрос в том, насколько хорош этот подход?
int findHost(struct addrinfo *servinfo[])
{
puts("Searching for a server...");
for (int i = 1; i < MAX_CONN; i++) {
if ((mySock = socket(servinfo[0]->ai_family,
servinfo[0]->ai_socktype, servinfo[0]->ai_protocol)) == -1)
sendErr("Socket() failed");
if (debug)
printf("Trying for 192.168.1.%d...\n", i);
alarm(1);
if (connect(mySock, servinfo[i]->ai_addr,
servinfo[i]->ai_addrlen) != -1) {
alarm(OFF);
printf("Connected to 192.168.1.%d\n",i);
//remove non-blocking
return SUCCESS;
}
//sleep(1);
close(mySock);
}
return FAIL;
}