C语⾔实现基于TCP的多线程聊天室基于TCP的服务器/客户机的模型
1.基本特征
⾯向连接的,可靠的,保证数据完整性和有序性
每个发送都有应答,若在时间窗⼝内没有收到A的应答,则从A开始重新发送。
编程模型
三次握⼿四次分⼿
服务器的思路是等待客户端的连接,并且实时接收每个连接上来了的客户端发送的消息,并将这个消息包打包发送给其他客户端。
1.创建套接字
int sfd =socket(AF_INET,SOCK_STREAM,0);
//AF_INET: 基于TCP/IPv4(32位IP地址)的⽹络通信;
//SOCKET_STREAM:数据流协议,即TCP协议;
if(sfd ==-1){
perror("socket");
return-1;
}
2.准备通信地址
struct sockaddr_in addr;
/*
struct sockaddr_in
19.{
20. // 地址族
21. sa_family_t sin_family;
22.
23. // 端⼝号
24. // unsigned short, 0-65535
25. // 逻辑上表⽰⼀个参与通信的进程
26. // 使⽤时需要转成⽹络字节序
27. // 0-1024端⼝⼀般被系统占⽤
28. // 如:21-FTP、23-Telnet、80-WWW
29. in_port_t sin_port;
30.
31. // IP地址
32. struct in_addr sin_addr;
33.};
*/
addr.sin_family = AF_INET;
addr.sin_port =htons(port);
addr.sin_addr.s_addr =inet_addr(ip);
socketlen_t addrlen =sizeof(addr);
3.绑定套接字
int ret =bind(sfd,(const struct sockaddr*)(&addr),addrlen);
if(ret ==-1){
perror("bind");
return-1;
}
4.监听套接字
将sockfd参数所标识的套接字标记为被动模式,使之可⽤于接受连接请求。
if(listen(sfd,10)==-1){
//10表⽰最⼤的客户端排队数为10
perror("listen");
return-1;
}
5.接受连接
⾃定义客户端类型
typedef struct Client{
int cfd;
char name[40];
}Client;
Client client[MAX]={};
size_t cnt =0;
对线程互斥锁初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
while(1){
struct sockaddr_in caddr;
socklen_t clen =sizeof(caddr);
printf("等待客户机连接...\n");
int cfd =accept(sfd,(struct sockaddr*)(&caddr),&clen);
/*从sockfd参数所标识套接字的未决连接请求队列中,提取第⼀个连接请求。
同时创建⼀个新的套接字,⽤于在该连接中通信,返回该套接字的描述符。
caddr和clen参数⽤于输出连接请求发起者的地址信息。
*/
if(cfd ==-1){
perror("accept");
return-1;
}
//向其他客户端发送上线消息
char buf[100]={};
//把客户端的⽹名和套接字放在客户端的类型中
recv(cfd,&client[cnt].name,40,0);
client[cnt].cfd = cfd;
strcpy(buf,"您的好友");
strcat(buf,client[cnt].name);
strcat(buf,"上线啦!");
/
/⼴播消息
broadcast(buf,client[cnt]);
pthread_t id;
ret =pthread_create(&id,NULL,pthread_run,(void*)(&client[cnt]));
//每当有⼀个客户机连接上来,变创建⼀个线程,该线程⽤于接收这个客户机的消息,并将这些消息加⼯⼴播给其他连接上来的客户机。 cnt++;
if(ret !=0){
printf("pthread_create:%s\n",strerror(ret));
continue;
}
printf("有⼀个客户机成功连接:ip <%s> port [%hu]\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));socket编程聊天室基本流程
}
6.线程处理函数
实现消息的接收处理转发功能
void*pthread_run(void*arg){
Client cl =*(Client*)(arg);
while(1){
char buf[1024]={};
strcpy(buf,cl.name);
strcat(buf," :");
int ret =recv(cl.cfd,buf+strlen(buf),1024-strlen(buf),0);
//j接收消息
if(ret <=0){
size_t i;
for(i=0;i<cnt;i++){
if(client[i].cfd == cl.cfd){
client[i]= client[cnt-1];
cnt--;
strcpy(buf,"您的好友");
strcat(buf,cl.name);
strcat(buf,"退出了!");
break;
}
}
broadcast(buf,cl);
return NULL;
}else{
broadcast(buf,cl);
}
}
}
7.⼴播函数
void broadcast(char*msg,Client c){
size_t i;
pthread_mutex_lock(&mutex);
for(i=0;i<cnt;i++){
if(client[i].cfd != c.cfd){
if(send(client[i].cfd,msg,strlen(msg),0)<=0){
break;
}
}
}
pthread_mutex_unlock(&mutex);
}
8.服务器源码
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#define MAX 100
typedef struct Client{
int cfd;
char name[40];
}Client;
Client client[MAX]={};
size_t cnt =0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void broadcast(char*msg,Client c){
size_t i;
pthread_mutex_lock(&mutex);
for(i=0;i<cnt;i++){
if(client[i].cfd != c.cfd){
if(send(client[i].cfd,msg,strlen(msg),0)<=0){ break;
}
}
}
pthread_mutex_unlock(&mutex);
}
void*pthread_run(void*arg){
Client cl =*(Client*)(arg);
while(1){
char buf[1024]={};
strcpy(buf,cl.name);
strcat(buf," :");
int ret =recv(cl.cfd,buf+strlen(buf),1024-strlen(buf),0); if(ret <=0){
size_t i;
for(i=0;i<cnt;i++){
if(client[i].cfd == cl.cfd){
client[i]= client[cnt-1];
--cnt;
strcpy(buf,"您的好友");
strcat(buf,cl.name);
strcat(buf,"退出了");
break;
}
}
broadcast(buf,cl);
close(cl.cfd);
return NULL;
}else{
broadcast(buf,cl);
}
}
}
int main(int argc,char*argv[]){
if(argc !=3){
fprintf(stderr,"use: %s <ip> [port]\n",argv[0]);
return-1;
}
const char*ip = argv[1];
unsigned short int port =atoi(argv[2]);
int sfd =socket(AF_INET,SOCK_STREAM,0);
if(sfd ==-1){
perror("socket");
return-1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port =htons(port);
addr.sin_addr.s_addr =inet_addr(ip);
socklen_t addrlen =sizeof(addr);
int ret =bind(sfd,(struct sockaddr*)(&addr),addrlen); if(ret ==-1){
perror("bind");
return-1;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论