小作业: TCP实现广播

目的: 本地主机运行server, 其他多个主机运行client

list.h

/* Copyright (C) 2002 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#ifndef _LIST_H
#define _LIST_H	1

/* The definitions of this file are adopted from those which can be
   found in the Linux kernel headers to enable people familiar with
   the latter find their way in these sources as well.  */


/* Basic type for the double-link list.  */
typedef struct list_head
{
  struct list_head *next;
  struct list_head *prev;
} list_t;

// 定义/创建一个名为name的链表头, 上一项自己, 下一项也指向自己
/* Define a variable with the head and tail of the list.  */
#define LIST_HEAD(name) \
  list_t name = { &(name), &(name) }


// 将传入的ptr作为链表头
/* Initialize a new list head.  */
#define INIT_LIST_HEAD(ptr) \
  (ptr)->next = (ptr)->prev = (ptr)

/* 紧挨着链表头增加新节点(节点不包含数据)
		head   new(节点)	  2		
*/
/* Add new element at the head of the list.  */
static inline void
list_add (list_t *newp, list_t *head)
{
  head->next->prev = newp;		// 2的上一项是new
  newp->next = head->next;		// new的下一项是2
  newp->prev = head;			// new的上一项是head
  head->next = newp;			// head的下一项是new
}

/* 添加新元素到链表尾部
	head 	1	2	3 new
*/
/* Add new element at the tail of the list.  */
static inline void
list_add_tail (list_t *newp, list_t *head)
{
  head->prev->next = newp;	// 3的下一项是new
  newp->next = head;		// new的下一项时head
  newp->prev = head->prev;	// new的上一项时3
  head->prev = newp;		// 头的上一项new
}

/* 删除指定元素:
	(1)   1 elem 2
	(2) 当链表中只有链表头时, 链表头没有没删除(因为节点不包含数据)
		head	// 自己的上一项是自己
				// 自己的下一项也是自己
*/
/* Remove element from list.  */
static inline void
list_del (list_t *elem)
{
  elem->next->prev = elem->prev;	// 2的上一项是1
  elem->prev->next = elem->next;	// 1的下一项时2
}

/* 拼接两个链表:
	1.如果新增的链表只有一个链表头就不做任何处理(因为链表头不包含数据)
	2.	(head 	 2) 	+	(add  4  5)	
		==> (head 	(5 	 6)   2   3)		// 抛弃链表头add
*/
/* Join two lists.  */
static inline void
list_splice (list_t *add, list_t *head)
{
  /* Do nothing if the list which gets added is empty.  */
  if (add != add->next)
    {
      add->next->prev = head;		// 5的上一项是head
      add->prev->next = head->next; // 6的下一项是2
      head->next->prev = add->prev; // 2的上一项是6
      head->next = add->next;		// head的下一项是5
    }
}

// 根据节点ptr的地址,得到其type结构体的首地址, member是type结构体中的节点变量名
/* Get typed element from list at a given position.  */
#define list_entry(ptr, type, member) \
  ((type *) ((char *) (ptr) - (unsigned long) (&((type *) 0)->member)))

#define container_of(ptr, type, member) \
	list_entry(ptr, type, member)

// 从第一个节点(head的下一项开始, 向后遍历head链表中的所有节点)
/* Iterate forward over the elements of the list.  */
#define list_for_each(pos, head) \
  for (pos = (head)->next; pos != (head); pos = pos->next)

// 从第最后一个节点(head的上一项开始, 向前遍历head链表中的所有节点)
/* Iterate forward over the elements of the list.  */
#define list_for_each_prev(pos, head) \
  for (pos = (head)->prev; pos != (head); pos = pos->prev)

// 1.从第最后一个节点(head的上一项开始, 向前遍历head链表中的所有节点)
// 2.当遍历过程中需要删除节点时, 用此函数
// 3.如果使用list_for_each_prev, 在删除节点后pos = pos->prev会出错
/* Iterate backwards over the elements list.  The list elements can be
   removed from the list while doing this.  */
#define list_for_each_prev_safe(pos, p, head) \
  for (pos = (head)->prev, p = pos->prev; \
       pos != (head); \
       pos = p, p = pos->prev)

#endif	/* list.h */

server.c

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>          /* See NOTES */
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <signal.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "list.h"

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
LIST_HEAD(head);
typedef struct sockaddr	   sockaddr_t;
typedef struct sockaddr_in sockaddr_in_t;
typedef struct sockaddr_in6	   in6_addr_t;
typedef	unsigned int		u32;
#define THRAN_1024   1025
#define PORT         THRAN_1024
#define SeverIP		 "192.168.1.104"  
#define CUR			 printf("%s : %d\n", __FILE__, __LINE__);

static int sfd, ret, len = sizeof(sockaddr_t);
static char buf[50];

typedef struct connect {
	int newsfd;
	sockaddr_in_t client_addr;
	list_t list;
} connect_t;

