Linux系统 + Gcc + Gdb + makefile
实现局域网OICQ程序设计,包括客户端和服务端。
客户端描述:客户端运行开始出现登陆界面。与服务端进行连接,连接后把账号信息发送给服务端,服务端验证后,把确认结果通知客户端。如果通过验证,客户端从服务端接收其他在线客户端信息并把这些客户信息显示给用户。用户可以选择客户并与之进行信息交流。即发送消息和接受消息。并把结果显示给用户。
服务端功能描述:服务端启动后,等待客户端连接。接受客户端发送过来的账号信息。进行验证。并把验证的结果返回给客户端。如果验证通过,记录客户端的信息。并把该客户端的记录的信息发送给其他的客户端,也把其他的在线用户发送给该用户,实现整个网络内的在线客户信息的同步。接受客户端的断开连接的请求。服务端断开连接,删除记录并把结果发送给其他的客户端。
Sock编程
根据配置文件信息启动服务端程序,监听端口,等待客户端连接。完成客户端于服务端简单的tcp连接。使用I/O复用机制完成客户端与服务端之间的一对多的连接。服务端记录每个客户端的基本信息:每个客户端的IP、端口等基本信息。使用链表记录保存这些信息。
相关代码参考
客户端:
#include
typedef struct
{
char type;
char name[20];
char text[128];
}msg_t;
//定义传送消息的结构体
int main(int argc, const char *argv[])
{
if(argc != 3)
{
printf("请输入服务器IP和端口号!\n");
return -1;
}
//提醒输入IP和端口号
int cfd = socket(AF_INET,SOCK_DGRAM,0);
if(cfd == -1)
{
perror("socket error");
return -1;
}
//创建套接字用于通信
msg_t msg;
char name[20] = "";
printf("请输入用户名>>");
scanf("%s",msg.name);
getchar();
//输入名字
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(atoi(argv[2]));
sin.sin_addr.s_addr = inet_addr(argv[1]);
//定义发送的服务器结构体
char buf[129] = "";
char rbuf[128] = "";
bzero(buf,sizeof(buf));
msg.type = 'L';
sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sizeof(sin));
//发送登录信息结构体
struct pollfd fds[2];
fds[0].fd = 0;
fds[0].events = POLLIN;
fds[1].fd = cfd;
fds[1].events = POLLIN;
int res = 0;
//用poll函数多路复用
while(1)
{
res = poll(fds,2,-1);
if(res == -1)
{
perror("poll error");
return -1;
}
else if(res == 0)
{
printf("time out\n");
return -1;
}
bzero(buf,sizeof(buf));
bzero(rbuf,sizeof(rbuf));
if(fds[1].revents == POLLIN)
{
recvfrom(cfd,rbuf,sizeof(rbuf),0,NULL,NULL);
printf("%s\n",rbuf);
}
if(fds[0].revents == POLLIN)
{
fgets(msg.text,sizeof(msg.text),stdin);
msg.text[strlen(msg.text)-1]='\0';
if(strcmp(msg.text,"quit")==0)
{
msg.type = 'Q';
sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sizeof(sin));
goto A;
}
msg.type = 'C';
sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sizeof(sin));
}
}
A:
close(cfd);
return 0;
}
服务端:
#include
typedef struct group
{
char type;
char name[20];
char text[128];
}msg_t;
//创建信息结构体
typedef struct Node
{
int PORT;
struct Node* next;
}*Linklist;
//创建链表数据域的结构体
Linklist create_node()
{
Linklist s=(Linklist)malloc(sizeof(struct Node));
if(NULL == s)
return NULL;
s->PORT =0;
s->next =NULL;
return s;
}
//创建链表节点
Linklist insert_rear(Linklist head,int element)
{
Linklist s=create_node();
s->PORT=element;
if(NULL == head)
{
head = s;
return head;
}
Linklist p = head;
while(p->next != NULL)
{
p=p->next;
}
p->next = s;
return head;
}
//链表的头删
int lenth(Linklist head)
{
if(head == NULL)
return 0;
int count=0;
Linklist p=head;
while(p!=NULL)
{
count++;
p=p->next;
}
free(p);
p=NULL;
return count;
}
//链表求长度
int find_element(Linklist head,int element)
{
Linklist p=head;
for(int i=0;iPORT == element)
return i;
p=p->next;
}
}
//链表的按照元素查找
Linklist link_del_head(Linklist head)
{
if(head->next == NULL)
{
free(head);head=NULL;
return head;
}
Linklist del=head->next;
head->PORT=del->PORT;
head->next=del->next;
free(del);del=NULL;
return head;
}
//链表的头删
Linklist link_del_rear(Linklist head)
{
if(head->next == NULL)
{
free(head);
head = NULL;
return head;
}
Linklist del=head;
while(del->next->next!=NULL)
{
del=del->next;
}
free(del->next);
del->next=NULL;
return head;
}
//链表的尾删
Linklist link_del_pos(Linklist head,int pos)
{
if(pos == lenth(head)-1)
{
head = link_del_rear(head);
return head;
}
else if(pos == 0)
{
head = link_del_head(head);
return head;
}
else
{
Linklist p=head;
for(int i=0;inext;
}
Linklist r=p->next;
p->next=r->next;
free(r);
r=NULL;
return head;
}
}
//链表的按照信息删除
Linklist del(Linklist head,int element)
{
if(head ==NULL)
return head;
int pos = find_element(head,element);
head = link_del_pos(head,pos);
return head;
}
//链表按照位置删除
int main(int argc, const char *argv[])
{
if(argc != 3)
{
printf("请输入服务器IP和端口号!\n");
return -1;
}
int sfd = socket(AF_INET,SOCK_DGRAM,0);
if(sfd == -1)
{
perror("socket error");
return -1;
}
//创建套接字用于通信
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(atoi(argv[2]));
sin.sin_addr.s_addr = inet_addr(argv[1]);
//定义服务器的信息结构体
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("bind error");
return -1;
}
printf("bind success\n");
struct sockaddr_in cin;
cin.sin_family = AF_INET;
socklen_t socklen = sizeof(cin);
Linklist Usr_PORT=NULL;
msg_t usr;
char buf[149] = "";
char rbuf[130] = "";
//根据poll函数IO多路复用
struct pollfd fds[2];
fds[0].fd = 0;
fds[0].events = POLLIN;
fds[1].fd = sfd;
fds[1].events = POLLIN;
int res = 0; //接收select的返回值
while(1)
{
res = poll(fds,2,-1);
if(res == -1)
{
perror("poll error");
return -1;
}
else if(res == 0)
{
printf("time out");
return -1;
}
bzero(buf,sizeof(buf));
if(fds[0].revents == POLLIN)
{
strcpy(buf,"SYSMSG:");
fgets(buf+7,sizeof(buf)-7,stdin);
buf[strlen(buf)-1] = '\0';
Linklist p = Usr_PORT;
while(p!= NULL)
{
cin.sin_port = htons(p->PORT);
sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,sizeof(cin));
p=p->next;
}
}
if(fds[1].revents == POLLIN)
{
recvfrom(sfd,&usr,sizeof(usr),0,(struct sockaddr*)&cin,&socklen);
if(usr.type == 'L')
{
Usr_PORT = insert_rear(Usr_PORT,ntohs(cin.sin_port));
printf("[%s:%d]已经上线\n",usr.name,ntohs(cin.sin_port));
sprintf(buf,"%s已经上线",usr.name);
printf("buf = %s\n",buf);
Linklist p = Usr_PORT;
while(p->next!= NULL)
{
cin.sin_port = htons(p->PORT);
sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,sizeof(cin));
p=p->next;
}
}
else if(usr.type == 'C')
{
sprintf(buf,"%s:%s",usr.name,usr.text);
Linklist p = Usr_PORT;
int NONE=ntohs(cin.sin_port);
while(p!= NULL)
{
if(NONE!=p->PORT)
{
cin.sin_port = htons(p->PORT);
sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(cin),sizeof(cin));
}
p=p->next;
}
}
else if(usr.type == 'Q')
{
sprintf(buf,"%sdownline",usr.name);
printf("[%s:%d]downline\n",usr.name,ntohs(cin.sin_port));
Usr_PORT = del(Usr_PORT,ntohs(cin.sin_port));
Linklist p = Usr_PORT;
while(p!= NULL)
{
cin.sin_port = htons(p->PORT);
sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&cin,sizeof(cin));
p=p->next;
}
}
}
}
close(sfd);
return 0;
}