我正在研究Wi-Fi多用户聊天程序。在以下方面,我将不胜感激。
printUsers()
是唯一其输出有时被传递而有时不被传递的函数。我希望有人解释原因。服务器代码
#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);
}
更新:
问题是这种方法有多好?
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;
}
[使用无阻塞插槽限制connect
的时间,并使用1秒的超时限制select
的时间,这可能不是正确的方法。
首先,您确定您的服务器将需要少于第二秒的时间来回答,并且因为您要继续使用新地址,因此无法使用以前的套接字,并且对我来说尚不清楚,可以安全地在关闭套接字之前关闭套接字连接尝试结束。
因此,我建议您尝试使用无阻塞套接字和select
无超时与多个地址并行连接。
在我的提案中,当连接完成后,我当然不会尝试与其他尚未尝试的地址进行连接,但我会等待正在进行的尝试完成。
客户端被修改为接收参数:
-d
冗长,也必须给出并行尝试的次数这里是完整程序,我只修改了与连接相关的部分,请注意,在chat
中您发送了固定大小的数组,因此可能包含未初始化的字节。
#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>
#include <fcntl.h>
#include <errno.h>
#define FDS_NUM 2
#define HOST_SIZE 4
#define MSG_LEN 255
#define PORT 39071
enum { ERROR = -1, OFF = 0, SERV_DOWN = 0 , FAIL = 0, SUCCESS = 1 };
int Debug; //command-line argument
int MySock; //the successfuly connected socket
//int catchSignal(int sig, void (*handler)(int));
void chat();
//void doNoth(int sig) { sig = sig; return; }
void exit_closedConn();
int findHost(int maxhost, int npar);
void handleArgs(int argc, char *argv[], int * maxhost, int * npar);
void sendErr(char *msg);
int main(int argc, char *argv[])
{
int maxhost = 10; //search 192.168.1.1 .. 192.168.1.<MaxIP>
int npar; //number of // connections
handleArgs(argc, argv, &maxhost, &npar);
//if (catchSignal(SIGALRM, doNoth) == -1)
// sendErr("Sigaction failed");
if (!findHost(maxhost, npar))
sendErr("No hosts found");
else
puts("host 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[], int * maxhost, int * npar)
{
if ((argc < 2) ||
(argc > 4) ||
(sscanf(argv[1], "%d", maxhost) != 1) ||
(*maxhost < 1) ||
((argc >= 3) && ((sscanf(argv[2], "%d", npar) != 1) || (*npar < 1))) ||
((Debug = (argc == 4)) && strcmp(argv[3], "-d"))) {
fprintf(stderr, "Usage: %s max_host [n_par [-d]] (default n_par is 10)\n", *argv);
exit(-1);
}
}
//
void exit_closedConn()
{
puts("Server closed connection. Exiting...");
exit(1);
}
//
int do_connect(int host, int * highersock, fd_set * fdset)
{
if (Debug)
printf("Trying for 192.168.1.%d...\n", host);
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (fcntl(sock, F_SETFL,O_NONBLOCK) != 0)
sendErr("fcntl nonblock");
struct sockaddr_in sin = { 0 };
sin.sin_addr.s_addr = htonl(0xC0a80100 + host); /* 192.168.1. */
sin.sin_port = htons(PORT);
sin.sin_family = AF_INET; /////AF_UNSPEC; // any IPv*
errno = 0;
int r = connect(sock, (struct sockaddr*) &sin, sizeof(sin));
if ((r < 0) && (errno != EINPROGRESS)) {
close(sock);
return 0;
}
if (sock > *highersock)
*highersock = sock;
FD_SET(sock, fdset);
return sock;
}
int findHost(int maxhost, int npar)
{
int socks[npar]; /* socks under connection or 0 */
int hosts[npar]; /* the next host to check */
int highersock = 0; /* for select */
fd_set fdset;
int i = 0, nsocks = 0, host = 1;
memset(socks, 0, sizeof(int)*npar);
FD_ZERO(&fdset);
puts("Searching for a server...");
/* set the initial list */
do {
int sock = do_connect(host, &highersock, &fdset);
if (sock != 0) {
socks[i] = sock;
hosts[i++] = host;
}
} while ((++host <= maxhost) && (i != npar));
nsocks = i;
while (nsocks) {
fd_set rset2 = fdset;
fd_set wset2 = fdset;
if (select(highersock+1, &rset2, &wset2, NULL, NULL) < 0)
sendErr("select");
for (int j = 0; j != i; ++j) {
int sock = socks[j];
if (sock &&
(FD_ISSET(sock, &rset2) || FD_ISSET(sock, &wset2))) {
int r;
socklen_t len = sizeof(r);
FD_CLR(sock, &rset2);
FD_CLR(sock, &wset2);
FD_CLR(sock, &fdset);
nsocks -= 1;
socks[j] = 0;
if (sock == highersock) {
/* must be recomputed */
highersock = socks[0];
for (int k = 1; k < i; ++k) {
if (socks[k] > highersock)
highersock = socks[k];
}
}
if ((getsockopt(sock, SOL_SOCKET, SO_ERROR, &r, &len) < 0) || r) {
if (Debug)
printf("cannot connect to 192.168.1.%d\n", hosts[j]);
close(sock);
if (!MySock) {
/* replace it with an other */
while (host <= maxhost) {
if ((sock = do_connect(host, &highersock, &fdset)) != 0) {
socks[j] = sock;
hosts[j] = host++;
nsocks += 1;
break;
}
host += 1;
}
}
}
else {
if (Debug)
printf("connected to 192.168.1.%d\n", hosts[j]);
MySock = sock;
/* may be you want to return, without waiting other attempts finish */
}
}
}
}
if (MySock) {
/* put socket in blocking mode */
int flags = fcntl(MySock, F_GETFL, 0);
if (fcntl(MySock, F_SETFL, flags ^ O_NONBLOCK) < 0)
sendErr("fcntl set non blocking");
alarm(OFF);
return SUCCESS;
}
return FAIL;
}
//
void sendErr(char *msg)
{
perror(msg);
exit(1);
}