//头文件--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. linux网络流量实时监控工具之iptraf

    这个工具还是很强大 linux网络流量实时监控工具之iptraf [我的Linux,让Linux更易用]IPTraf是一个网络监控工具,功能比nload更强大,可以监控所有的流量,IP流量,按协议分的 ...

  2. linux中fork()函数详解(原创!!实例讲解) (转载)

     一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程, 也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不 ...

  3. 【转】android应用程序的安装方式与原理

    四种安装方式: 1.系统应用安装――开机时完成,没有安装界面 2.网络下载应用安装――通过market应用完成,没有安装界面 3.ADB工具安装――没有安装界面. 4.第三方应用安装――通过SD卡里的 ...

  4. Objective-c 基础框架(初学者-总结)

    一个框架其实就是一个软件包,它包含了多个类.Mac 操作系统提供了几十个框架,主要帮助开发者快速的在Mac 系统上开发应用程序.其中包括一些基础框架,就是为所有程序开发提供基础的框架,其中几个常用的类 ...

  5. oc 字符串

    #import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { ...

  6. UIlable 属性详用

    我的好朋友给我制定了一个新的学习方法,从新的看每个控件,去了解他的每个属性,方法来让自己对oc的认识更加充实 今天重新认识一下UILable 的属性lable的阴影设置: 阴影的偏移量是以lable中 ...

  7. js检测浏览器型号

    公司要求做内部统计,要求监控客服玩游戏使用的浏览器的型号,是火狐的.谷歌的.还是IE的等等. [code lang="javascript"] /**** * 目前识别范围 * M ...

  8. 静态代码检查工具-PMD初学者入门篇

    前言: PMD是一款静态代码分析工具,它能够自动检测各种潜在缺陷以及不安全或未优化的代码. PMD更多地是集中在预先检测缺陷上,它提供了高度可配置的丰富规则集,用户可以方便配置对待特定项目使用那些规则 ...

  9. cocos2d-x之物理按键初试

    bool HelloWorld::init() { if ( !Layer::init() ) { return false; } auto listener=EventListenerKeyboar ...

  10. BaseDao

    public class BaseDao { private static Log logger = LogFactory.getLog(BaseDao.class); // 查询数据 public ...