//头文件--helper.h
#ifndef _vzhang
#define _vzhang #ifdef __cplusplus
extern "C"
{
#endif #define MAX_SOCKET_NUM 1024
#define BUF_SIZE 1024 //server create socket
int server_socket(int port); //close socket
int close_socket(int st); //start select
int start_select(int listen_st); //connect server
int connect_server(char * ipaddr, int port); //thread for recv message
void * thread_recv(void *arg); //thread for send message
void * thread_send(void *arg); #ifdef __cplusplus
}
#endif #endif
//辅助方法--helper.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <time.h>
#include <fcntl.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include "helper.h" //create socket
int create_socket()
{
int st = socket(AF_INET, SOCK_STREAM, );
if (st < )
{
printf("create socket failed ! error message :%s\n", strerror(errno));
return -;
}
return st;
} //set reuseaddr
int set_reuseaddr(int st)
{
int on = ;
if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < )
{
printf("reuseaddr failed ! error message :%s\n", strerror(errno));
return -;
}
return ;
} //bind IP and port
int bind_ip(int st, int port)
{
struct sockaddr_in addr;
memset(&addr, , sizeof(addr));
//type
addr.sin_family = AF_INET;
//port
addr.sin_port = htons(port);
//address
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) < )
{
printf("bind failed ! error message :%s\n", strerror(errno));
return -;
}
return ;
} //listen
int listen_port(int st, int num)
{
if (listen(st, num) < )
{
printf("listen failed ! error message :%s\n", strerror(errno));
return -;
}
return ;
} //server create socket
int server_socket(int port)
{
int st = create_socket();
if (st < )
{
return -;
}
if (set_reuseaddr(st) < )
{
return -;
}
if (bind_ip(st, port) < )
{
return -;
}
if (listen_port(st, ) < )
{
return -;
}
return st;
} //get IP address by sockaddr_in
void sockaddr_toa(const struct sockaddr_in *addr, char * ipaddr)
{
unsigned char * p = (unsigned char *) &(addr->sin_addr.s_addr);
sprintf(ipaddr, "%u.%u.%u.%u", p[], p[], p[], p[]);
} //accept client
int accept_client(int st)
{
if (st < )
{
printf("function accept_client param not correct!\n");
return -;
}
struct sockaddr_in addr;
memset(&addr, , sizeof(addr));
socklen_t len = sizeof(addr);
int client_st = accept(st, (struct sockaddr *) &addr, &len);
if (client_st < )
{
printf("accept failed ! error message :%s\n", strerror(errno));
return -;
}
char buf[] = { };
sockaddr_toa(&addr, buf);
printf("accept by %s\n", buf);
return client_st;
} //close socket
int close_socket(int st)
{
if (st < )
{
printf("function close_socket param not correct!\n");
return -;
}
close(st);
return ;
} //protect message
int protect_message(int st, int * starr)
{
if (st < || starr == NULL)
{
printf("function recv_message param not correct!\n");
return -;
}
char buf[BUF_SIZE] = { };
int i = ;
int rc = recv(st, buf, sizeof(buf), );
if (rc < )
{
printf("recv failed ! error message :%s\n", strerror(errno));
return -;
} else if (rc == )
{
printf("client is closed !\n");
return -;
}
/*
*QQ消息处理:接收到client1的消息直接发送给client2
*/
for (; i < MAX_SOCKET_NUM; i++)
{
if (starr[i] != st && starr[i] != -)
{
//向其他的chient发送消息
if (send(starr[i], buf, strlen(buf), ) < )
{
printf("send failed ! error message :%s\n", strerror(errno));
return -;
}
}
}
return ;
} //send message
int send_message(int st, int * starr)
{
return ;
} //start select
int start_select(int listen_st)
{
if (listen_st < )
{
printf("function create_select param not correct!\n");
return -;
}
int i = ;
//定义select第一个参数变量
int maxfd = ;
int rc = ;
//创建客户端socket池
int client[MAX_SOCKET_NUM] = { };
//初始化socket池
for (; i < MAX_SOCKET_NUM; i++)
{
client[i] = -;
}
//定义事件数组结构
fd_set allset;
while ()
{
//清空事件数组
FD_ZERO(&allset);
//将服务器端socket加入事件数组
FD_SET(listen_st, &allset);
//假设值最大的socket是listen_st
maxfd = listen_st;
//将所有客户端socket加入到事件数组
for (i = ; i < MAX_SOCKET_NUM; i++)
{
if (client[i] != -)
{
FD_SET(client[i], &allset);
if (maxfd < client[i])
{
maxfd = client[i];
}
}
}
//select阻塞接收消息
rc = select(maxfd + , &allset, NULL, NULL, NULL);
//select函数报错,直接退出循环
if (rc < )
{
printf("select failed ! error message :%s\n", strerror(errno));
break;
}
if (FD_ISSET(listen_st, &allset))
{
rc--;
//处理服务端socket事件
int client_st = accept_client(listen_st);
if (client_st < )
{
/*
* 如果accept失败,直接退出select,
* continue不太合适,因为其他的客户端事件还没有处理,这样就会丢事件
*/
break;
}
//将客户端socket放到socket池中
for (i = ; i < MAX_SOCKET_NUM; i++)
{
if (client[i] == -)
{
client[i] = client_st;
break;
}
}
if (i == MAX_SOCKET_NUM)
{
printf("服务器端socket池已经满了");
//socket池已满,关闭客户端连接
close_socket(client_st);
}
}
//处理客户端事件
for (i = ; i < MAX_SOCKET_NUM; i++)
{
//如果事件数组中的事件已经处理完成,直接进入跳出当前循环
if (rc <= )
{
break;
}
if (client[i] != -)
{
if (FD_ISSET(client[i], &allset))
{
//该socket有事件发生
if (protect_message(client[i], client) < )
{
//接收消息失败,但是由于逻辑复杂,直接退出select
//关闭客户端socket
close_socket(client[i]);
//将该socket从socket池中删除
client[i] = -1;
break;
}
}
}
}
}
return ;
} //connect server
int connect_server(char * ipaddr, int port)
{
int st = create_socket();
if (st < )
{
return -;
}
struct sockaddr_in addr;
memset(&addr, , sizeof(addr));
//type
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ipaddr);
if (connect(st, (struct sockaddr *) &addr, sizeof(addr)) < )
{
printf("connect failed ! error message :%s\n", strerror(errno));
return -;
}
return st;
} //thread for recv message
void * thread_recv(void *arg)
{
int st = *(int *) arg;
if (st < )
{
printf("function thread_recv param not correct!\n");
return NULL;
}
char buf[BUF_SIZE] = { };
while ()
{
if (recv(st, buf, sizeof(buf), ) < )
{
printf("recv failed ! error message :%s\n", strerror(errno));
break;
}
printf("%s", buf);
memset(buf, , sizeof(buf));
}
return NULL;
} //thread for send message
void * thread_send(void *arg)
{
int st = *(int *) arg;
if (st < )
{
printf("function thread_send param not correct!\n");
return NULL;
}
char buf[BUF_SIZE] = { };
while ()
{
read(STDIN_FILENO, buf, sizeof(buf));
if (send(st, buf, sizeof(buf), ) < )
{
printf("send failed ! error message :%s\n", strerror(errno));
break;
}
memset(buf, , sizeof(buf));
}
return NULL;
}
//QQ客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include "helper.h" int main(int arg, char *args[])
{
if (arg < )
{
printf("please print two param !\n");
return -;
}
int port = atoi(args[]);
char ipaddr[] = { };
strcpy(ipaddr, args[]);
//connect server
int st = connect_server(ipaddr, port);
if (st < )
{
return -;
}
//recv thread
pthread_t thr1, thr2;
if (pthread_create(&thr1, NULL, thread_recv, &st) != )
{
printf("pthread_create failed ! error message :%s\n", strerror(errno));
return -;
}
if (pthread_create(&thr2, NULL, thread_send, &st) != )
{
printf("pthread_create failed ! error message :%s\n", strerror(errno));
return -;
}
//join
pthread_join(thr1,NULL);
pthread_join(thr2,NULL);
//close socket
close_socket(st);
return ;
}
//QQ服务端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "helper.h" int main(int arg,char *args[])
{
//服务器端需要传入端口号
if(arg<)
{
printf("please print one param !\n");
return -;
}
int port=atoi(args[]);
int st=server_socket(port);
if(st<)
{
return -;
}
//开启select 监听事件
start_select(st);
//close socket
close_socket(st);
return ;
}
.SUFFIXES:.c .o
CC=gcc
SRCS1=QQserver.c\
helper.c
SRCS2=QQclient.c\
helper.c
OBJS1=$(SRCS1:.c=.o)
OBJS2=$(SRCS2:.c=.o)
EXEC1=mserver
EXEC2=mclient start:$(OBJS1) $(OBJS2)
$(CC) -o $(EXEC1) $(OBJS1)
$(CC) -o $(EXEC2) $(OBJS2) -lpthread
@echo "-------ok-----------"
.c.o:
$(CC) -Wall -g -o $@ -c $<
clean:
rm -f $(OBJS1)
rm -f $(EXEC1)
rm -f $(OBJS2)
rm -f $(EXEC2)
小结:
  这次程序编码调试都很快,调试中第一个错误,客户端连接不上服务器,但是connect不报错,我几乎每次写网络程序都有这个问题,就目前而言有两种出错可能
