1对1聊天
通过select添加可读事件的监听实现。
服务端:
#include "header.h"
int main(void)
{
int listenfd = -1;
int connfd = -1;
struct sockaddr_in server, client;
int on = 1;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(8888);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (0 > (listenfd = socket(AF_INET, SOCK_STREAM, 0)))
err_exit("socket");
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (0 > bind(listenfd, (struct sockaddr *)&server, sizeof(server)))
err_exit("bind");
listen(listenfd, 1024);
printf("listen...\n");
memset(&client, 0, sizeof(client));
socklen_t len = sizeof(client);
if (0 > (connfd = accept(listenfd, (struct sockaddr *)&client, &len)))
err_exit("accept");
printf("client's ip is:%s, port is:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
char recv_buf[BUFSIZ];
char send_buf[BUFSIZ];
fd_set readfds;
int maxfd = -1;
int ret=-1;
int i;
while(1) {
FD_ZERO(&readfds);//所有位置零
FD_SET(0, &readfds);//设置0比特位为1,用于发送数据消息通知
maxfd = maxfd > 0 ? maxfd : 0;
FD_SET(connfd, &readfds);//设置connfd比特位为1,用于接受数据消息通知,
maxfd = maxfd > connfd ? maxfd : connfd;
if (0 > select(maxfd + 1, &readfds, NULL, NULL, NULL))//设置被监听的文件描述符的总数为maxfd + 1,readfds可读事件,且无限等待
err_exit("select");
for (i=0; i<=maxfd; i++)
{
if (FD_ISSET(i, &readfds)) //select返回后,用FD_ISSET测试给定位是否置位,通过获取那个bit位就知道是那个连接有数据
{
if (i == 0)//有数据需要发送
{
fgets(send_buf, sizeof(send_buf), stdin);
if (0 > send(connfd, send_buf, strlen(send_buf) + 1, 0))
err_exit("send");
}
else if (i == connfd)//有数据需要接收
{
if (0 > (ret = recv(connfd, recv_buf, BUFSIZ, 0)))
err_exit("recv");
else if (0 == ret)
{
printf("client quit!\n");
exit(0);
}
recv_buf[ret] = '\0';
printf("client: %s\n", recv_buf);
}
}
}
}
close(listenfd);
close(connfd);
exit(0);
}
客户端:
#include "header.h"
int main(void)
{
int connfd = -1;
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(8888);
server.sin_addr.s_addr = inet_addr("192.168.1.50");
if (0 > (connfd = socket(AF_INET, SOCK_STREAM, 0)))
err_exit("socket");
if (0 > connect(connfd, (struct sockaddr *)&server, sizeof(server)))
err_exit("connect");
printf("connect success!\n");
char send_buf[BUFSIZ];
char recv_buf[BUFSIZ];
int maxfd= -1;
fd_set readfds;
int ret=-1;
int i;
while(1) {
FD_ZERO(&readfds);
FD_SET(0, &readfds);
maxfd = maxfd > 0 ? maxfd : 0;
FD_SET(connfd, &readfds);
maxfd = maxfd > connfd ? maxfd : connfd;
if (0 > select(maxfd + 1, &readfds, NULL, NULL, NULL))
err_exit("select");
for (i=0; i<=maxfd; i++)
{
if (FD_ISSET(i, &readfds))
{
if (0 == i)
{
fgets(send_buf, sizeof(send_buf), stdin);
if (0 > send(connfd, send_buf, strlen(send_buf) + 1, 0))
err_exit("send");
}
else if (connfd == i)
{
if (0 > (ret = recv(connfd, recv_buf, BUFSIZ, 0)))
err_exit("recv");
else if (0 == ret)
{
printf("server quit!\n");
exit(0);
}
recv_buf[ret] = '\0';
printf("server: %s\n", recv_buf);
}
}
}
}
close(connfd);
exit(0);
}
1对多聊天
服务端:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <sys/select.h>
struct peer_node { //链表结构体
int id; //id号
int connfd; //描述符
struct peer_node *next; //指针
};
static struct peer_node *head; //结构体指针
int server_init(short port) //server服务器初始化函数
{
int sockfd;
int on = 1;
struct sockaddr_in self;
if(0 > (sockfd = socket(AF_INET, SOCK_STREAM, 0))) { //创建套接字
fprintf(stderr, "socket : %s\n", strerror(errno));
return -errno; //返回出错
}
if (0 > setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof(on))) { //允许重用本地址和端口
fprintf(stderr, "setsockopt : %s\n", strerror(errno));
exit(1);
}
memset(&self, 0, sizeof(self)); //初始化
self.sin_family = AF_INET; //ipv4
self.sin_port = htons(port); //端口
self.sin_addr.s_addr = INADDR_ANY; //本地ip
if(0 > bind(sockfd, (struct sockaddr *)&self, sizeof(self))) { //绑定本地信息
fprintf(stderr,"socket bind error:%s\n",strerror(errno));
return -errno;
}
printf("listen...\n"); //启动监听
listen(sockfd,1024);
return sockfd; //返回描述符
}
int find_id_by_connfd(int connfd) //在链接里找id
{
struct peer_node *node; //节点
node = head->next;
while(node) {
if(node->connfd == connfd)
return node->id; //返回id
node = node->next;
}
return -1;
}
int show_message(int skfd) //打印信息
{
char buf[1024];
struct sockaddr_in client;
socklen_t len;
int ret;
len = sizeof(client);
memset(&client, 0, sizeof(client));
ret = recv(skfd,buf,1023,0); //收
if(ret == 0) {
return 0;
}
buf[ret] = 0;
if(0 > getpeername(skfd, (struct sockaddr *)&client, &len)) { //或得信息
fprintf(stderr, "getpeername : %s\n", strerror(errno));
exit(1);
}
printf("message received from id:%d [%s:%d] : %s", find_id_by_connfd(skfd),inet_ntoa(client.sin_addr),ntohs(client.sin_port), buf);
return 1; //打印 id ip 端口 信息
}
int add_peerlist(int skfd) //把id从尾压入链表
{
struct peer_node *node; //节点
static int id = 1;
struct peer_node *tail; //尾
node = (struct peer_node *) malloc(sizeof(struct peer_node)); //开辟 堆
if(node == NULL) {
perror("malloc");
return -1;
}
node->id = id;
node->connfd = skfd;
node->next = NULL;
tail = head;
while(tail->next) {
tail = tail->next;
}
tail->next = node;
id++; //id 自加
return (node->id);
}
int del_peerlist(int skfd) //删除
{
struct peer_node *node;
struct peer_node *tmp;
int id;
node = head;
while(node->next->connfd != skfd) {
node = node->next;
}
tmp = node->next;
id = tmp->id;
node->next = tmp->next;
free(tmp);
return id;
}
int show_peerlist(void) //打印id列表
{
struct peer_node *node;
struct sockaddr_in client;
int ret;
socklen_t len = sizeof(client);
node = head->next;
printf("=========================\n");
while(node) {
ret = getpeername(node->connfd, (struct sockaddr *)&client,&len);
if(ret < 0) {
perror("getpeername");
}
printf("%d:%s:%d\n",node->id, inet_ntoa(client.sin_addr),ntohs(client.sin_port)); //id ip 端口
node = node->next;
}
printf("=========================\n");
return 0;
}
int find_connfd_by_id(int id) // 找文件描述符
{
struct peer_node *node;
node = head->next;
while(node) {
if(node->id == id)
return node->connfd; //返回
node = node->next;
}
return -1;
}
int init_peerlist(void) //初始化链表
{
head = (struct peer_node *) malloc(sizeof(struct peer_node));
if (NULL == head) {
fprintf(stderr, "malloc : %s\n", strerror(errno));
exit(1);
}
head->connfd = -1;
head->next = NULL;
return 0;
}
int send_message(void) //发送信息
{
char buf[1024];
char *ptr = NULL;
int skfd, id;
char id_str[10]; //存放id
fgets(buf,1024,stdin);
if (!strncasecmp(buf, "quit", 4)) {
printf("server close!\n");
exit(1);
}
if(0 == strncmp(buf,"list",4)) { //list 显示列表
show_peerlist();
return 0;
}
if(NULL == (ptr = strstr(buf,":"))) { //找出“:”位置指针
fprintf(stderr,"please usage \"ID:Message\" format!\n");
return 0;
}
memset(id_str, 0, 10);
memcpy(id_str, buf, (ptr - buf)); //拷贝内存内容 把1:***** 字符1拷贝到 id_str
if (0 >= (id = atoi(id_str))) { //转换成整数
fprintf(stderr, "the id is not valid!\n");
return -1;
}
skfd = find_connfd_by_id(id);
if(skfd < 0) {
fprintf(stderr,"Not found valid user!!!\n");
return -1;
}
if (0 > send(skfd, ptr+1, strlen(ptr + 1) + 1, 0)) { //发送:后面内容
fprintf(stderr,"send : %s\n", strerror(errno));
return -1;
}
return 0;
}
int main(int argc, char *argv[]) //主函数
{
if (2 > argc) { //判断
fprintf(stderr, "Usage : %s + server portnumber\n", argv[0]);
exit(1);
}
int listenfd;
int newfd;
int ret,maxfd,i,id;
fd_set current_fds,bak_fds;
char buf[1024];
init_peerlist();
short portnumber;
if (0 >= (portnumber = atoi(argv[1]))) { //端口号输入
fprintf(stderr, "port number is invalid!\n");
exit(1);
}
listenfd = server_init(portnumber); //调用前面服务器函数
if(listenfd < 0)
exit(-listenfd);
printf("type \"list\" for the client list!\n");
FD_ZERO(¤t_fds);
FD_ZERO(&bak_fds);
FD_SET(0, ¤t_fds);
FD_SET(listenfd, ¤t_fds);
maxfd = listenfd > 0 ? listenfd : 0;
while(1) {
bak_fds = current_fds;
ret = select(maxfd+1, &bak_fds, NULL, NULL, NULL); //多路复用
if(ret < 0) {
fprintf(stderr,"select error:%s\n",strerror(errno));
return errno;
}
for(i = 0; i <= maxfd; i++) {
if(FD_ISSET(i, &bak_fds)) {
if(0 == i) {
send_message();
} else if (i == listenfd){
if (0 > (newfd = accept(listenfd, NULL, NULL))) {
fprintf(stderr, "accept : %s\n", strerror(errno));
exit(1);
}
FD_SET(newfd, ¤t_fds);
if(newfd > maxfd)
maxfd = newfd;
id = add_peerlist(newfd);
snprintf(buf, sizeof(buf), "you id is %d\n", id);
send(newfd, buf, strlen(buf) + 1, 0);
printf("new connection : connfd is %d, id is %d\n", newfd, id);
} else {
ret = show_message(i);
if(ret == 0) {
close(i);
FD_CLR(i, ¤t_fds);
if(i == maxfd)
maxfd--;
printf("id : %d client quit!\n", del_peerlist(i));
}
}
}
}
}
return 0;
}
客户端:
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#define MAXBUF 1024
int main(int argc, char *argv[])
{
int sockfd;
socklen_t len;
struct sockaddr_in dest;
struct sockaddr_in self;
char buffer[MAXBUF + 1];
fd_set rfds;
struct timeval tv;
int retval, maxfd = -1;
if (argc < 2) {
fprintf(stderr, "Usage : %s + server_port\n", argv[0]);
exit(0);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
fprintf(stderr, "Socket : %s\n", strerror(errno));
exit(errno);
}
bzero(&dest, sizeof(dest));
dest.sin_family = AF_INET;
if (0 > (dest.sin_port = htons(atoi(argv[1])))) {
fprintf(stderr, "port number is wrong\n");
exit(1);
}
dest.sin_addr.s_addr = inet_addr("192.168.1.50");
if (0 > connect(sockfd, (struct sockaddr *) &dest, sizeof(dest))) {
fprintf(stderr, "Connect : %s\n", strerror(errno));
exit(1);
}
printf("connect success!\n");
len = sizeof(self);
if(0 > getsockname(sockfd, (struct sockaddr *)&self, &len)) {
fprintf(stderr, "getsockname : %s\n", strerror(errno));
exit(1);
}
printf("Myself is %s:%d\n",inet_ntoa(self.sin_addr),ntohs(self.sin_port));
while (1) {
FD_ZERO(&rfds);
FD_SET(0, &rfds);
FD_SET(sockfd, &rfds);
if (sockfd > maxfd)
maxfd = sockfd;
tv.tv_sec = 1;
tv.tv_usec = 0;
retval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
if (retval == -1) {
fprintf(stderr, "select : %s\n", strerror(errno));
break;
} else if (retval == 0) {
continue;
} else {
if (FD_ISSET(sockfd, &rfds)) {
bzero(buffer, MAXBUF + 1);
len = recv(sockfd, buffer, MAXBUF, 0);
if (len > 0)
printf("message received from server : %s",buffer);
else{
if (len < 0)
fprintf(stderr, "recv : %s\n", strerror(errno));
else
printf("server close!\n");
break;
}
}
else if(FD_ISSET(0, &rfds)) {
bzero(buffer, MAXBUF + 1);
fgets(buffer, MAXBUF, stdin);
if (!strncasecmp(buffer, "quit", 4)) {
printf("client close!\n");
break;
}
if ( 0 > send(sockfd, buffer, strlen(buffer)+1, 0)) {
fprintf(stderr, "send : %s\n", strerror(errno));
//break;
}
}
}
}
close(sockfd);
return 0;
}