Linux-socket编程-1对1和1对多聊天

1对1聊天

    通过select添加可读事件的监听实现。

    服务端:

#include "header.h"

int main(void)
{
	int listenfd = -1;
	int connfd = -1;
	struct sockaddr_in server, client;
	int on = 1;

	memset(&server, 0, sizeof(server));
	server.sin_family = AF_INET;
	server.sin_port = htons(8888);
	server.sin_addr.s_addr = htonl(INADDR_ANY);
	
	if (0 > (listenfd = socket(AF_INET, SOCK_STREAM, 0)))
		err_exit("socket");

	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

	if (0 > bind(listenfd, (struct sockaddr *)&server, sizeof(server)))
		err_exit("bind");
	
	listen(listenfd, 1024);
	printf("listen...\n");

	memset(&client, 0, sizeof(client));
	socklen_t len = sizeof(client);
	if (0 > (connfd = accept(listenfd, (struct sockaddr *)&client, &len)))
		err_exit("accept");
	printf("client's ip is:%s, port is:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));

	char recv_buf[BUFSIZ];
	char send_buf[BUFSIZ];
	fd_set readfds;
	int maxfd = -1;
	int ret=-1;
	int i;
	
	while(1) {
		FD_ZERO(&readfds);//所有位置零

		FD_SET(0, &readfds);//设置0比特位为1,用于发送数据消息通知
		maxfd = maxfd > 0 ? maxfd : 0;
		FD_SET(connfd, &readfds);//设置connfd比特位为1,用于接受数据消息通知,
		maxfd = maxfd > connfd ? maxfd : connfd;

		if (0 > select(maxfd + 1, &readfds, NULL, NULL, NULL))//设置被监听的文件描述符的总数为maxfd + 1,readfds可读事件,且无限等待
			err_exit("select");

		for (i=0; i<=maxfd; i++) 
		{
			if (FD_ISSET(i, &readfds)) //select返回后,用FD_ISSET测试给定位是否置位,通过获取那个bit位就知道是那个连接有数据
			{
				if (i == 0)//有数据需要发送
				{
					fgets(send_buf, sizeof(send_buf), stdin);
					if (0 > send(connfd, send_buf, strlen(send_buf) + 1, 0))
						err_exit("send");				
				} 				
				else if (i == connfd)//有数据需要接收
				{
					if (0 > (ret = recv(connfd, recv_buf, BUFSIZ, 0)))
						err_exit("recv");
					else if (0 == ret) 
					{
						printf("client quit!\n");
						exit(0);
					}
					recv_buf[ret] = '\0';
					printf("client: %s\n", recv_buf);				
				}
			}
		}
	}

	close(listenfd);
	close(connfd);
	exit(0);
}

    客户端:

#include "header.h"

int main(void)
{
	int connfd = -1;
	struct sockaddr_in server;
	
	memset(&server, 0, sizeof(server));
	server.sin_family = AF_INET;
	server.sin_port = htons(8888);
	server.sin_addr.s_addr = inet_addr("192.168.1.50");

	if (0 > (connfd = socket(AF_INET, SOCK_STREAM, 0)))
		err_exit("socket");

	if (0 > connect(connfd, (struct sockaddr *)&server, sizeof(server)))
		err_exit("connect");
	printf("connect success!\n");
	
	char send_buf[BUFSIZ];
	char recv_buf[BUFSIZ];
	int maxfd= -1;
	fd_set readfds;
	int ret=-1;
	int i;
	
	while(1) {
		FD_ZERO(&readfds);

		FD_SET(0, &readfds);
		maxfd = maxfd > 0 ? maxfd : 0;
		FD_SET(connfd, &readfds);
		maxfd = maxfd > connfd ? maxfd : connfd;
		
		if (0 > select(maxfd + 1, &readfds, NULL, NULL, NULL))
			err_exit("select");

		for (i=0; i<=maxfd; i++) 
		{
			if (FD_ISSET(i, &readfds)) 
			{
				if (0 == i) 
				{
					fgets(send_buf, sizeof(send_buf), stdin);		
					if (0 > send(connfd, send_buf, strlen(send_buf) + 1, 0))
						err_exit("send");				
				} 
				else if (connfd == i) 
				{
					if (0 > (ret = recv(connfd, recv_buf, BUFSIZ, 0)))
						err_exit("recv");
					else if (0 == ret) 
					{
						printf("server quit!\n");
						exit(0);
					}
					
					recv_buf[ret] = '\0';
					printf("server: %s\n", recv_buf);
				}
			}
		}
	}
	close(connfd);
	exit(0);
}

1对多聊天

    服务端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <sys/select.h>

struct peer_node {  //链表结构体
	int id;  //id号
	int connfd;  //描述符
	struct peer_node *next;  //指针
};

static struct peer_node *head;  //结构体指针

int server_init(short port)  //server服务器初始化函数
{
	int sockfd;
	int on = 1;
	struct sockaddr_in self;
	if(0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))) {  //创建套接字
		fprintf(stderr, "socket : %s\n", strerror(errno));
		return -errno;  //返回出错
	}
	
	if (0 > setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof(on))) {  //允许重用本地址和端口
		fprintf(stderr, "setsockopt : %s\n", strerror(errno));
		exit(1);
	}
	
	memset(&self, 0, sizeof(self)); //初始化
	self.sin_family = AF_INET; //ipv4
	self.sin_port = htons(port);  //端口
	self.sin_addr.s_addr = INADDR_ANY;  //本地ip
	if(0 > bind(sockfd, (struct sockaddr *)&self, sizeof(self))) {  //绑定本地信息
		fprintf(stderr,"socket bind error:%s\n",strerror(errno));
		return -errno;
	}
	printf("listen...\n");  //启动监听
	listen(sockfd,1024);
	
	return sockfd;  //返回描述符
}