出错场景1:服务端socket有问题,为0或者不是正确的socket
出错场景2:服务器端的socket没有放在select池中,没有被select监视
我暂时就只有这两种犯错场景。
调试第二个错误:“Segmentation fault”,这个错误我觉得一般都是操作内存出了问题,内存泄漏,我这边是因为在函数sockaddr_toa()中
unsigned char *p=(unsiged char *)&(addr->sin_addr.s_addr); //没有取addr->sin_addr.s_addr的地址转化成unsigned char类型,发生内存泄漏
调试第三个错误是逻辑错误,每次有客户端socket接收数据出错后,我没有从socket池中将该socket清除,导致select()函数不再阻塞,每次直接返回

Linux Linux程序练习十二(select实现QQ群聊)的更多相关文章

  1. Linux学习总结(十二)—— CentOS用户管理:创建用户、修改用户、修改密码、密码有效期、禁用账户、解锁账户、删除用户、查看所有用户信息

    文章首发于Linux学习总结(十二)-- CentOS用户管理,请尊重原创保留原文链接. 创建用户 useradd -g webadmin -d /home/zhangsan zhangsan pas ...

  2. Linux学习之CentOS(十二)----磁盘管理之 认识ext文件系统(转)

    认识ext文件系统 硬盘组成与分割 文件系统特性 Linux 的 EXT2 文件系统(inode) 与目录树的关系 EXT2/EXT3 文件的存取与日志式文件系统的功能 Linux 文件系统的运行 挂 ...

  3. 鸟哥的linux私房菜——第十二章学习(Shell Scripts)

    第十二章  Shell Scripts 1.0).什么是shell scripts? script 是"脚本.剧本"的意思.整句话是说, shell script 是针对 shel ...

  4. Linux学习之CentOS(十二)------磁盘管理之 磁盘的分区、格式化、挂载(转)

    磁盘分区.格式化.挂载磁盘分区    新增分区    查询分区    删除分区磁盘格式化    mkfs    mke2fs磁盘挂载与卸载    mount    umount 磁盘的分区.格式化.挂 ...

  5. Linux经常使用命令(十二) - less

    less 工具也是对文件或其他输出进行分页显示的工具.应该说是linux正统查看文件内容的工具.功能极其强大. less 的使用方法比起 more 更加的有弹性.使用了 less 时.更easy用来查 ...

  6. Linux 入门记录:十二、Linux 权限机制【转】

    转自:https://www.cnblogs.com/mingc/p/7591287.html 一.权限 权限是操作系统用来限制资源访问的机制,权限一般分为读.写.执行. 系统中每个文件都拥有特定的权 ...

  7. Linux学习之CentOS(十二)--crontab命令的使用方法

    http://www.cnblogs.com/xiaoluo501395377/archive/2013/04/06/3002602.html crontab命令常见于Unix和Linux的操作系统之 ...

  8. Linux 入门记录:十二、Linux 权限机制

    一.权限 权限是操作系统用来限制资源访问的机制,权限一般分为读.写.执行. 系统中每个文件都拥有特定的权限.所属用户及所属组,通过这样的机制来限制哪些用户.哪些组可以对特定的文件进行什么样的操作. 每 ...

  9. Linux Shell常用技巧(十二)

    二十三. Bash Shell编程:  1.  读取用户变量:    read命令是用于从终端或者文件中读取输入的内建命令,read命令读取整行输入,每行末尾的换行符不被读入.在read命令后面,如果 ...