void free_connect(void)
{	
	connect_t *c;
	list_t *pos, *p;
	pthread_mutex_lock(&mutex);
	list_for_each_prev_safe(pos, p, &head) {
		c = container_of(pos, connect_t, list);
		close(c->newsfd);
		list_del(pos);
		free(c);
	}
	pthread_mutex_unlock(&mutex);
}
/* ctr +c 关闭描述符*/
static void sig_int(int sig)
{
	close(sfd);
	system("killall client");
	free_connect();
	exit(0);
}

void *work_connect(void *arg)
{
	int newsfd;
	connect_t *c;
	sockaddr_in_t client_addr; 
	struct in_addr in;
	while(1){
		newsfd = accept(sfd, (sockaddr_t *)&client_addr, &len);	// 这是一个阻塞函数
		pthread_mutex_lock(&mutex);
		c = malloc(sizeof(connect_t));
		c->newsfd = newsfd;
		c->client_addr = client_addr;
		list_add_tail(&c->list, &head);
		// 打印连接的IP地址
		printf("accept ok: %s\n", inet_ntoa(c->client_addr.sin_addr));
		pthread_mutex_unlock(&mutex);
	}
	
	pthread_exit(NULL);
}
void *work_boadcast(void *arg)
{	
	list_t *ptr, *pos;
	connect_t *c;
	while(1){
		memset(buf, 0, 50);
		fgets(buf, 50, stdin);	// 获取输入
		pthread_mutex_lock(&mutex);
		// 广播所有客户端
		list_for_each(pos, &head) {
			c = container_of(pos, connect_t, list);
			write(c->newsfd, buf, strlen(buf));
		}
		pthread_mutex_unlock(&mutex);
	}
	pthread_exit(NULL);
}

int main(int argc, char **argv)
{
	sockaddr_in_t server_addr;	// 服务器地址
	u32 host_addr;				// 参考glibc
	pthread_t tid_con, tid_bc;

	signal(SIGINT, sig_int);
	
	// 创建IPV4流套接字
	sfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sfd) {
		perror("socket err");
		return -1;
	}
	
	// 服务器套接字绑定地址
	memset(&server_addr, 0, len);
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(PORT);					// 服务器端口号
#if 0	// 方法二
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);	// 服务器主机上的任意一个可用的IP地址
#else
	server_addr.sin_addr.s_addr = inet_addr(SeverIP);
#endif
	if(-1 == bind(sfd, (sockaddr_t *)&server_addr, len)) {
		perror("bind err");
		goto err;
	}
	
	// 设置监听的最大路数
	listen(sfd, 5); 

	CUR;

	if(pthread_create(&tid_con, NULL, work_connect, NULL))
		perror("pthread_create err");
	if(pthread_create(&tid_bc, NULL, work_boadcast, NULL))
		perror("pthread_create err");

	pthread_join(tid_con, NULL);
	pthread_join(tid_bc, NULL);
	
	// 释放
	free_connect();

	return 0;
err:
	close(sfd);
	return -1;
}

client.c

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <signal.h>

typedef	unsigned int		u32;
typedef struct sockaddr	   sockaddr_t;
typedef struct sockaddr_in sockaddr_in_t;
typedef struct in6_addr	   in6_addr_t;
#define THRAN_1024   1025
#define PORT         THRAN_1024
#define LEN 		 sizeof(sockaddr_t)
#define SeverIP			"192.168.1.104"  
#define CUR			printf("%s : %d\n", __FILE__, __LINE__);

static int cfd, ret;

/* ctr +c 关闭描述符*/
static void sig_int(int sig)
{
	printf("sig_int\n");
	close(cfd);
	exit(0);
}


int main(int argc, char **argv)
{
	sockaddr_in_t server_addr;	// 服务器地址
	char buf[50];
	u32 host_addr;	// 参考glibc

	signal(SIGINT, sig_int);

	// 创建IPV4流套接字
	cfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == cfd) {
		perror("socket err");
		return -1;
	}
	
	memset(&server_addr, 0, LEN);
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(PORT);					// 服务器端口号
#if 0	// 方法二
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);	// 服务器主机上的任意一个可用的IP地址
#else
	server_addr.sin_addr.s_addr = inet_addr(SeverIP);
#endif
	CUR;
	// 连接
	if(-1 ==  connect(cfd, (sockaddr_t *)&server_addr, LEN)) {	// 这是一个阻塞函数
		perror("connect err");
		goto err;
	}
	printf("connect ok\n");
	// 接收数据
	while(1) {
		memset(buf, 0, 50);
		ret = read(cfd, buf, 50);	
		printf("read %d: %s\n",ret, buf);
		if (!ret) {
			printf("server no connect\n");
			goto err;
		}
	}
	return 0;
err:
	close(cfd);
	return -1;
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值