目的: 本地主机运行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;
}