更多资料请点击:我的目录
本篇仅用于记录自己所学知识及应用,代码仍可优化,仅供参考,如果发现有错误的地方,尽管留言于我,谢谢。
任务要求:
①链表分别存储不同客户端产生的新套接字;
②群发功能(广播),客户端A发消息,其他所有客户端都能收到A的消息,如果客户端A断开连接,所有其他客户端收到A的下线通知,聊天仍然继续,需要删除客户端A的信息;
③服务器断开,所有在线客户端收到“服务器出错”提示,并要求所有在线客户端都退出;
④私聊功能(点播),客户端A以 “IP:端口号:消息”格式发送消息,服务器收到消息先进行判断IP号、端口号对应的是哪个客户端,在进行对应消息的发送。
例如:客户端B IP: 123456 端口号:8848
客户端A向服务器发送:123456:8848:jiajiage
那么结果是所有客户端中,只有客户端B收到:jiajiage
示例效果:
具体代码如下:
服务器部分:
/*
服务器部分:
*/
#include "myhead.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 ;
struct node *p1 ;
char buf[100] = {0}; //存放客户端发送的消息
char *to_ip; //存放客户端发送给指定客户端的ip地址
int to_port; //存放客户端发送给指定客户端的端口号
char to_buf[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节点
{
if(strstr(buf,":") != NULL) //判断是否指定接收的客户端
{
to_ip = (strtok(buf,":")); //获取发送给指定客户端的ip地址
to_port = atoi(strtok(NULL,":")); //获取发送给指定客户端的端口号
sprintf(to_buf,"%s",(strtok(NULL,":"))); //获取发送给指定客户端的消息
p1 = head->next;
while(p1 != head) //新的循环
{
if(strstr(p1->ip,to_ip)!=NULL //比较ip
&& p1->port == to_port) //比较端口号
{
printf("%d receive: %s\n",p1->data,to_buf); //输出q的文件描述符
char newbuf[110] = {0};
sprintf(newbuf,"%d: %s",q->data,to_buf);
write(p1->data,newbuf,strlen(newbuf)); //给指定客户端发送p的信息
break; //退出此次while循环
}
p1 = p1->next; //下一个节点
}
break; //退出此次循环
}
if(strstr(buf,":") == NULL ) //判断是否指定接收的客户端
{
printf("%d receive: %s\n",p->data,buf); //输出q的文件描述符
char newbuf[110] = {0};
sprintf(newbuf,"%d: %s",q->data,buf);
write(p->data,newbuf,strlen(newbuf)); //给q发送p的信息
}
}
p = p->next; //下一节点
}
}
}
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.23.26"); //绑定服务器自己的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\n",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部分:
/*
客户端1部分:
*/
#include "myhead.h"
int tcpsock;
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()
{
write(tcpsock,"sign out!",9);
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.23.26"); //绑定客户端自己的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.23.26"); //服务器的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)
{
finish();
}
write(tcpsock,buf,strlen(buf));
}
close(tcpsock);
return 0;
}
客户端2部分:
/*
客户端1部分:
*/
#include "myhead.h"
int tcpsock;
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()
{
write(tcpsock,"sign out!",9);
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.23.26"); //绑定客户端自己的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.23.26"); //服务器的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)
{
finish();
}
write(tcpsock,buf,strlen(buf));
}
close(tcpsock);
return 0;
}
客户端3部分:
/*
客户端1部分:
*/
#include "myhead.h"
int tcpsock;
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()
{
write(tcpsock,"sign out!",9);
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.23.26"); //绑定客户端自己的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.23.26"); //服务器的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)
{
finish();
}
write(tcpsock,buf,strlen(buf));
}
close(tcpsock);
return 0;
}
头文件部分:
#ifndef _MY_HEAD_H
#define _MY_HEAD_H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <linux/input.h>
#include <errno.h>
#include <dirent.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <error.h>
#include <strings.h>
#include <pthread.h>
#include "list.h"
#endif
链表部分:
#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)
{
struct node *p = list;
while(p->next != list)
{
p = p->next;
}
new->prev = list->prev;
new->next = list;
list->prev = new;
new->prev->next = new;
}
//删除节点
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