linux下socket实现多个客户端与服务器的通信

学习完《UNIX环境高级编程》套接字一章的内容之后,自己实现了单个客户端与服务器的通信程序,后面想想要是多个客户端如何与服务器通信呢?这就有了这篇文章。

这里采用的是用多线程实现多客户端与服务器的通信,多线程的思路参考了Linux C利用Socket套接字进行服务器与多个客户端进行通讯,在此感谢原文章作者。


服务器端程序:

#include<stdio.h>

#include <stdlib.h> 

#include <errno.h> 

#include <string.h> 

#include <netdb.h> 

#include <sys/types.h> 

#include <netinet/in.h> 

#include <sys/socket.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <fcntl.h>

#include <pthread.h>



#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<stdio.h>

#include <stdlib.h> 

#include <errno.h> 

#include <string.h> 

#include <netdb.h> 

#include <sys/types.h> 

#include <netinet/in.h> 

#include <sys/socket.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <fcntl.h>

#include <pthread.h>



#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);



}



  • 1
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值