学习完《UNIX环境高级编程》套接字一章的内容之后,自己实现了单个客户端与服务器的通信程序,后面想想要是多个客户端如何与服务器通信呢?这就有了这篇文章。
这里采用的是用多线程实现多客户端与服务器的通信,多线程的思路参考了Linux C利用Socket套接字进行服务器与多个客户端进行通讯,在此感谢原文章作者。
服务器端程序:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFLEN 128
#define QLEN 10
#define DATALEN 200
#define SERVPORT 48800
#define HOST_NAME_MAX 256
#define IPLEN 16
//store accept id of client connect to server
int acceptfd[QLEN];
//number of client connect to server
int acceptnum = 0;
//information from main thread to created thread
struct threadinfo
{
int clfd;
char ipaddr[IPLEN];
int port;
};
void sys_err(char *errinfo)
{
if(NULL == errinfo)
{
return;
}
perror("errinfo");
exit(0);
}
int initsrver(int type, const struct sockaddr *addr, socklen_t alen, int qlen)
{
int fd;
int err = 0;
int reuse = -1;
if((fd = socket(addr->sa_family, type, 0)) < 0)
{
return -1;
}
//set socket option
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) < 0)
goto errout;
if(bind(fd, addr, alen) < 0)
goto errout;
if(type == SOCK_STREAM || type == SOCK_SEQPACKET)
{
//listen
if(listen(fd, qlen) < 0)
goto errout;
}
return fd;
errout:
err = errno;
close(fd);
errno = err;
return -1;
}
void *recvmessage(void *arg)
{
char buf[BUFLEN];
char recvtitle[DATALEN] = "receive from ";
int n;
struct threadinfo *ti = arg;
memset(buf, 0, BUFLEN);
sprintf(recvtitle, "%s%s:%d ", recvtitle, ti->ipaddr, ti->port);
while((n = recv(ti->clfd, buf, BUFLEN, 0)) > 0)
{
write(STDOUT_FILENO, recvtitle, strlen(recvtitle));
write(STDOUT_FILENO, buf, n);
memset(buf, 0, BUFLEN);
}
if(n < 0)
printf("recv error\n");
}
void *acceptThread(void *sockfd)
{
int clfd;
const char *addr;
char buf[BUFLEN];
int n;
char abuf[INET_ADDRSTRLEN];
struct sockaddr_in clientsockaddr;
int clientlen;
int err;
pthread_t recvtid;
struct threadinfo ti;
clientlen = sizeof(clientsockaddr);
//always accept new client to accept a lot of client
while(1)
{
if((clfd = accept(*(int *)sockfd, (struct sockaddr *)&clientsockaddr, &clientlen)) < 0)
sys_err("accept error");
printf("%s:%d login in to server\n", inet_ntoa(clientsockaddr.sin_addr), clientsockaddr.sin_port);
//when accept client, store it's accept id
acceptfd[acceptnum++] = clfd;
//create thread to receive message of every client
ti.clfd = clfd;
strcpy(ti.ipaddr, inet_ntoa(clientsockaddr.sin_addr));
ti.port = clientsockaddr.sin_port;
if((err = pthread_create(&recvtid, NULL, recvmessage, &ti)) != 0)
sys_err("pthread_create recvmessage error");
}
}
int communication(int sockfd)
{
int clfd;
pid_t pid;
char buf[BUFLEN];
int fd;
int err;
pthread_t acpttid;
int i;
memset(buf, 0, BUFLEN);
//create thread to accept client, always accept to accept a lot of client
if((err = pthread_create(&acpttid, NULL, acceptThread, &sockfd)) != 0)
sys_err("pthread_create acceptThread error");
//send message to client, but only send the same mesage to all connected client now
memset(buf, 0, BUFLEN);
while(1)
{
if(fgets(buf, BUFLEN, stdin) != NULL)
{
//print the num of accept client connect to server
printf("acceptnum: %d\n", acceptnum);
for (i = 0; i < acceptnum; ++i)
{
send(acceptfd[i], buf, strlen(buf), 0);
}
memset(buf, 0, BUFLEN);
}
}
close(clfd);
}
int main(int argc, char *argv[])
{
struct sockaddr_in serversockaddr, clientsockaddr;
int sockfd, clientfd;
int err, n;
if(argc != 1)
{
printf("usage: %s\n", argv[0]);
exit(0);
}
serversockaddr.sin_family = AF_INET;
serversockaddr.sin_port = htons(SERVPORT);
serversockaddr.sin_addr.s_addr = INADDR_ANY;
bzero(&(serversockaddr.sin_zero), 8);
if((sockfd = initsrver(SOCK_STREAM, (struct sockaddr *)&serversockaddr, sizeof(struct sockaddr), QLEN)) > 0)
{
//start communication
communication(sockfd);
exit(0);
}
exit(0);
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVPORT 48800
#define MAXSLEEP 128
#define BUFLEN 128
#define DATALEN 200
int connect_retry(int domain, int type, int protocol, const struct sockaddr *addr)
{
int numsec, fd;
//try to connect with exponential backoff
for(numsec = 1; numsec <= MAXSLEEP; numsec <<= 1)
{
if((fd = socket(domain, type, protocol)) < 0)
return -1;
if(connect(fd, addr, sizeof(struct sockaddr)) == 0)
{
return fd;
}
close(fd);
//delay before trying again
if(numsec <= MAXSLEEP/2)
sleep(numsec);
}
return -1;
}
void *sendmessage(void *arg)
{
int sockfd;
char buf[BUFLEN];
sockfd = *(int *)arg;
memset(buf, 0, BUFLEN);
while(fgets(buf, BUFLEN, stdin) != NULL)
{
send(sockfd, buf, strlen(buf), 0);
memset(buf, 0, BUFLEN);
}
}
void communication(int sockfd)
{
int n;
pid_t pid;
char buf[BUFLEN];
char *recvtitle = "received from server: ";
int fd;
int err;
pthread_t tid;
memset(buf, 0, BUFLEN);
//create thread to send message
if(err = pthread_create(&tid, NULL, sendmessage, &sockfd))
{
printf("pthread_create error\n");
exit(0);
}
//receive message
while(1)
{
while((n = recv(sockfd, buf, BUFLEN, 0)) > 0)
{
write(STDOUT_FILENO, recvtitle, strlen(recvtitle));
write(STDOUT_FILENO, buf, n);
memset(buf, 0, BUFLEN);
}
if(n < 0)
printf("recv error\n");
}
}
int main(int argc, char *argv[])
{
int sockfd, err;
struct sockaddr_in serversockaddr;
const char *addr;
char abuf[INET_ADDRSTRLEN];
struct hostent *host;
if(argc != 2)
{
printf("usage: %s hostname\n", argv[0]);
exit(0);
}
if((host = gethostbyname(argv[1])) == NULL)
{
perror("gethostbyname");
exit(0);
}
serversockaddr.sin_family = AF_INET;
serversockaddr.sin_port = htons(SERVPORT);
serversockaddr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(serversockaddr.sin_zero), 0);
if((sockfd = connect_retry(serversockaddr.sin_family, SOCK_STREAM, 0, (struct sockaddr *)&serversockaddr)) < 0)
{
err = errno;
}
else
{
communication(sockfd);
exit(0);
}
printf("can't connect to %s\n", argv[1]);
exit(0);
}