/*
TCP聊天室 客戶端
*/
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<signal.h>
#include<netinet/in.h>
//一些準備工作
char* IP = "172.30.11.104";//127.0.0.1 本機地址,回送地址,用於網絡軟件測試 或本機通信
short PORT = 10222; // 端口
typedef struct sockaddr SA; //用做類型轉換
int sockfd= 0;
char name[20] = {};
//1 啓動客戶端,連接服務器
void init()
{
printf("聊天室客戶端開始啓動。。\n");
sockfd = socket(PF_INET,SOCK_STREAM,0);
struct sockaddr_in addr;
addr.sin_family = PF_INET;
addr.sin_port = htons(PORT);
addr.sin_family.s_addr = inet_addr(IP);
//連接
if(connect(sockfd,(SA*)&addr,sizeof(addr)) ==-1)
{
perror("無法連接服務器");
printf("客戶端啓動失敗\n");
exit(-1);
}
printf("客戶端啓動成功\n");
}
//2 開始通信
void start()
{
//主要是用來發送消息
//發消息之前,啓動一個線程 用來接收消息
pthread_t pid;
void* recv_thread(void* );
pthread_create(&pid,0,recv_thread,0);
while(1)
{
char buf[100] = {};
scanf("%s",buf);//讀取客戶的輸入
char msg[200] = {};
sprintf(msg,"%s 說:%s",name,buf);
send(sockfd,msg,strlen(msg),0);
}
}
//線程函數 ,用來接收消息
void* recv_thread(void* p)
{
char buf[200] = {};
if(recv(sockfd,buf,sizeof(buf),0)<=0)
{
return ; //結束線程
}
printf("%s\n",buf);
}
void sig_close(int signo)
{//關閉客戶端socket
close(sockfd);
exit(0);
}
int main()
{
signal(SIGINT,sig_close);
printf("請輸入暱稱:\n");
scanf("%s",name);
init(); //啓動客戶端 連接到服務器
send(sockfd,name,strlen(name),0);
start();
}
/*
TCP聊天室服務器
*/
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<signal.h>
#include<netinet/in.h>
//一些準備工作
char* IP = "172.30.11.104";//127.0.0.1 本機地址,回送地址,用於網絡軟件測試 或本機通信
short PORT = 10222; // 端口
typedef struct sockaddr SA; //用做類型轉換
struct client
{
char name[20];//記錄客戶端的暱稱
int fds; //客戶端的socket描述符
};
struct client c[100] = {0};//記錄客戶端的結構體數組 最多記錄了100個
int size = 0; //記錄客戶端的個數,也可以用來遍歷客戶端結構體
int sockfd = 0; //服務器的sockt
//1 初始化服務器的網絡,創建socket
void init()
{
printf("聊天室服務器開始啓動……\n");
sockfd = socket(PF_INET,SOCK_STREAM,0);
if(sockfd == -1) perror("socket創建失敗,服務器啓動失敗"),exit(-1);
struct sockaddr_in addr;
addr.sin_family = PF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = inet_addr(IP);
if(bind(sockfd,(SA*)&addr,sizeof(addr))==-1)
{
perror("綁定失敗");
printf("服務器啓動失敗\n");
exit(-1);
}
printf("綁定成功\n");
if(listen(sockfd,100) == -1)
{
perror("設置監聽失敗");
printf("服務器啓動失敗\n");
exit(-1);
}
printf("設置監聽成功\n");
//等待客戶端連接到另一個函數中
printf("初始化服務器成功\n");
}
/*
輔助函數,用來分發消息
*/
void sendMsgToAll(char* msg)
{
int i = 0;
for( i = 0;i<size;i++)
{
printf("sendto %d\n",c[i].fds);
send(c[i].fds,msg,strlen(msg),0);
}
}
//線程函數,用來接收客戶端發來的消息,並且把消息發送給所有客戶端
void* service_thread(void* p)
{
int fd = *(int*)p; //拿到客戶端的socket描述符
printf("pthread = %d",fd); //線程對應客戶端
//保留客戶端的信息
c[size].fds = fd;
char name[20] = {};
if(recv(fd,name,sizeof(name),0)>0) //接收暱稱
{
strcpy(c[size].name,name);
}
size++;
//先羣發一條消息,表示歡迎 也可以通知其他客戶端有人連接了 進入聊天室
char tishi[100];
sprintf(tishi,"熱烈歡迎%s登陸聊天室。。",name);
//羣發消息
sendMsgToAll(tishi);
//通信部分,收發消息
while(1)
{
char buf[200] = {};
if(recv(fd,buf,sizeof(buf),0) == 0)
{
printf("fd = %d quit\n",fd);
//有客戶端斷開
int i;
char name[20] = {};
for(i= 0;i<size;i++)
{
if(c[i].fds == fd)
{
strcpy(name,c[i].name);
c[i].fds = c[size-1].fds;
strcpy(c[i].name,c[size-1].name);
}
}
size--;
printf("quit->fd=%d\n",fd);
char msg[100] = {};
sprintf(msg,"歡送 %s 離開聊天室,再見\n",name);
sendMsgToAll(msg);
close(fd);
return ; //客戶端退出 結束線程
}
sendMsgToAll(buf); //成功接收到消息,就直接所有的客戶端
}
}
//等待客戶端連接,啓動服務器服務
void service()
{
printf("服務器開始服務\n");
while(1)
{
struct sockaddr_in fromaddr; //存儲客戶端的通信地址
socklen_t len = sizeof(fromaddr);
int fd = accept(sockfd,(SA*)&fromaddr,&len);
if(fd == -1)
{
printf("客戶端連接出錯\n");
continue;
}
//如果客戶成功連接
//開啓線程,爲該客戶端進行服務
printf("fd =%d\n",fd);
pthread_t pid;
pthread_create(&pid,0,service_thread,&fd);
}
}
void sig_close(int signo)
{ //關閉socket
int i = 0;
for(;i<size;i++)
{
if(c[i].fds!=0)
close(c[i].fds);
}
close(sockfd);//服務器的socket;
printf("服務器已經關閉\n");
exit(0);
}
int main()
{
signal(SIGINT,sig_close);//做好善後工作
init();
service();
return 0;
}