思维导图:
代码:
#include<myhead.h>
#define SER_IP "192.168.122.46"
#define SER_PORT 8888
typedef struct Node
{
struct sockaddr_in cin1;
char name[128];
struct Node *next;
}*linklist;
linklist create_node(struct sockaddr_in cin2)
{
linklist p=(linklist)malloc(sizeof(struct Node));
if(p==NULL)
return NULL;
p->next=NULL;
bzero(p->name,sizeof(p->name));
p->cin1=cin2;
return p;
}
linklist insert_rear(linklist head,char*a,struct socksddr_in cin2)
{
//创建新节点
linklist p=create_node();
if(p==NULL)
return head;
//传递名字
strcpy(p->name,a);
if(NULL==head)
{
head=p;
}
else
{
linklist end=head;
while(end->next!=NULL)
{
end=end->next;
}
}
end->next=p;
p->cin1=cin2;
return head;
}
linklist delete_any(linklist head,struct sockaddr_in *cin2)
{
linklist p =head;
if(p->cin1=*cin2)
{
free(p);
p=NULL;
}
else
{ while(p->next!=NULL)
{
if(p->next->cin1=*cin2)
{
linklist s=p->next;
p->next=s->next;
free(s);
s=NULL;
break;
}
p=p->next;
}
}
return head;
}
int main(int argc,char argv[])
{
if(argc!=3)
{
printf("请输入ip和端口号\n");
return -1;
}
//创建连接套接字
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd==-1)
{
perror("socket error");
return -1;
}
printf("sfd = %d\n", sfd);
struct sockaddr_in sin;
sin.sin_family = AF_INET;
//此处有问题
sin.sin_port = htons();
sin.sin_addr.s_addr = inet_addr(argv[1]);
//端口复用
int resue=-1;
resue=setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&resue,sizeof(resue));
if(resue==-1)
{
perror("setsockopt error");
return -1;
}
printf("端口号快速重用成功\n");
//绑定
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("bind error");
return -1;
}
printf("bind success:%s %s %d\n",__FILE__,__func__,__LINE__);
//监听
if(listen(sfd,128)==-1)
{
perror("listen error");
return -1;
}
printf("listen success:%s %s %d\n",__FILE__,__func__,__LINE__);
//定义存储客户端信息的结构体
struct sockaddr_in cin;
socklen_t socklen=sizeof(cin);
int newfd=-1;
fd_set readfds,tempfds;
FD_ZERO(&readfds);
FD_SET(0,&readfds);
FD_SET(sfd,&readfds);
int maxfd=sfd;
struct sockaddr_in cin_arr[1024];
//创建头指针
linklist head=NULL;
while(1)
{
tempfds=readfds;
int res=select(maxfd+1,&tempfds,NULL,NULL,NULL);
if(res==-1)
{
perror("select error");
return -1;
}
else if(res==0)
{
printf("timeout\n");
return -1;
}
for(int cli=0;cli<=maxfd;cli++)
{
if(FD_ISSET(cli,&tempfds)==0)
continue;
if(cli==sfd) //客户端连接上了服务器
{
newfd=accept(sfd,(struct sockaddr*)&cin,&socklen);
if(newfd==-1)
{
perror("accept error");
return -1;
}
printf("[%s:%d]发来链接请求 %s %s %d\n",\
inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),__FILE__,__func__,__LINE__);
cin_arr[newfd]=cin;
FD_SET(newfd,&readfds);
if(maxfd<newfd)
{
maxfd=newfd;
}
char buf[128]="";
bzero(buf,sizeof(buf));
int res=recv(cli,buf,sizeof(buf),0);
printf("%s:[%s,%d]:%s登陆成功\n",buf,inet_ntoa(cin_arr[cli].sin_addr),ntohs(cin_arr[cli].sin_port),buf);
//创建链表
//尾插
head=insert_rear(head,buf,cin);
}
else if(cli==0) //终端输入
{
char buf[128]="";
scanf(" %s",buf);
printf("触发终端输入\n");
if(strcmp(buf,"quit")==0)
goto END;
for(int i=4;i<maxfd;i++)
{
sendto(i,buf,sizeof(buf),0,(struct sockaddr*)&cin_arr[i],sizeof(cin_arr[i]));
}
}
else //客户端发送消息
{
char buf[128]="";
bzero(buf,sizeof(buf));
int res=recv(cli,buf,sizeof(buf),0);
if(res==0)
{
printf("客户端已下线\n");
close(cli);
FD_CLR(cli,&readfds);
for(int i=maxfd;i>=sfd;i--)
{
if(FD_ISSET(i,&readfds))
{
maxfd=i;
break;
}
}
//删除链表元素
head=delete_any(head,&cin_arr[i]);
continue;
}
//如果输入的是1,则用户名
printf("[%s,%d]:%s\n",inet_ntoa(cin_arr[cli].sin_addr),ntohs(cin_arr[cli].sin_port),buf);
strcat(buf,"*_*");
send(cli,buf,sizeof(buf),0);
printf("发送成功\n");
}
}
}
END:
close(sfd);
}
基于udp的服务器:
#include <myhead.h>
#define SER_IP "192.168.125.151"
#define SER_PORT 9999
typedef struct Msg
{
char user[32]; //用户名
int type; //执行操作1.登录、2.发消息、0.退出
char text[1024]; //消息内容
} msg_t;
typedef struct List
{
struct sockaddr_in cin; //客户端的网络信息结构体
struct List *next; //链表指针,指向下一个
} * list;
struct sockaddr_in cin;
//创建头节点
list list_create()
{
list p = (list)malloc(sizeof(struct List));
if (p == NULL)
{
perror("create list error");
}
p->next = NULL;
p = NULL;
}
//线程函数事件 向所有客户端发送消息
void *task(void *arg)
{
int *sockfd = (int *)arg;
msg_t msg;
strcpy(msg.user, "*system*");
while (1)
{
scanf("%s", msg.text);
getchar();
if (strncmp(msg.text, "quit", 4) == 0)
{
exit(0);
}
sendto(*sockfd, msg.text, sizeof(msg), 0, (struct sockaddr *)&cin, sizeof(cin));
}
}
//登录事件处理
void login(int sockfd, msg_t msg, list p, struct sockaddr_in cin)
{
//新客户端插入链表
list new = NULL;
new = (list)malloc(sizeof(struct List));
sprintf(msg.text, "login");
while (p->next != NULL)
{
//发送给的其他客户端登录消息
p = p->next;
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->cin), sizeof(p->cin));
printf("[%s:%d]:%s login\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), msg.user);
}
//新节点的数据域填充新客户端地址结构体,尾插
new->cin = cin;
p->next = new;
new->next = NULL;
}
//接收客户端消息事件处理
void chatmsg(int sockfd, msg_t msg, list p, struct sockaddr_in cin)
{
//将客户端发来的消息发送给其他客户端
while (p->next != NULL)
{
p = p->next;
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->cin), sizeof(p->cin));
}
}
//客户端退出消息事件处理
void quit(int sockfd, msg_t msg, list p, struct sockaddr_in cin)
{
list del = NULL;
sprintf(msg.text, "%s out", msg.user);
while (p->next != NULL)
{
//遍历链表找到要退出的客户端地址结构体的前一个
if (memcmp(&(p->next->cin), &cin, sizeof(cin)) == 0)
{
del = p->next;
p->next = del->next;
free(del);
del = NULL;
}
else
{
p = p->next;
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&(p->cin), sizeof(p->cin));
}
}
}
int main(int argc, char const *argv[])
{
msg_t msg;
//创建套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror("socket error");
return -1;
}
struct sockaddr_in sin;
//填充服务器地址结构体
sin.sin_family = AF_INET;
sin.sin_port = htons(SER_PORT);
sin.sin_addr.s_addr = inet_addr(SER_IP);
//绑定服务器
if (bind(sockfd, (struct sockaddr *)&sin, sizeof(sin)) == -1)
{
perror("bind error");
return -1;
}
//创建客户端地址结构体
struct sockaddr_in cin;
//获取客户端地址结构体大小
socklen_t socklen = sizeof(cin);
//创建链表节点
list p = list_create();
//创建线程
pthread_t tid;
if (pthread_create(&tid, NULL, task, &sockfd) == -1)
{
printf("pthread_create error\n");
return -1;
}
//分离线程
pthread_detach(tid);
//接收客户端消息
while (1)
{
//接收客户端发来的消息,返回消息字符个数
int res = recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&cin, &socklen);
if (res < 0)
{
perror("recvfrom error");
return -1;
}
//判断三种客户端状态 1登录 2消息 0退出
if (msg.type == 1)
{
login(sockfd, msg, p, cin);
}
else if (msg.type == 2)
{
chatmsg(sockfd, msg, p, cin);
}
else if (msg.type == 0)
{
printf("[%s:%d] %s out\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), msg.user);
quit(sockfd, msg, p, cin);
}
}
//关闭套接字
close(sockfd);
return 0;
}
客户端:
#include <myhead.h>
#define SER_IP "192.168.125.151"
#define SER_PORT 9999
typedef struct Msg
{
char user[32]; //用户名
int type; //执行操作1.登录、2.发消息、0.退出
char text[1024]; //消息内容
} msg_t;
int main(int argc, char const *argv[])
{
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror("sock error");
return -1;
}
msg_t msg;
struct sockaddr_in cin;
cin.sin_family = AF_INET;
cin.sin_addr.s_addr = inet_addr(SER_IP);
cin.sin_port = htons(SER_PORT);
socklen_t socklen = sizeof(cin);
char buf[128] = "";
msg.type = 1;
printf("please imput your name:");
scanf("%s", msg.user);
getchar();
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&cin, socklen);
pid_t pid = fork();
if (pid < 0)
{
perror("fork error");
return -1;
}
else if (pid == 0) //子进程循环发送消息
{
while (1)
{
printf("---------------------\n");
scanf("%s", msg.text);
getchar();
if (strncmp(msg.text, "quit", 4) == 0)
{
msg.type = 0;
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&cin, socklen);
kill(pid, SIGINT);
exit(0);
wait(NULL);
}
else
{
msg.type = 2;
}
sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&cin, socklen);
}
}
else //父进程循环接受消息
{
int res;
while (1)
{
res = recv(sockfd, &msg, sizeof(msg), 0);
if (res == -1)
{
perror("recv error");
return -1;
}
printf("[%s]:%s\n", msg.user, msg.text);
}
wait(NULL);
}
close(sockfd);
return 0;
}