int find_id_by_connfd(int connfd)  //在链接里找id
{
	struct peer_node *node; //节点
	node = head->next;
	while(node) {
		if(node->connfd == connfd)
			return node->id; //返回id
		node = node->next;
	}
	
	return -1;
}

int show_message(int skfd)  //打印信息
{
	char buf[1024];
	struct sockaddr_in client;
	socklen_t len;
	int ret;
	
	len = sizeof(client);
	memset(&client, 0, sizeof(client));
	
	ret = recv(skfd,buf,1023,0);  //收
	if(ret == 0) {
		return 0;
	}
	
	buf[ret] = 0;
	if(0 > getpeername(skfd, (struct sockaddr *)&client, &len)) {  //或得信息
		fprintf(stderr, "getpeername : %s\n", strerror(errno));
		exit(1);
	}
	
	printf("message received from id:%d [%s:%d] : %s", find_id_by_connfd(skfd),inet_ntoa(client.sin_addr),ntohs(client.sin_port), buf);
	return 1; //打印 id ip 端口 信息
}

int add_peerlist(int skfd)  //把id从尾压入链表
{
	struct peer_node *node;  //节点
	static int id = 1;
	struct peer_node *tail;  //尾
	
	node = (struct peer_node *) malloc(sizeof(struct peer_node));  //开辟 堆
	if(node == NULL) {
		perror("malloc");
		return -1;
	}
	
	node->id = id;
	node->connfd = skfd;
	node->next = NULL;
	
	tail = head;
	while(tail->next) {
		tail = tail->next;
	}
	tail->next = node;
	id++;  //id 自加
	return (node->id);
}

int del_peerlist(int skfd)  //删除
{
	struct peer_node *node;
	struct peer_node *tmp;
	int id;
	
	node = head;
	while(node->next->connfd != skfd) {
		node = node->next;
	}
	
	tmp = node->next;
	id = tmp->id;
	node->next = tmp->next;
	free(tmp);
	return id;
}

int show_peerlist(void)  //打印id列表
{
	struct peer_node *node;
	struct sockaddr_in client;
	int ret;
	socklen_t len = sizeof(client);
	
	node = head->next;
	printf("=========================\n");
	while(node) {
		ret = getpeername(node->connfd, (struct sockaddr *)&client,&len);
		if(ret < 0) {
			perror("getpeername");
		}
		printf("%d:%s:%d\n",node->id, inet_ntoa(client.sin_addr),ntohs(client.sin_port));   //id ip 端口 
		node = node->next;
	}
	printf("=========================\n");
	return 0;
}

int find_connfd_by_id(int id)  // 找文件描述符
{
	struct peer_node *node;
	node = head->next;
	while(node) {
		if(node->id == id)
			return node->connfd;  //返回
		node = node->next;
	}
	return -1;
}

int init_peerlist(void)  //初始化链表
{
	head = (struct peer_node *) malloc(sizeof(struct peer_node));
	if (NULL == head) {
		fprintf(stderr, "malloc : %s\n", strerror(errno));
		exit(1);
	}
	head->connfd = -1;
	head->next = NULL;
	return 0;
}

int send_message(void)  //发送信息
{
	char buf[1024];
	char *ptr = NULL;
	int skfd, id;
	char id_str[10];  //存放id
	
	fgets(buf,1024,stdin);
	if (!strncasecmp(buf, "quit", 4)) {
		printf("server close!\n");
		exit(1);
	}
	
	if(0 == strncmp(buf,"list",4)) {  //list 显示列表
		show_peerlist();
		return 0;
	}
	
	if(NULL == (ptr = strstr(buf,":"))) {  //找出“:”位置指针
		fprintf(stderr,"please usage \"ID:Message\" format!\n");
		return 0;
	}
	memset(id_str, 0, 10);
	memcpy(id_str, buf, (ptr - buf));  //拷贝内存内容   把1:***** 字符1拷贝到 id_str

	if (0 >= (id = atoi(id_str))) {  //转换成整数
		fprintf(stderr, "the id is not valid!\n");
		return -1;
	}
	
	skfd = find_connfd_by_id(id);
	if(skfd < 0) {
		fprintf(stderr,"Not found valid user!!!\n");
		return -1;
	}
	
	if (0 > send(skfd, ptr+1, strlen(ptr + 1) + 1, 0)) {  //发送:后面内容
		fprintf(stderr,"send : %s\n", strerror(errno));
		return -1;
	}
	
	return 0;
}

int main(int argc, char *argv[])   //主函数
{
	if (2 > argc) {  //判断
		fprintf(stderr, "Usage : %s + server portnumber\n", argv[0]);
		exit(1);
	}
	int listenfd;
	int newfd;
	int ret,maxfd,i,id;
	fd_set current_fds,bak_fds;
	char buf[1024];
	init_peerlist();
	short portnumber;
	if (0 >= (portnumber = atoi(argv[1]))) {  //端口号输入
		fprintf(stderr, "port number is invalid!\n");
		exit(1);
	}
	listenfd = server_init(portnumber);  //调用前面服务器函数
	if(listenfd < 0)
		exit(-listenfd);
	printf("type \"list\" for the client list!\n");
	
	FD_ZERO(&current_fds);
	FD_ZERO(&bak_fds);
	
	FD_SET(0, &current_fds);
	FD_SET(listenfd, &current_fds);
	
	maxfd = listenfd > 0 ? listenfd : 0;
	while(1) {
		bak_fds = current_fds;
		ret = select(maxfd+1, &bak_fds, NULL, NULL, NULL);  //多路复用
		if(ret < 0) {
			fprintf(stderr,"select error:%s\n",strerror(errno));
			return errno;
		}
		for(i = 0; i <= maxfd; i++) {
			if(FD_ISSET(i, &bak_fds)) {
				if(0 == i) {
					send_message();
				} else if (i == listenfd){
					if (0 > (newfd = accept(listenfd, NULL, NULL))) {
						fprintf(stderr, "accept : %s\n", strerror(errno));
						exit(1);
					}
					FD_SET(newfd, &current_fds);
					if(newfd > maxfd)
						maxfd = newfd;
					id = add_peerlist(newfd);
					snprintf(buf, sizeof(buf), "you id is %d\n", id);
					send(newfd, buf, strlen(buf) + 1, 0);
					printf("new connection : connfd is %d, id is %d\n", newfd, id);
				} else {
					ret = show_message(i);
					if(ret == 0) {
						close(i);
						FD_CLR(i, &current_fds);
						if(i == maxfd)
							maxfd--;
						printf("id : %d client quit!\n", del_peerlist(i));
					}
				}
			}
		}	
	}
	return 0;
}

    客户端:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>

#define MAXBUF 1024

int main(int argc, char *argv[])
{
	int sockfd;
	socklen_t len;
	struct sockaddr_in dest;
	struct sockaddr_in self;
	char buffer[MAXBUF + 1];
	fd_set rfds;
	struct timeval tv;
	int retval, maxfd = -1;

	if (argc < 2) {
		fprintf(stderr, "Usage : %s + server_port\n", argv[0]);
		exit(0);
	}
	
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		fprintf(stderr, "Socket : %s\n", strerror(errno));
		exit(errno);
	}
	
	bzero(&dest, sizeof(dest));
	dest.sin_family = AF_INET;
	if (0 > (dest.sin_port = htons(atoi(argv[1])))) {
		fprintf(stderr, "port number is wrong\n");
		exit(1);
	}
    dest.sin_addr.s_addr = inet_addr("192.168.1.50");
	
	if (0 > connect(sockfd, (struct sockaddr *) &dest, sizeof(dest))) {
		fprintf(stderr, "Connect : %s\n", strerror(errno));
		exit(1);
	}

	printf("connect success!\n");
	len = sizeof(self);
	if(0 > getsockname(sockfd, (struct sockaddr *)&self, &len)) {
		fprintf(stderr, "getsockname : %s\n", strerror(errno));
		exit(1);
	}
	
	printf("Myself is %s:%d\n",inet_ntoa(self.sin_addr),ntohs(self.sin_port));
	while (1) {
		FD_ZERO(&rfds);
		FD_SET(0, &rfds);
		FD_SET(sockfd, &rfds);
		if (sockfd > maxfd)
			maxfd = sockfd;

		tv.tv_sec = 1;
		tv.tv_usec = 0;
		
		retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
		if (retval == -1) {
			fprintf(stderr, "select : %s\n", strerror(errno));
			break;
		} else if (retval == 0) {
			continue;
		} else {
			if (FD_ISSET(sockfd, &rfds)) {
				bzero(buffer, MAXBUF + 1);
				len = recv(sockfd, buffer, MAXBUF, 0);
				if (len > 0)
					printf("message received from server : %s",buffer);
				else{
					if (len < 0)
						fprintf(stderr, "recv : %s\n", strerror(errno));
					else
						printf("server close!\n");
					break;
				}
			}
			else if(FD_ISSET(0, &rfds)) {
				bzero(buffer, MAXBUF + 1);
				fgets(buffer, MAXBUF, stdin);
				if (!strncasecmp(buffer, "quit", 4)) {
					printf("client close!\n");
					break;
				}
				
				if ( 0 > send(sockfd, buffer, strlen(buffer)+1, 0)) {
					fprintf(stderr, "send : %s\n", strerror(errno));
					//break;
				}
			}
		}
	}
	close(sockfd);
	return 0;
}

   

猜你喜欢

转载自blog.csdn.net/TSZ0000/article/details/83377580
今日推荐