本节通过socket实现一个简单的聊天室功能
聊天室中如果有人说话,则服务器负责将内容传送给聊天室的其他人
那么就需要客户端和服务端两个程序,客户端负责发送消息,服务端负责接收和转发消息
客户端代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MYPORT 8606
#define BUFFER_SIZE 1024
int main()
{
int sock_cli;
fd_set rfds;
struct timeval tv;
int retval, maxfd;
///定义sockfd
sock_cli = socket(AF_INET,SOCK_STREAM, 0);
///定义sockaddr_in
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(MYPORT); ///服务器端口
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); ///服务器ip
//连接服务器,成功返回0,错误返回-1
if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
{
perror("connect");
exit(1);
}
while(1){
/*把可读文件描述符的集合清空*/
FD_ZERO(&rfds);
/*把标准输入的文件描述符加入到集合中*/
FD_SET(0, &rfds);
maxfd = 0;
/*把当前连接的文件描述符加入到集合中*/
FD_SET(sock_cli, &rfds);
/*找出文件描述符集合中最大的文件描述符*/
if(maxfd < sock_cli)
maxfd = sock_cli;
/*设置超时时间*/
tv.tv_sec = 10;
tv.tv_usec = 0;
/*等待聊天*/
retval = select(maxfd+1, &rfds, NULL, NULL, &tv);
if(retval == -1){
printf("select出错,客户端程序退出\n");
break;
}else if(retval == 0){
//printf("客户端没有任何输入信息,并且服务器也没有信息到来,waiting...\n");
continue;
}else{
/*服务器发来了消息*/
if(FD_ISSET(sock_cli,&rfds)){
char recvbuf[BUFFER_SIZE];
int len;
len = recv(sock_cli, recvbuf, sizeof(recvbuf),0);
if(len > 0)
{
printf("%s",recvbuf);
}
memset(recvbuf, 0, sizeof(recvbuf));
}
/*用户输入信息了,开始处理信息并发送*/
if(FD_ISSET(0, &rfds)){
char sendbuf[BUFFER_SIZE];
fgets(sendbuf, sizeof(sendbuf), stdin);
send(sock_cli, sendbuf, strlen(sendbuf),0); //发送
memset(sendbuf, 0, sizeof(sendbuf));
}
}
}
close(sock_cli);
return 0;
}
服务端代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 8606
#define IP "127.0.0.1"
int mSocket;
struct sockaddr_in servaddr;
socklen_t len;
std::list connList;//用list来存放套接字,没有限制套接字的容量就可以实现一个server跟若干个client通信
void sendMsg(int sender,char* msg,int length)
{
char buf[1024];
memset(buf, 0 ,sizeof(buf));
sprintf(buf,"玩家%d:%s",sender ,msg);
std::list::iterator it;
for(it=connList.begin(); it!=connList.end(); ++it)
{
//给其他人发送消息
if (sender != *it)
{
send(*it, buf, strlen(buf), 0);
}
}
}
void getConn()
{
while(1)
{
int conn = accept(mSocket, (struct sockaddr*)&servaddr, &len);
connList.push_back(conn);
//printf("%d\n", conn);
printf("玩家 %d 进入房间 \n",conn);
}
}
void getData()
{
struct timeval tv;
tv.tv_sec = 10;//设置倒计时时间
tv.tv_usec = 0;
while(1)
{
std::list::iterator it;
for(it=connList.begin(); it!=connList.end(); ++it)
{
fd_set rfds;
FD_ZERO(&rfds);
int maxfd = 0;
int retval = 0;
FD_SET(*it, &rfds);
if(maxfd < *it)
{
maxfd = *it;
}
retval = select(maxfd+1, &rfds, NULL, NULL, &tv);
if(retval == -1)
{
printf("select error\n");
}
else if(retval == 0)
{
//printf("not message\n");
}
else
{
char buf[1024];
memset(buf, 0 ,sizeof(buf));
int len = recv(*it, buf, sizeof(buf), 0);
if (len > 0) {
printf("收到 %d的消息 %s", *it ,buf);
sendMsg(*it, buf, len);
}
}
}
//sleep(1);
}
}
int main()
{
//new socket
std::cout<<"1.创建socket"<> accpet
std::thread t(getConn);
t.detach();//detach的话后面的线程不同等前面的进程完成后才能进行,如果这里改为join则前面的线程无法判断结束,就会
//一直等待,导致后面的线程无法进行就无法实现操作
//printf("done\n");
//thread : recv ==>> show
std::thread t2(getData);
t2.detach();
while(1)//做一个死循环使得主线程不会提前退出
{
}
return 0;
}
首先启动服务器程序;
这里由于有多个客户端,需要找到Xcode编译出来的可运行文件
鼠标右键->选择show in finder,即可找到客户单的可执行文件,双击即可打开一个客户端的可运行程序
可以看到两个客户端程序可以进行简单的文字交谈了 ,小明可能接下来会去小红家修电脑去了,但是小红的电脑具体有没有坏我们就不得而知了,懂得都懂 ~~ 嘿嘿