之前在学习关于网络tcp和多线程的编程,学了知识以后不用一下总绝对心虚,于是就编写了一个基于tcp和多线程的多人聊天室。
具体的实现过程:
服务器端:绑定socket对象->设置监听数->等待连接->有客户端连接就新建一个线程,这个线程中,一旦就收到这个客户发送的消息,就广播的向其他客户端发送同样的消息。
客户端:向客户端连接->新建线程用来接收服务器端发送的消息,同时主进程用来发送消息
话不多说,直接上代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h> typedef struct sockaddr *sockaddrp; //存储客户端地址的结构体数组
struct sockaddr_in src_addr[];
socklen_t src_len = sizeof(src_addr[]); //连接后记录confd数组
int confd[] = {}; //设置连接人数
int count = ; void *broadcast(void *indexp)
{
int index = *(int *)indexp;
char buf_rcv[] = {};
char buf_snd[] = {};
//第一次读取用户姓名
char name[] = {};
int ret = recv(confd[index],name,sizeof(name),);
if( > ret)
{
perror("recv");
close(confd[index]);
return;
} while()
{
bzero(buf_rcv,sizeof(buf_rcv));
recv(confd[index],buf_rcv,sizeof(buf_rcv),); //判断是否退出
if( == strcmp("quit",buf_rcv))
{
sprintf(buf_snd,"%s已经退出悟空聊天室",name);
for(int i = ;i <= count;i++)
{
if(i == index || == confd[i])
{
continue;
} send(confd[i],buf_snd,strlen(buf_snd),);
}
confd[index] = -;
pthread_exit(); } sprintf(buf_snd,"%s:%s",name,buf_rcv);
printf("%s\n",buf_snd);
for(int i = ;i <= count;i++)
{
if(i == index || == confd[i])
{
continue;
} send(confd[i],buf_snd,sizeof(buf_snd),);
} } } int main(int argc,char **argv)
{
printf("悟空聊天室服务器端开始运行\n"); //创建通信对象
int sockfd = socket(AF_INET,SOCK_STREAM,);
if( > sockfd)
{
perror("socket");
return -;
} //准备地址
struct sockaddr_in addr = {AF_INET};
addr.sin_port = htons(atoi(argv[]));
addr.sin_addr.s_addr = inet_addr(argv[]); socklen_t addr_len = sizeof(addr); //绑定
int ret = bind(sockfd,(sockaddrp)&addr,addr_len);
if( > ret)
{
perror("bind");
return -;
} //设置最大排队数
listen(sockfd,); int index = ; while(count <= )
{
confd[count] = accept(sockfd,(sockaddrp)&src_addr[count],&src_len);
++count;
//保存此次客户端地址所在下标方便后续传入
index = count-; pthread_t tid;
int ret = pthread_create(&tid,NULL,broadcast,&index);
if( > ret)
{
perror("pthread_create");
return -;
} } }
server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <string.h> typedef struct sockaddr *sockaddrp;
int sockfd; void *recv_other(void *arg)
{
char buf[]= {};
while()
{
int ret = recv(sockfd,buf,sizeof(buf),);
if( > ret)
{
perror("recv");
return;
}
printf("%s\n",buf);
}
} int main(int argc,char **argv)
{
if( != argc)
{
perror("参数错误");
return -;
} //建立socket对象
sockfd = socket(AF_INET,SOCK_STREAM,);
if( > sockfd)
{
perror("socket");
return -;
} //准备连接地址
struct sockaddr_in addr = {AF_INET};
addr.sin_port = htons(atoi(argv[]));
addr.sin_addr.s_addr = inet_addr(argv[]); socklen_t addr_len = sizeof(addr); //连接
int ret = connect(sockfd,(sockaddrp)&addr,addr_len);
if( > ret)
{
perror("connect");
return -;
} //发送名字
char buf[] = {};
char name[] = {};
printf("请输入您的昵称:");
scanf("%s",name);
ret = send(sockfd,name,strlen(name),);
if( > ret)
{
perror("connect");
return -;
} //创建接收子线程
pthread_t tid;
ret = pthread_create(&tid,NULL,recv_other,NULL); if( > ret)
{
perror("pthread_create");
return -;
}
//循环发送
while()
{
//printf("%s:",name);
scanf("%s",buf);
int ret = send(sockfd,buf,strlen(buf),);
if( > ret)
{
perror("send");
return -;
} //输入quit退出
if( == strcmp("quit",buf))
{
printf("%s,您已经退出了悟空聊天室\n",name);
return ;
} } }
client.c
将两份代码分别编译生成相应可执行文件,例如在Linux下server,client,然后先执行./server 端口号 ip ,再执行./client 端口号 ip就可以运行这个聊天室了。
总结:关于网络编程,tcp是一种连接方式的通信方式,两边一旦建立连接,就可以通过send和recv函数发送消息,比较的可靠,缺点是速度比较慢(相对于udp来说)。另外关于多线程编程方面,线程其实是一个进程的实体,是一个进程的组成部分,多个线程共享除了栈区以外的大部分区域,因此进程间的通信比较方便,这种方便带来的代价是,当多个进程同时去操作同一量时,容易造成不可预知的错误,因此就引入了互斥量(锁)的概念,互斥量的使用就保证了进程间通信的同步。