更多资料请点击:我的目录
本篇仅用于记录自己所学知识及应用,代码仍可优化,仅供参考,如果发现有错误的地方,尽管留言于我,谢谢。
要求:
1.能随时接收任何客户端的连接和信息。
2.建立一定的防刷屏机制,使得客户端的吐槽不能太快。
3.显示当前的连接总数。
4.如果使用多线程或者多进程,注意防范僵尸。
升级版1
升级版2
运行结果:
具体代码:
/*
服务器部分:
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "list.h"
int tcpsock;
int newsock;
struct node *list;
void finish()
{
struct node *p = list->next;
while(p != list) //循环读取每个节点
{
write(p->data,"quit",4); //给q发送退出信息
p = p->next; //下一节点
}
close(tcpsock);
close(newsock);
exit(0);
}
struct arg //定义结构体存储文件描述符
{
struct node *list1; //存放所有服务器的链表
struct node *p1; //存放新连接的服务器节点
};
void *transfer(void *arg)
{
struct arg *arg1 = arg;
struct node *head = arg1->list1;
struct node *q = arg1->p1;
struct node *p ;
char buf[100] = {
0}; //存放客户端发送的消息
char newbuf[100] = {
0}; //存放向其他客户端发送的消息
while(1)
{
p = head->next;
bzero(&buf,100);
if(read(q->data,buf,100)) //q客户没有断开
{
while(p!=head) //循环读取每个节点
{
if(p->data != q->data ) //判断是否为p节点
{
//printf("%d receive: %s\n",p->data,buf); //输出q的文件描述符
bzero(&newbuf,100);
sprintf(newbuf,"%d: %s",q->data,buf);
write(p->data,newbuf,strlen(newbuf)); //给q发送p的信息
}
p = p->next; //下一节点
}
}
else
{
delnode(q, head); //客户端断开,删除对应节点
}
}
}
int main()
{
int ret;
struct sockaddr_in bindaddr; //定义ipv4地址结构体变量
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=inet_addr("192.168.1.157"); //绑定服务器自己的ip地址
bindaddr.sin_port=htons(10086); //绑定服务器自己的端口号
struct sockaddr_in clientaddr;
bzero(&clientaddr,sizeof(clientaddr));
int addrsize=sizeof(clientaddr);
tcpsock=socket(AF_INET,SOCK_STREAM,0); //创建tcp套接字
if(tcpsock==-1)
{
perror("创建tcp套接字!\n");
return -1;
}
int on=1;
setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); //设置为端口断开可以立即重复使用
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr)); //绑定ip和端口号
if(ret==-1)
{
perror("绑定失败!\n");
return -1;
}
//监听
ret=listen(tcpsock,8);
if(ret==-1)
{
perror("监听失败!\n");
return -1;
}
list = list_init();
signal(SIGINT,finish);
int i = 0;
while(1)
{
newsock=accept(tcpsock,(struct sockaddr *)&clientaddr,&addrsize);//接收客户端的连接请求
if(newsock==-1)
{
perror("接收客户端的连接请求失败!\n");
return -1;
}
printf("ip == %s\n",inet_ntoa(clientaddr.sin_addr));
printf("端口号 == %d ",ntohs(clientaddr.sin_port));
struct node *new = malloc(sizeof(struct node)); //创建新节点
new = newnode(newsock, //新节点存放新的文件描述符
inet_ntoa(clientaddr.sin_addr), //存放新连接的服务器的ip地址
ntohs(clientaddr.sin_port));
addnode(new,list); //插入新节点
printf("新的文件描述符:%d\n",newsock);
struct arg *arg1 = malloc(sizeof(struct arg)); //定义结构体存放参数,传给新线程任务函数
arg1->list1 = list;
arg1->p1 = new;
pthread_t id[i];
pthread_create(&id[i],NULL,&transfer,arg1); //每次接受客户端连接都创建一个线程
i++;
}
}
/*
客户端1部分:
*/
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <pthread.h>
time_t now ;
struct tm *tm_now ; //定义当前时间结构体
int tcpsock;
int m = 61, s = 61; //定义分、秒初始值为61
pthread_t id;
void *fun()
{
char recv[100];
while(1)
{
bzero(recv,100);
if(read(tcpsock,recv,100)) //接收服务器发送的消息
{
if(strstr(recv,"quit") != NULL) //判断时候收到“quit”(服务器中断)
{
printf("服务器出错!\n");
close(tcpsock);
exit(0);
}
printf("receive %s\n",recv);
}
}
}
void finish()
{
char fbuf[10] = {
0};
sprintf(fbuf,"%d gone!\n",tcpsock); //向其他所有客户端发送退出信息
write(tcpsock,fbuf,strlen(fbuf)); //当客户端中断时,向服务器发送gone
close(tcpsock);
pthread_cancel(id);
exit(0);
}
int main()
{
int ret;
char buf[100];
struct sockaddr_in bindaddr; //定义ipv4地址结构体变量
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=inet_addr("192.168.1.157"); //绑定客户端自己的ip地址
bindaddr.sin_port=htons(10000); //绑定客户端自己的端口号
struct sockaddr_in serveraddr; //定义结构体变量存放对方(服务器)的ip和端口号
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.s_addr=inet_addr("192.168.1.157"); //服务器的ip地址
serveraddr.sin_port=htons(10086); //服务器的端口号
tcpsock=socket(AF_INET,SOCK_STREAM,0); //创建tcp套接字
if(tcpsock==-1)
{
perror("创建tcp套接字!\n");
return -1;
}
int on=1;
setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); //设置端口释放后立即就可以被再次使用
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr)); //绑定ip和端口号
if(ret==-1)
{
perror("绑定失败!\n");
return -1;
}
ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr)); //连接服务器
if(ret==-1)
{
perror("连接服务器!\n");
return -1;
}
signal(SIGINT,finish);
pthread_create(&id,NULL,&fun,NULL);
printf("请输入要发送给服务器的信息!\n"); //发送信息给服务器
while(1)
{
bzero(buf,100);
scanf("%s",buf);
if(strstr(buf,"quit")!=NULL) //当客户端输入quit退出时,调用finish()函数
{
finish(); //向服务器发送quit
}
{
time(&now) ; //获取当前时间
tm_now = localtime(&now);
if(m == tm_now->tm_min && tm_now->tm_sec - s < 1) //如果分相等,秒相差1s
{
printf("你说话太快了,请歇息一下再来!\n");
}
else
{
write(tcpsock,buf,strlen(buf)); //发送消息
time(&now) ; //获取本次消息发送的时间
tm_now = localtime(&now);
m = tm_now->tm_min;
s = tm_now->tm_sec ;
}
}
}
close(tcpsock);
return 0;
}
/*
客户端2部分:
*/
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <pthread.h>
time_t now ;
struct tm *tm_now ; //定义当前时间结构体
int tcpsock;
int m = 61, s = 61; //定义分、秒初始值为61
pthread_t id;
void *fun()
{
char recv[100];
while(1)
{
bzero(recv,100);
if(read(tcpsock,recv,100)) //接收服务器发送的消息
{
if(strstr(recv,"quit") != NULL) //判断时候收到“quit”(服务器中断)
{
printf("服务器出错!\n");
close(tcpsock);
exit(0);
}
printf("receive %s\n",recv);
}
}
}
void finish()
{
char fbuf[10] = {
0};
sprintf(fbuf,"%d gone!\n",tcpsock); //向其他所有客户端发送退出信息
write(tcpsock,fbuf,strlen(fbuf)); //当客户端中断时,向服务器发送gone
close(tcpsock);
pthread_cancel(id);
exit(0);
}
int main()
{
int ret;
char buf[100];
struct sockaddr_in bindaddr; //定义ipv4地址结构体变量
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=inet_addr("192.168.1.157"); //绑定客户端自己的ip地址
bindaddr.sin_port=htons(20000); //绑定客户端自己的端口号
struct sockaddr_in serveraddr; //定义结构体变量存放对方(服务器)的ip和端口号
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.s_addr=inet_addr("192.168.1.157"); //服务器的ip地址
serveraddr.sin_port=htons(10086); //服务器的端口号
tcpsock=socket(AF_INET,SOCK_STREAM,0); //创建tcp套接字
if(tcpsock==-1)
{
perror("创建tcp套接字!\n");
return -1;
}
int on=1;
setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); //设置端口释放后立即就可以被再次使用
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr)); //绑定ip和端口号
if(ret==-1)
{
perror("绑定失败!\n");
return -1;
}
ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr)); //连接服务器
if(ret==-1)
{
perror("连接服务器!\n");
return -1;
}
signal(SIGINT,finish);
pthread_create(&id,NULL,&fun,NULL);
printf("请输入要发送给服务器的信息!\n"); //发送信息给服务器
while(1)
{
bzero(buf,100);
scanf("%s",buf);
if(strstr(buf,"quit")!=NULL) //当客户端输入quit退出时,调用finish()函数
{
finish(); //向服务器发送quit
}
{
time(&now) ; //获取当前时间
tm_now = localtime(&now);
if(m == tm_now->tm_min && tm_now->tm_sec - s < 1) //如果分相等,秒相差1s
{
printf("你说话太快了,请歇息一下再来!\n");
}
else
{
write(tcpsock,buf,strlen(buf)); //发送消息
time(&now) ; //获取本次消息发送的时间
tm_now = localtime(&now);
m = tm_now->tm_min;
s = tm_now->tm_sec ;
}
}
}
close(tcpsock);
return 0;
}
/*
客户端3部分:
*/
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <pthread.h>
time_t now ;
struct tm *tm_now ; //定义当前时间结构体
int tcpsock;
int m = 61, s = 61; //定义分、秒初始值为61
pthread_t id;
void *fun()
{
char recv[100];
while(1)
{
bzero(recv,100);
if(read(tcpsock,recv,100)) //接收服务器发送的消息
{
if(strstr(recv,"quit") != NULL) //判断时候收到“quit”(服务器中断)
{
printf("服务器出错!\n");
close(tcpsock);
exit(0);
}
printf("receive %s\n",recv);
}
}
}
void finish()
{
char fbuf[10] = {
0};
sprintf(fbuf,"%d gone!\n",tcpsock); //向其他所有客户端发送退出信息
write(tcpsock,fbuf,strlen(fbuf)); //当客户端中断时,向服务器发送gone
close(tcpsock);
pthread_cancel(id);
exit(0);
}
int main()
{
int ret;
char buf[100];
struct sockaddr_in bindaddr; //定义ipv4地址结构体变量
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=inet_addr("192.168.1.157"); //绑定客户端自己的ip地址
bindaddr.sin_port=htons(30000); //绑定客户端自己的端口号
struct sockaddr_in serveraddr; //定义结构体变量存放对方(服务器)的ip和端口号
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.s_addr=inet_addr("192.168.1.157"); //服务器的ip地址
serveraddr.sin_port=htons(10086); //服务器的端口号
tcpsock=socket(AF_INET,SOCK_STREAM,0); //创建tcp套接字
if(tcpsock==-1)
{
perror("创建tcp套接字!\n");
return -1;
}
int on=1;
setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); //设置端口释放后立即就可以被再次使用
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr)); //绑定ip和端口号
if(ret==-1)
{
perror("绑定失败!\n");
return -1;
}
ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr)); //连接服务器
if(ret==-1)
{
perror("连接服务器!\n");
return -1;
}
signal(SIGINT,finish);
pthread_create(&id,NULL,&fun,NULL);
printf("请输入要发送给服务器的信息!\n"); //发送信息给服务器
while(1)
{
bzero(buf,100);
scanf("%s",buf);
if(strstr(buf,"quit")!=NULL) //当客户端输入quit退出时,调用finish()函数
{
finish(); //向服务器发送quit
}
{
time(&now) ; //获取当前时间
tm_now = localtime(&now);
if(m == tm_now->tm_min && tm_now->tm_sec - s < 1) //如果分相等,秒相差1s
{
printf("你说话太快了,请歇息一下再来!\n");
}
else
{
write(tcpsock,buf,strlen(buf)); //发送消息
time(&now) ; //获取本次消息发送的时间
tm_now = localtime(&now);
m = tm_now->tm_min;
s = tm_now->tm_sec ;
}
}
}
close(tcpsock);
return 0;
}
#ifndef _LIST_H
#define _LIST_H
//设置节点
struct node
{
char *ip; //节点存放IP地址
int port; //节点存放端口号
int data;
struct node *next;
struct node *prev;
};
//初始化链表
struct node *list_init()
{
struct node *head = malloc(sizeof(struct node));
head->next = head;
head->prev = head;
return head;
}
//创建新节点
struct node *newnode(int newdata,char *IP, int port)
{
struct node *new = malloc(sizeof(struct node));
new->ip = IP;
new->port = port;
new->data = newdata;
new->next = NULL;
new->prev = NULL;
}
//加入新节点
int addnode(struct node *new,struct node *list)
{
int sum = 1; //初始值为1
struct node *p = list;
while(p->next != list)
{
p = p->next;
sum ++; //每个节点代表每个客户端
}
new->prev = list->prev;
new->next = list;
list->prev = new;
new->prev->next = new;
printf("总链接数:%d\n",sum); //输出当前总链接数
}
//删除节点
int delnode(struct node *q, struct node *head)
{
struct node *p = head;
struct node *p1 = NULL;
while(p->next != head)
{
if(p == q)
{
p1 = p->prev;
p->prev->next = p->next;
p->next->prev = p->prev;
p->next = NULL;
p->prev = NULL;
free(p);
p = p1;
}
p = p->next;
}
if(p->next == head && p == q)
{
p->prev->next = head;
head->prev = p->prev;
p->next = NULL;
p->prev = NULL;
}
}
#endif