客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#define PORT 7777
#define IP "192.168.100.11"
#define ERR_MSG(msg) do{\
fprintf(stderr,"line:__%d__\n",__LINE__);\
perror(msg);\
}while(0)
//创建结构体封装数据包
struct msg
{
char type; //封装协议
char name[20];
char text[128];
}recvdata,snddata;
//存储服务器信息,供给sendto使用
struct sockaddr_in rcv;
struct sockaddr_in ser;
//recvfrom接收到的数据包地址信息
socklen_t addrlen=sizeof(rcv);//数据包大小
//创建双线程用于收发
pthread_t tid1,tid2;
//发
void *callback1(void *arg) //void *arg=&sfd
{
int sfd=*(int *)arg;
ssize_t res=0;
while(1)
{
bzero(snddata.text,sizeof(snddata.text));
snddata.type = 'C';//封装聊天协议
fgets(snddata.text,sizeof(snddata.text),stdin);
snddata.text[strlen(snddata.text)-1]='\0';
if(strcmp(snddata.text, "quit") == 0)
{
//组下线数据包
snddata.type = 'Q';
if(sendto(sfd,&snddata, sizeof(snddata), 0, (struct sockaddr*)&ser, sizeof(ser))< 0)
{
ERR_MSG("sendto");
return NULL;
}
exit(0);
//break;
}
if(sendto(sfd, &snddata, sizeof(snddata), 0, (struct sockaddr*)&ser, sizeof(ser)) < 0)
{
ERR_MSG("sendto");
return NULL;
}
printf("sendto success\n");
}
}
//收
void *callback2(void *arg) //void *arg=&sfd
{
int sfd=*(int *)arg;
char s=0;
ssize_t res=0;
while(1)
{
//接收
bzero(&recvdata, sizeof(recvdata));
//接收数据包
res = recvfrom(sfd,&recvdata, sizeof(recvdata),0,NULL,NULL);
if(res < 0)
{
ERR_MSG("recvfrom");
return NULL;
}
s=recvdata.type;
switch(s)
{
case 'L':
printf("[%s]已经上线---\n",recvdata.name);
break;
case 'C':
printf("[%s]:[%s]\n",recvdata.name,recvdata.text);
break;
case 'Q':
printf("[%s]已经下线---\n",recvdata.name);
break;
}
}
pthread_exit(&tid2);
}
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
//填充服务器地址信息结构体
ser.sin_family=AF_INET;
ser.sin_port=htons(PORT);
ser.sin_addr.s_addr=inet_addr(IP);
char buf[128]="";
ssize_t res=0;
printf("请输入登录用户名>>>\n");
snddata.type='L';
scanf("%s",snddata.name);
while(getchar() != 10);
if(sendto(sfd,&snddata,sizeof(snddata),0,(struct sockaddr *)&ser,sizeof(ser))<0)
{
ERR_MSG("sendto");
return -1;
}
// printf("sendto success\n");
//创建callback1线程用于服务器发
if(pthread_create(&tid1,NULL,callback1,(void *)&sfd)!=0)
{
fprintf(stderr,"pthread_create error\n");
return -1;
}
pthread_detach(tid1); //分离线程
//创建callback2线程用于服务器收
if(pthread_create(&tid2,NULL,callback2,(void *)&sfd)!=0)
{
fprintf(stderr,"pthread_create error\n");
return -1;
}
//阻塞线程
pthread_join(tid2,NULL);
//关闭文件描述符
close(sfd);
return 0;
}
服务器
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#define PORT 7777 //指定端口号,1024~49151
#define IP "192.168.100.11" //填自己的本机IP ifconfig查看
#define ERR_MSG(msg) do{\
fprintf(stderr, "line:%d\n", __LINE__);\
perror(msg);\
}while(0)
//创建数据结构体
typedef struct data{
char type; //存储交互协议
char name[20]; //用户名
char text[128];
}msg;
msg recvdata; //用于收数据
msg snddata; //用于发数据
//定义用户信息结构体
typedef struct Node{
union{
int len; //头结点数据域
struct sockaddr_in data; //用户地址信息结构体
};
struct Node *next;
}Linklist;
//链表头结点
Linklist *L;
int sfd = 0;
Linklist *list_create(); //创建链表
int list_empty(Linklist *L); //判空函数
struct sockaddr_in cin; //存储用户信息结构体
socklen_t addr = sizeof(cin); //用户信息结构体的大小
void list_show(Linklist *L,msg p); //登录遍历链表函数
Linklist *buy_node(struct sockaddr_in e); //申请节点封装数据函数
int list_insert_tail(Linklist *L,struct sockaddr_in e); //尾插函数
void list_show1(Linklist *L,msg p,struct sockaddr_in sin); //发送信息遍历链表
int list_search_value(Linklist *L,struct sockaddr_in e); //按值进行查找函数
int list_delete_pos(Linklist *L,int pos); //任意位置删除
void list_show2(Linklist *L,msg p); //用户下线遍历链表
void *callback1(void *p); //接收线程函数
void *callcback2(void *p); //发送线程函数
Linklist *find_node(Linklist *L,int pos); //按位置查找返回结点
//接收线程
void *callback1(void *arg) //void *arg=&sfd
{
int sfd = *(int *)arg;
ssize_t res = 0;
char c = 0;
int a = 0;
while(1)
{
res = recvfrom(sfd,&recvdata,sizeof(recvdata),0,(struct sockaddr *)&cin,&addr);
if(res < 0)
{
ERR_MSG("recvfrom");
return NULL;
}
c = recvdata.type;
switch(c)
{
case 'L':
//打印信息
printf("[%s]上线---\n",recvdata.name);
//遍历函数发送数据
list_show(L,recvdata);
//添加结点
list_insert_tail(L,cin);
break;
case 'C':
//打印信息
printf("[%s]:%s\n",recvdata.name,recvdata.text);
//遍历转发数据
list_show1(L,recvdata,cin);
break;
case 'Q':
printf("[%s]下线\n",recvdata.name);
//按值查找返回位置
a = list_search_value(L,cin);
//任意位置删除
list_delete_pos(L,a);
//遍历输出数据
list_show2(L,recvdata);
break;
}
}
}
//发送线程
void *callcback2(void *arg) //void *arg=&sfd
{
int sfd = *(int *)arg;
char str[128] = "";
snddata.type = 'C';
strcpy(snddata.name,"system");
while(1)
{
bzero(str,sizeof(str));
fgets(str,sizeof(str),stdin);
str[strlen(str)-1] = 0;
strcpy(snddata.text,str);
list_show2(L,snddata);
}
}
int main(int argc, const char *argv[])
{
//创建报式套接字
sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd < 0)
{
ERR_MSG("socket");
return -1;
}
//允许端口快速重用--->允许程序退出后,端口号能被新的进程快速覆盖
//必须放在bind之前
int reuse = 1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) <0)
{
ERR_MSG("setsockopt");
return -1;
}
printf("允许端口快速重用成功\n");
//填充服务器自身的地址信息结构体
cin.sin_family = AF_INET;
cin.sin_port = htons(PORT);
cin.sin_addr.s_addr = inet_addr(IP);
//绑定IP和端口
if(bind(sfd,(struct sockaddr *)&cin,sizeof(cin)) < 0)
{
ERR_MSG("bind");
return -1;
}
//创建链表
L = list_create();
if(NULL == L)
{
printf("%d:创建失败\n",__LINE__);
return -1;
}
pthread_t tid1,tid2;
//创建收线程callback1;
if(pthread_create(&tid1,NULL,callback1,&sfd) < 0)
{
ERR_MSG("pthread_create");
return -1;
}
//创建发线程callback2;
if(pthread_create(&tid2,NULL,callcback2,&sfd) < 0)
{
ERR_MSG("pthread_create");
return -1;
}
//阻塞线程
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
//关闭文件描述符
close(sfd);
return 0;
}
//遍历链表
void list_show(Linklist *L,msg p)
{
//判断逻辑
if(NULL == L || list_empty(L))
{
return;
}
//遍历逻辑
Linklist *q = L->next; //定义指针指向第一个结点
//puts("此时链表元素分别是:");
while(q != NULL)
{
//printf("%c\t",q->data);
if(sendto(sfd,&p,sizeof(p),0,(struct sockaddr *)&q->data,sizeof(q->data)) < 0)
{
ERR_MSG("sendto");
return ;
}
q = q->next;
}
//putchar(10);
}
//申请节点封装数据函数
Linklist *buy_node(struct sockaddr_in e)
{
//在堆区申请一个结点的空间
Linklist *p = (Linklist *)malloc(sizeof(Linklist));
if(NULL == p)
{
puts("空间申请失败");
return NULL;
}
//将数据封装到结点的数据域中
p->data = e;
p->next = NULL;
return p;
}
//尾插函数
int list_insert_tail(Linklist *L,struct sockaddr_in e)
{
//判断逻辑
if(NULL == L)
{
puts("所给链表不合法");
return 0;
}
//申请节点封装数据
Linklist *p = buy_node(e);
//定义遍历指针,指向最后一个结点
Linklist *q = L;
while(q->next != NULL)
{
q = q->next;
}
//尾插操作
p->next = q->next;
q->next = p;
//表长变化
L->len++;
//puts("尾插成功");
return 1;
}
//遍历链表
void list_show1(Linklist *L,msg p,struct sockaddr_in sin)
{
//判断逻辑
if(NULL == L || list_empty(L))
{
return;
}
//遍历逻辑
Linklist *q = L->next; //定义指针指向第一个结点
//puts("此时链表元素分别是:");
while(q != NULL)
{
//printf("%c\t",q->data);
if(memcmp(&q->data,&sin,sizeof(cin)) != 0)
{
if(sendto(sfd,&p,sizeof(p),0,(struct sockaddr *)&q->data,sizeof(q->data)) < 0)
{
ERR_MSG("sendto");
return ;
}
}
q = q->next;
}
//putchar(10);
}
//按值进行查找函数
int list_search_value(Linklist *L,struct sockaddr_in e){
//判断逻辑
if(NULL == L || list_empty(L)){
puts("查找失败");
return -1;
}
//查找逻辑
Linklist *q = L->next; //从第一个结点出发
for(int i=1;i<=L->len;i++){
if(memcmp(&q->data,&e,sizeof(cin)) == 0){
return i;
}
q = q->next;
}
return 0;
}
//任意位置删除
int list_delete_pos(Linklist *L,int pos){
//判断逻辑
if(NULL == L || list_empty(L) || pos<1 || pos>L->len){
puts("删除失败");
return 0;
}
//找到要删除结点的前驱
Linklist *q = find_node(L,pos-1);
if(NULL == q){
puts("删除失败");
return 0;
}
//删除操作
Linklist *p = q->next;
q->next = p->next;
free(p);
p = NULL;
//表长变化
L->len--;
puts("删除成功");
return 1;
}
//遍历链表
void list_show2(Linklist *L,msg p)
{
//判断逻辑
if(NULL == L || list_empty(L))
{
return;
}
//遍历逻辑
Linklist *q = L->next; //定义指针指向第一个结点
//puts("此时链表元素分别是:");
while(q != NULL)
{
//printf("%c\t",q->data);
if(sendto(sfd,&p,sizeof(p),0,(struct sockaddr *)&q->data,sizeof(q->data)) < 0)
{
ERR_MSG("sendto");
return ;
}
q = q->next;
}
//putchar(10);
}
//创建链表
Linklist *list_create(){
Linklist *L = (Linklist *)malloc(sizeof(Linklist));
if(NULL == L)
{
puts("创建失败");
return NULL;
}
//创建成功进行初始化
L->len = 0;
L->next = NULL;
puts("创建成功");
return L;
}
//判空函数
int list_empty(Linklist *L)
{
return L->next == NULL;
}
//按位置查找返回结点
Linklist *find_node(Linklist *L,int pos){
//判断逻辑
if(pos<0 || pos>L->len)
{
puts("查找失败");
return NULL;
}
//查找逻辑
Linklist *q=L; //定义指针从头结点出发
for(int i=0;i<pos;i++){
q = q->next;
}
return q; //将找到的结点返回
}
运行结果如下: