服务器
流程:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <stdlib.h>
#define PORT 5555
#define IP "172.17.94.145"
#define ERR_MSG(msg) do{\
fprintf(stderr,"LINE:__%d__",__LINE__);\
perror(msg);\
}while(0)
//存储客户端发来的信息结构体
typedef struct cli_msg{
char type;
char name[20];
char info[128];
}msgcli;
//存储登录信息结构体,以链表方式存储
typedef struct logcin_msg{
union{
struct sockaddr_in logcin;//登录的客户端
int len;
};
struct logcin_msg* next;
}seqlist;
//创建链表的函数
seqlist *CreatSeqlist()
{
seqlist *L = (seqlist*)malloc(sizeof(seqlist));
if(NULL == L)
{
printf("set seqlist fail\n");
return NULL;
}
L->len = 0;
L->next = NULL;
return L;
}
//创建新结点函数
seqlist* Newnode()
{
seqlist *L = CreatSeqlist();
if(NULL == L)
{
printf("newnode fail\n");
return NULL;
}
L->next = NULL;
return L;
}
//登录函数
int do_logcin(struct sockaddr_in sendaddr,msgcli buf,int fd,seqlist *L)
{
seqlist *p = L;
printf("%s上线.....\n",buf.name);
while(NULL != p->next)
{
p=p->next;
if(-1 == sendto(fd,buf,sizeof(buf),0,(struct sockaddr*)&sendaddr,sizeof(sendaddr)))
{
ERR_MSG("sendto");
return -1;
}
}
//将登录的用户的地址信息结构体存储到链表中
seqlist *q = Newnode();//创建新的结点
if(NULL == q)
{
ERR_MSG("Newnode");
return -1;
}
//插入链表
q->next = L->next;
L->next = q;
L->len++;
//遍历链表
showline(L);
return 0;
}
//聊天函数
int do_chat(struct sockaddr_in sendaddr,msgcli buf,int fd,seqlist *L)
{
seqlist* p = L;
printf("%s:%s\n",buf.name,buf.info);
while(NULL != p->next)
{
p=p->next;
if(p->logcin.sin_port != sendaddr.sin_port)//跳过自己
{
if(-1 == sendto(fd,buf,sizeof(buf),0,\
(struct sockaddr*)&sendaddr,sizeof(sendaddr)))
{
ERR_MSG("sendto");
return -1;
}
}
}
return 0;
}
//下线函数
int do_quit(struct sockaddr_in sendaddr,msgcli buf,int fd,seqlist *L)
{
seqlist *p = L;
while(NULL != p->next)
{
seqlist* fort = p;
p=p->next;
if(p->logcin.sin_port == sendaddr.sin_port)
{
fort->next = p->next;
free(p);
}
}
//向客户端发送下线信息
seqlist* q = L;
while(NULL != q->next)
{
q=q->next;
if(-1 == sendto(fd,buf,sizeof(buf),0,\
(struct sockaddr*)&sendaddr,sizeof(sendaddr)))
{
ERR_MSG("sendto");
return -1;
}
}
showline(L);
return 0;
}
//遍历链表
void showline(seqlist *L)
{
seqlist* p = L;
for(int i=0;i<L->len;i++)
{
p=p->next;
printf("%d\n",p->logcin.sin_port);
}
}
int main(int argc, const char *argv[])
{
//创建报式套接字
int fd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == fd)
{
ERR_MSG("socket");
return -1;
}
printf("socket success");
//填充服务器网络信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port =htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
//绑定套接字和网络信息结构体(必须)
if(-1 == bind(fd,(struct sockaddr*)&sin,sizeof(sin)))
{
ERR_MSG("bind");
return -1;
}
printf("bind success\n");
//新建一个网络信息结构体来存储发送端的地址信息
struct sockaddr_in sendaddr;
int sendlen = sizeof(sendaddr);
//创建一个进程,在父进程中接收数据,在子进程中发送数据
char buf[128] = "";
ssize_t res = 0;
pid_t cpid = fork();
if(cpid > 0) //父进程接受数据
{
msgcli recvbuf;
while(1)
{
bzero(buf,sizeof(buf));
res = recvfrom(fd,buf,sizeof(buf),0,\
(struct sockaddr*)&sendaddr,*sendlen);
if(-1 == res)
{
ERR_MSG("recvfrom");
return -1;
}
printf("recvfrom success\n");
printf("[%s:%d] msg:%s",inet_ntoa(sendaddr.sin_addr),\
ntohs(sendaddr.sin_port),buf);
switch(recvbuf.type)
{
case 'L':
do_logcin(sendaddr,recvbuf,L,fd);
break;
case 'C':
do_chat(sendaddr,recvbuf,L,fd);
break;
case 'Q':
do_quit(sendaddr,recvbuf,L,fd);
break;
default:
printf("输入错误\n");
break;
}
}
}
else if(0 == cpid)//子进程发送数据
{
while(1)
{
bzero(buf,sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf) - 1] = 0;
res = sendto(fd,buf,sizeof(buf),0,\
(struct sockaddr*)&sendaddr,sendlen);
if(-1 == res)
{
ERR_MSG("sendto");
return -1;
}
printf("sendto success\n");
}
}
else
{
ERR_MSG("fork");
return -1;
}
//关闭所有文件描述符
close(fd);
return 0;
}
客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <unistd.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 5555
#define IP "172.17.94.145"
#define ERR_MSG(msg) do{\
fprintf(stderr,"LINE:__ %d __",__LINE__);\
perror(msg);\
}while(0)
struct climsg{
char type;
char name[20];
char info[128];
};
int main(int argc, const char *argv[])
{
int fd = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == fd)
{
ERR_MSG("socket");
return -1;
}
//填充网络信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
//bind可绑可不绑
printf("请输入您的账户名>>>>>>>");
char name[20] = "";
scanf("%s",name);
getchar();
//发送登录请求
struct climsg sendbuf;
sendbuf.type = 'L';
strcpy(sendbuf.name,name);
if(-1 == sendto(fd,&sendbuf,sizeof(sendbuf),0,\
(struct sockaddr*)&sin,sizeof(sin)))
{
ERR_MSG("sendto");
return -1;
}
printf("登录请求发送成功");
//新建一个网络信息结构体存储数据包是从哪里来
struct stockaddr_in recvaddr;
socklen_t addrlen = sizeof(recvaddr);
ssize_t res = 0;
pid_t cpid = fork();
if(cpid > 0)//父进程接受数据系统消息和登录消息
{
while(1)
{
res = recvfrom(fd,sendbuf,sizeof(sendbuf),0,\
(struct sockaddr*)&recvaddr,&addrlen);
if(res < 0)
{
ERR_MSG("recvfrom");
return -1;
}
//接受登录消息
switch(sendbuf.type)
{
case 'L':
printf("%s上线...\n",sendbuf.name);
break;
case 'C':
printf("%s:%s\n",sendbuf.name,sendbuf.info);
break;
case 'Q':
printf("%s已下线......\n",sendbuf.name);
break;
default :
printf("协议错误\n");
break;
}
}
}
else if(0 == cpid)//子进程发送聊天数据
{
while(1)
{
sendbuf.type = 'C';
bzero(sendbuf.info,sizeof(sendbuf.info));
fgets(sendbuf.info,sizeof(sendbuf.info),stdin);
sendbuf.info[strlen(sendbuf.info)-1]=0;
//如果发送quit表示下线
if(0 == strcmp(sendbuf.info,"quit"))
{
sendbuf.type = 'Q';
//发送退出请求
if(-1 == sendto(fd,sendbuf,sizeof(sendbuf),0,\
(struct sockaddr*)&recvaddr,addrlen))
{
ERR_MSG("sendto");
return -1;
}
}
else//如果发送内容不是quit
{
if(-1 == sendto(fd,&sendbuf,sizeof(sendbuf),0,\
(struct sockaddr*)&recvaddr,addrlen))
{
ERR_MSG("sendto");
return -1;
}
}
}
}
else
{
ERR_MSG("fork");
return -1;
}
close(fd);
return 0;
}
ubuntu@ubuntu:~/kyy/network/relearn$