2014-04-03 Linux网络编程 - 基于UDP的多人群聊
/* server.c */
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
struct LinkNode //链表,储存连接服务器的所有客户端地址信息
{
struct sockaddr_in client_addr ;
struct LinkNode * next ;
}LinkNode;
typedef struct LinkNode* LinkList;
void init_list(LinkList * L) //初始化链表
{
(*L)=(LinkList)malloc(sizeof(LinkNode));
memset(&((*L)->client_addr), 0, sizeof(struct sockaddr_in)) ;
(*L)->next = NULL;
}
void add_client(LinkList L, struct sockaddr_in new_client_addr) //添加新地址
{
LinkList p = L ;
LinkList tail ;
tail = (LinkList)malloc(sizeof(LinkNode)) ;
memset(tail, 0, sizeof(LinkNode)) ;
while(p->next != NULL)
{
p = p->next ;
}
tail->client_addr = new_client_addr ; //可以结构体直接赋值
//tail->client_addr.sin_family = new_client_addr.sin_family ;
//tail->client_addr.sin_port =new_client_addr.sin_port ;
//tail->client_addr.sin_addr.s_addr = new_client_addr.sin_addr.s_addr ;
p->next = tail ;
tail->next = NULL ;
}
void del_client(LinkList L, struct sockaddr_in offline_client) //删除下线客户端地址
{
LinkList del, p = L ;
while(p->next != NULL)
{
if(offline_client.sin_addr.s_addr == p->next->client_addr.sin_addr.s_addr && offline_client.sin_port == p->next->client_addr.sin_port)
break ;
p = p->next ;
}
del = p->next ;
p->next = del->next ;
//free(del) ;
}
int main(int argc, char * argv[]) // ---> ./exe [port]
{
int server_fd, iret ;
char recv_buf[128], send_buf[128], message[128];
struct sockaddr_in server_addr, cli_addr ;
LinkList L, p;
init_list(&L) ;
//socket
server_fd = socket(AF_INET, SOCK_DGRAM, 0) ;
if(server_fd == -1)
{
perror("socket");
exit(-1) ;
}
//bind
memset(&server_addr, 0, sizeof(server_addr)) ;
server_addr.sin_family = AF_INET ;
server_addr.sin_port = htons(atoi(argv[1])) ;
server_addr.sin_addr.s_addr = INADDR_ANY ;
iret = bind(server_fd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) ;
if(iret == -1)
{
perror("bind") ;
exit(-1) ;
}
while(1)
{
//recvfrom
memset(&cli_addr, 0, sizeof(cli_addr)) ;
memset(recv_buf, 0, 128) ;
int len = sizeof(cli_addr) ;
recvfrom(server_fd, recv_buf, 128, 0, (struct sockaddr*)&cli_addr, &len) ;
//add_client
if(strcmp(recv_buf, "online\n") == 0) //有客户端上线时新增地址到链表
{
add_client(L, cli_addr) ;
printf("online!!!\n") ;
}
//del_client
else if(strcmp(recv_buf, "offline\n") == 0) //客户端下线时删除该地址
{
del_client(L, cli_addr) ;
printf("offline!!!\n") ;
}
//printf("receive from ip : %s, port : %d, message : %s\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), recv_buf) ; //测试服务器端是否收到消息
memset(message, 0, 128) ;
sprintf(message, "from ip : %s, port : %d\nmessage : %s\n",inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port), recv_buf) ; //给message添加发信客户端信息
//sendto
p = L ;
while(p->next != NULL) //向链表内存储的全部客户端地址发消息
{
p = p->next ;
strcpy(send_buf, message) ;
sendto(server_fd, send_buf, strlen(send_buf), 0, (struct sockaddr*)&(p->client_addr), sizeof(cli_addr)) ;
}
}
//close
close(server_fd) ;
return 0;
}
/* client.c */ //可以有多个客户端同时通过服务器收发消息
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <pthread.h>
void * thd_handle(void * arg) //子线程,负责接收消息
{
int client_fd = *(int*)arg ;
char recv_buf[128] ;
struct sockaddr_in server_addr ;
while(1)
{
//recvfrom
memset(&server_addr, 0, sizeof(server_addr)) ;
memset(recv_buf, 0, 128) ;
int len = sizeof(server_addr) ;
recvfrom(client_fd, recv_buf, 128, 0, (struct sockaddr*)&server_addr, &len) ;
printf("%s", recv_buf) ;
}
}
int main(int argc, char * argv[]) // ---> ./exe ip port
{
int client_fd ;
pthread_t thd ;
char send_buf[128] ;
struct sockaddr_in server_addr;
//socket
client_fd = socket(AF_INET, SOCK_DGRAM, 0) ;
thd = pthread_create(&thd, NULL, thd_handle, (void*)&client_fd) ; //创建收消息线程
if(client_fd == -1)
{
perror("socket") ;
exit(-1) ;
}
memset(&server_addr, 0, sizeof(server_addr)) ;
server_addr.sin_family = AF_INET ;
server_addr.sin_port = htons(atoi(argv[2])) ;
server_addr.sin_addr.s_addr = inet_addr(argv[1]) ;
//sendto (online)
memset(send_buf, 0, 128) ;
strcpy(send_buf, "online\n") ;
sendto(client_fd, send_buf, strlen(send_buf), 0, (struct sockaddr*)&server_addr, sizeof(server_addr)) ; //连接服务器时发送上线信息
while(1)
{
//sendto
memset(send_buf, 0, 128) ;
fgets(send_buf, 128, stdin) ;
sendto(client_fd, send_buf, strlen(send_buf), 0, (struct sockaddr*)&server_addr, sizeof(server_addr)) ;
if(strcmp(send_buf, "offline\n") == 0) //发送offline时下线退出
{
break ;
}
}
//close
close(client_fd) ;
return 0 ;
}