客户端的任务:发送数据给服务器,并可以接收服务器的接收到的信息
服务器功能:可以接收多个客户端的发送的数据,并将数据发送给所有的客户端,也就是达到群聊的功能,同时也可以将服务器作为一个信息储存的中转站
客户端的代码
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
using namespace std;
#include <errno.h>
#define PUB_MSG_TYPE 256
#define ERR_EXIT(m) \
do{\
perror(m);\
_exit(1); \
} while (0)
int main()
{
int ret = 0;
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);//创建socket
if (socket_fd == 1)
ERR_EXIT("socket");
//绑定ip和端口
struct sockaddr_in ser_addr;
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(8097);
inet_aton("192.168.1.9", &ser_addr.sin_addr);
ret = connect(socket_fd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
if (ret == -1)
ERR_EXIT("connect");
cout << "connect success" << endl;
pid_t pid = fork();
if (pid == 0)
{
//子进程进行读取数据
char rbuf[1024] = { 0 };
while (1)
{
ret = read(socket_fd, rbuf, sizeof(rbuf));
if (ret == 0)
{
cout << "server is off" << endl;
break;
}
cout << "read:" << rbuf << endl;
memset(rbuf, 0x0, sizeof(rbuf));
}
exit(EXIT_SUCCESS);
}
//父进程负责从键盘获取数据输入 发送给服务器
char sbuf[1024] = { 0 };
while (fgets(sbuf, sizeof(sbuf), stdin) != 0)//stdin标准输入
{
write(socket_fd, sbuf, sizeof(sbuf));
memset(sbuf, 0x0, sizeof(sbuf));
}
return 0;
}
服务器的伪代码:
- 子进程读取信息到buf
- buf写入消息队列,并发送信号给父进程
- 父进程收到信号,去消息队列获取数据,遍历客户端的文件描述符数组,循环write写给已连接的客户端
服务器代码:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <unistd.h>
#include <string.h>
#include <vector>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <signal.h>
using namespace std;
#include <errno.h>
#define PUB_MSG_TYPE 256
#define ERR_EXIT(m) \
do{\
perror(m);\
exit(1); \
} while (0)
vector<int> vfds;//存放所有的客户端的文件描述符
int msgid;//消息队列的id
//消息结构体
typedef struct msg_buf {
long mtype;// message type, must be > 0
char mtest[PUB_MSG_TYPE];//message data
//void* mtest;
}MSG_T;
#define CHAT_MSG_TYPE 10
MSG_T rcvMsg;
vector<int>::iterator it;
//信号动作
void handler(int num)
{
cout << "num = " << num << endl;
//1.从消息对列中取出消息
msgrcv(msgid, &rcvMsg, sizeof(MSG_T), CHAT_MSG_TYPE, 0);
cout << "recv:" << rcvMsg.mtest << endl;
//2.群发
for (it = vfds.begin();it != vfds.end();it++)
{
write(*it, rcvMsg.mtest, sizeof(rcvMsg.mtest));
}
}
//服务器
int main()
{
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);//创建一个TCPsocket
if (socket_fd == 1)
ERR_EXIT("socket");
//绑定ip和端口
struct sockaddr_in ser_add;
ser_add.sin_family = AF_INET;
ser_add.sin_port = htons(8097);//端口号
ser_add.sin_addr.s_addr = htonl(INADDR_ANY);
//地址可重复使用
int on = 1;
setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
int ret = bind(socket_fd, (struct sockaddr*)&ser_add, sizeof(ser_add));
if (ret == -1)
ERR_EXIT("bind");
ret = listen(socket_fd, SOMAXCONN);//监听数据存放队列中
if (ret == -1)
ERR_EXIT("listen");
struct sockaddr_in cli_add;
socklen_t add_len = sizeof(cli_add);
//int add_len = sizeof(cli_add);
cout << "wait for client..." << endl;
//安装信号
msgid = msgget(5678, 0666 | IPC_CREAT);
signal(SIGUSR2, handler);
while (1)
{
int con_fd = accept(socket_fd, (struct sockaddr*)&cli_add, &add_len);
vfds.push_back(con_fd);//存放连接的客户端的文件描述符
cout << "someone on line..." << endl;
cout << "father pid = " << getpid() << endl;
pid_t pid = fork();
if (pid == 0)
{
char buf[1024] = { 0 };
while (1)
{
ret = read(con_fd, buf, sizeof(buf));
if (ret == 0)
{
cout << "client is offline" << endl;
break;
}
cout << "buf=" << buf << endl;
//1.把消息写入消息队列
MSG_T msg;
msg.mtype = CHAT_MSG_TYPE;
strcpy(msg.mtest, buf);
msgsnd(msgid, &msg, sizeof(MSG_T),0);//0非阻塞
//2.发信号通知父进程去取数据
kill(getppid(), SIGUSR2);
//write(con_fd, buf, sizeof(buf));
memset(buf, 0, sizeof(buf));
}
exit(EXIT_SUCCESS);
}
}
return 0;
}
实现效果