随机推荐

  1. JAVA基础学习day15--集合二 TreeSet和泛型

    一.TreeSet 1.1.TreeSet Set:hashSet:数据结构是哈希表.线程是非同步的.        保证元素唯一性的原理:判断元素的HashCode值是否相同.         如果 ...

  2. Spring(七)持久层

    一.Spring对DAO的支持 DAO:Data Access Object Spring提供了DAO框架,让开发人员无须耦合特定的数据库技术,就能进行应用程序的开发. Spring封闭了操作Orac ...

  3. iOS 界面调试利器Reveal

    Reveal下载地址:http://revealapp.com/ ,目前要收费了,而且还不便宜,好东西都这样嘛~ 针对越狱设备和非越狱设备可以采取不同的方法,一种是在工程项目中加入Reveal.fra ...

  4. Effective Java 59 Avoid unnecessary use of checked exceptions

    The burden is justified if the exceptional condition cannot be prevented by proper use of the API an ...

  5. java Hello 出现以下结果:Bad command or the file name 可能是什么原因

    没有这个命令或文件名 原因可能是没有成功安装jdk或者没有配置好jdk 的环境变量,或者没有编译相应的文件. 2. 出现以下结果:Exception in thread “main” java.lan ...

  6. 如何成功发布一个MSMQ的Windows服务

    因为MSMQ的使用需要不断的查看队列是否有新消息,所以一般是结合Windows的服务,当然也可以用一个不关闭的Winform程序,不过前者更好一些,不怕被人误关. 完成MSMQ的WindowsServ ...

  7. ERP产品价格成本计算的几个方法(转)

          一般财务计算产品价格又很多方法,我这里做了几个供参考,实体属性主要是编号.数量.价格等,这里就不列出了. /// <summary> /// 先进先出算法 /// </s ...

  8. 烂泥:U盘安装Centos6.5

    本文由秀依林枫提供友情赞助,首发于烂泥行天下. 使用U盘安装Centos6.5,需要以下几个步骤: 1. 制作U盘linux系统 2. 设置服务器BIOS 3. 安装Centos,注意引导分区的安装 ...

  9. C语言杂谈(二)自增运算符++与间接访问运算符*的结合关系和应用模式

    自增运算符++有前缀和后缀两种,在搭配间接访问运算符*时,因为顺序.括号和结合关系的影响,很容易让人产生误解,产生错误的结果,这篇文章来详细分析一下这几种运算符的不同搭配情况. ++.--和*的优先级 ...

  10. MFC 窗口分割

    动态分割窗口: BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) { , , CSize(, ...