编写一个socket通信客户端,首先要做的是确定I/O模型。这决定了程序的性能和试用场景,也决定了程序的大致框架。
下图来自《Unix网络编程》,直观地比较了各种I/O模型间的特点。
- 阻塞式I/O
收发消息过程中,若没有完成消息接受/发送工作,则程序阻塞在recv()/send()函数处。 - 非阻塞式I/O
收发消息过程中,若没有完成消息接受/发送工作,则recv()/send()立即返回,通过全局变量errno返回此时连接状态。但这种方式耗费cpu资源较高,不推荐使用。另外,如果单纯地想检测连接状态,可以使用select()函数。 - I/O复用
主要用在高并发场合。 - 信号驱动I/O
由SIGIO信号实现异步操作。不过由于TCP协议产生信号频繁,捕获信号并调用相应函数的意义不大,通常用于UDP应用。 - 异步I/O
只有这种I/O模型是真正意义上的异步I/O,有待学习。
以下是一个简单的半双工非阻塞式的socket客户端程序。所谓半双工是指不能同时收/发,非阻塞则指收/发过程不会使请求进程停滞。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define BUFSIZE 64 //缓冲区容量
#define CACHECNT 16 //暂存字符串数目
int main()
{
char select,buf[BUFSIZE],cache[CACHECNT][BUFSIZE];
int sockfd,cacheNum = -1;;
if((sockfd = socket(AF_INET,SOCK_STREAM,0)) <0)
{
perror("Create socket failed");
exit(-1);
}
struct sockaddr_in addr;
memse