服务器/客户端的简单例子
read()/write()函数代码
服务器代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/in.h>
#define PORT 8888 //prot
#define BACKLOG 2 //侦听队列长度
void process_conn_server(int s);
int main()
{
int serverSocket,clientSocket;
struct sockaddr_in server_addr;//服务器地址结构
struct sockaddr_in client_addr;//客户端地址结构
int err;
pid_t pid;//分叉进程ID
//建立套接字
serverSocket = socket(AF_INET,SOCK_STREAM,0);
if(serverSocket < 0)
{
printf("socket error\n");
return -1;
}
//设置服务器地址
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;//协议族
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//本地地址
server_addr.sin_port = htons(PORT);//服务器端口
//帮定地址结构到套接字描述符
err = bind(serverSocket,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(err < 0)
{
printf("bind error\n");
return -1;
}
//设置侦听
err = listen(serverSocket,BACKLOG);
if(err < 0)
{
printf("listen error\n");
return -2;
}
//主循环过程
for(;;)
{
int addrlen = sizeof(struct sockaddr);
clientSocket = accept(serverSocket,(struct sockaddr*)&client_addr,&addrlen);
//接收客户端连接
if(clientSocket < 0)
{
//printf("accept error\n");
continue;
}
printf("客户端连接成功\n");
//建立一个新的连接的进程处理到来的连接
pid = fork();//分叉进程
if(pid == 0)
{
//子进程
close(serverSocket);
process_conn_server(clientSocket);//处理连接
}
else
close(serverSocket);//在父进程中关闭客户端的连接
}
}
void process_conn_server(int s)
{
ssize_t size =0;
char buffer[1024];
memset(buffer,0,1024);
for(;;)
{
size = read(s,buffer,1024);
if(size == 0)
{
printf("size = %d\n",size);
return;
}
sprintf(buffer,"%d bytes altogether\n",size);
write(s,buffer,strlen(buffer)+1);//发送给客户端
}
}
客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/in.h>
#define PORT 8888 //prot
void process_conn_client(int s);
int main()
{
int s;//s为socket
struct sockaddr_in server_addr;//服务器地址
int err;
s = socket(AF_INET,SOCK_STREAM,0);//建立流式套接字
if(s<0)
{
printf("socket error\n");
return -1;
}
//设置服务器地址
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;//协议族
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//本地地址
server_addr.sin_port = htons(PORT);
//将字符串类型的ip地址转为整型
inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr);
//连接服务器
connect(s,(struct sockaddr*)&server_addr,sizeof(struct sockaddr));
process_conn_client(s);//客户端处理过程
close(s);//关闭连接
}
void process_conn_client(int s)
{
ssize_t size = 0;
char buffer[1024];
memset(buffer,0,1024);
for(;;)
{
//从标准输入中读取数据,放到缓冲区中
size = read(0,buffer,1024);
if(size>0)
{
//读到数据
write(s,buffer,size);//发送给服务器
size = read(s,buffer,1024);//从服务器中读取
write(1,buffer,size);//写到标准输出中
}
}
}
执行结果:
send()/recv()函数的代码
服务器代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/in.h>
#include <signal.h>
void sig_int(int signo);//信号SIGINT处理函数
void sig_pipe(int signo);//信号SIGPIPE处理函数
void process_conn_server(int s);
#define PORT 8888
#define BACKLOG 2
int main()
{
int ss=0,sc=0;
struct sockaddr_in server_addr;//服务器地址结构
struct sockaddr_in client_addr;//客户端地址结构
int err;
pid_t pid;
signal(SIGINT,sig_int);//注册SIGINT信号,通常由CRTL+C终止进程产生的信号
signal(SIGPIPE,sig_pipe);//注册SIGPIPE信号,当正在写入套接字时,读取端关闭时会产生SIGPIPE信号
//建立流式套接字
ss = socket(AF_INET,SOCK_STREAM,0);
if(ss<0)
{
printf("socket error\n");
return -1;
}
//设置服务器地址
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;//协议族
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//本地地址
server_addr.sin_port = htons(PORT);//服务器端口
//帮定地址到套接字
err = bind(ss,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(err<0)
{
printf("bind error\n");
return -2;
}
//监听
err = listen(ss,BACKLOG);
if(err<0)
{
printf("listrn error\n");
return -3;
}
printf("服务器已启动:ss=%d\n",ss);
//主循环
for(;;)
{
int addrlen = sizeof(struct sockaddr);
//接收客户端连接
sc = accept(ss,(struct sockaddr*)&client_addr,&addrlen);
if(sc<0)
{
//printf("=====\n");
continue;
}
printf("客户端连接成功\n");
//建立新进程处理到来的请求
pid = fork();
if(pid == 0)
{
//子进程
close(ss);
process_conn_server(sc);//处理连接
}
else
{
//父进程
close(sc);
}
}
}
void process_conn_server(int s)
{
ssize_t size =0;
char buffer[1024];
memset(buffer,0,1024);
for(;;)
{
//从套接字中读取数据到缓冲区
size = recv(s,buffer,1024,0);
if(size == 0)
{
printf("size = %d\n",size);
return;
}
sprintf(buffer,"%d bytes altogether\n",size);
send(s,buffer,strlen(buffer)+1,0);//发送给客户端
}
}
void sig_int(int signo)
{
printf("Catch a SIGINT signal\n");
_exit(0);
}
void sig_pipe(int signo)
{
printf("Catch a SIGPIPE signal\n");
}
客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/in.h>
#include <signal.h>
#include <errno.h>
void sig_int(int signo);//信号SIGINT处理函数
void sig_pipe(int signo);//信号SIGPIPE处理函数
void sig_process_client(int signo);
void process_conn_client(int s);
#define PORT 8888
static int s;
int main()
{
struct sockaddr_in server_addr;//服务器地址结构
int err;
signal(SIGINT,sig_int);
signal(SIGPIPE,sig_pipe);
//建立套接字
s = socket(AF_INET,SOCK_STREAM,0);
if(s<0)
{
printf("socket error\n");
return -1;
}
//设置服务器地址
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(PORT);
inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr);
//inet_pton(AF_INET,"127.0.0.1",&server_addr,&server_addr.sin_addr);//这里传入三个参数,linux下居然没有报错,找了几天才找出错误
err = connect(s,(struct sockaddr*)&server_addr,sizeof(struct sockaddr));
if(err<0)
{
printf("connect error:errno=%d\n",errno);
return -2;
}
process_conn_client(s);//客户端处理连接函数
close(s);
}
void process_conn_client(int s)
{
ssize_t size = 0;
char buffer[1024];
memset(buffer,0,1024);
printf("客户端已启动\n");
for(;;)
{
size = read(0,buffer,1024);//从标准输入读取数据
if(size>0)
{
send(s,buffer,size,0);//发送给客户端
size = recv(s,buffer,1024,0);//从服务器端读取数据
write(1,buffer,size);//写到标准输出中
}
}
}
void sig_int(int signo)
{
printf("Catch a SIGINT signal\n");
close(s);
_exit(0);
}
void sig_pipe(int signo)
{
printf("Catch a SIGPIPE signal\n");
}
void sig_process_client(int signo)
{
printf("Catch a exit signal\n");
close(s);
_exit(0);
}
最终结果:
readv()/wirtev()函数
下面代码时利用上述两个函数将数据写入向量中,并从向量中获取数据。
服务器代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/in.h>
#include <signal.h>
#include <sys/uio.h>
static struct iovec *vs=NULL,*vc=NULL;
void sig_int(int signo);//信号SIGINT处理函数
void sig_pipe(int signo);//信号SIGPIPE处理函数
void process_conn_server(int s);
#define PORT 8888
#define BACKLOG 2
int main()
{
int ss=0,sc=0;
struct sockaddr_in server_addr;//服务器地址结构
struct sockaddr_in client_addr;//客户端地址结构
int err;
pid_t pid;
signal(SIGINT,sig_int);//注册SIGINT信号,通常由CRTL+C终止进程产生的信号
signal(SIGPIPE,sig_pipe);//注册SIGPIPE信号,当正在写入套接字时,读取端关闭时会产生SIGPIPE信号
//建立流式套接字
ss = socket(AF_INET,SOCK_STREAM,0);
if(ss<0)
{
printf("socket error\n");
return -1;
}
//设置服务器地址
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;//协议族
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//本地地址
server_addr.sin_port = htons(PORT);//服务器端口
//帮定地址到套接字
err = bind(ss,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(err<0)
{
printf("bind error\n");
return -2;
}
//监听
err = listen(ss,BACKLOG);
if(err<0)
{
printf("listrn error\n");
return -3;
}
printf("服务器已启动:ss=%d\n",ss);
//主循环
for(;;)
{
int addrlen = sizeof(struct sockaddr);
//接收客户端连接
sc = accept(ss,(struct sockaddr*)&client_addr,&addrlen);
if(sc<0)
{
//printf("=====\n");
continue;
}
printf("客户端连接成功\n");
//建立新进程处理到来的请求
pid = fork();
if(pid == 0)
{
//子进程
close(ss);
process_conn_server(sc);//处理连接
}
else
{
//父进程
close(sc);
}
}
}
void process_conn_server(int s)
{
ssize_t size =0;
char buffer[30];
memset(buffer,0,30);
//申请3个向量
struct iovec* v=(struct iovec*)malloc(3*sizeof(struct iovec));
if(!v)
{
printf("Not enough memory\n");
return;
}
vs=v;//挂载全局变量,便于释放管理
//每个向量10个字节
v[0].iov_base = buffer;
v[1].iov_base = buffer+10;
v[2].iov_base = buffer+20;
v[0].iov_len = v[0].iov_len = v[0].iov_len = 10;
//循环过程
for(;;)
{
memset(buffer,0,30);
//从套接字中读取数据放到向量缓冲区中
size = readv(s,v,3);
if(size == 0)
return;
//这里可以输出接收到的字符串
int i=0;
printf("客户端发来消息:\n");
for(i=0;i<3;i++)
{
if(v[i].iov_len > 0)
write(1,v[i].iov_base,v[i].iov_len);//写到标准输出
}
printf("\n");
memset(buffer,0,30);
//构建响应字符,为接收到客户端字节的数量,分别放到3个缓冲区中
sprintf(v[0].iov_base,"%d",size);
sprintf(v[1].iov_base,"bytes alt");
sprintf(v[2].iov_base,"ogether\n");
v[0].iov_len = strlen(v[0].iov_base);
v[1].iov_len = strlen(v[1].iov_base);
v[2].iov_len = strlen(v[2].iov_base);
//发送给客户端
writev(s,v,3);
}
}
void sig_int(int signo)
{
printf("Catch a SIGINT signal\n");
free(vs);
free(vc);
_exit(0);
}
void sig_pipe(int signo)
{
printf("Catch a SIGPIPE signal\n");
free(vs);
free(vc);
_exit(0);
}
客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/in.h>
#include <signal.h>
#include <errno.h>
void sig_int(int signo);//信号SIGINT处理函数
void sig_pipe(int signo);//信号SIGPIPE处理函数
void sig_process_client(int signo);
void process_conn_client(int s);
#define PORT 8888
static int s;
static struct iovec *vs=NULL,*vc=NULL;
int main()
{
struct sockaddr_in server_addr;//服务器地址结构
int err;
signal(SIGINT,sig_int);
signal(SIGPIPE,sig_pipe);
//建立套接字
s = socket(AF_INET,SOCK_STREAM,0);
if(s<0)
{
printf("socket error\n");
return -1;
}
//设置服务器地址
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(PORT);
inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr);
err = connect(s,(struct sockaddr*)&server_addr,sizeof(struct sockaddr));
if(err<0)
{
printf("connect error:errno=%d\n",errno);
return -2;
}
process_conn_client(s);//客户端处理连接函数
close(s);
}
void process_conn_client(int s)
{
ssize_t size = 0;
char buffer[30];
memset(buffer,0,30);
printf("客户端已启动\n");
struct iovec *v=(struct iovec*)malloc(3*sizeof(struct iovec));
if(!v)
{
printf("Not enough memory\n");
return;
}
vc=v;//挂载全局变量,便于释放
//每个向量10个字节的空间
v[0].iov_base = buffer;
v[1].iov_base = buffer+10;
v[2].iov_base = buffer+20;
v[0].iov_len = v[1].iov_len = v[2].iov_len = 10;//初始化长度
int i = 0;
for(;;)
{
//从标准输入中读取数据放到缓冲区buffer中
size = read(0,v[0].iov_base,10);
if(size>0)
{
v[0].iov_len=size;
writev(s,v,1);//发送给服务器,最后1表示1个缓冲区
v[0].iov_len = v[1].iov_len = v[2].iov_len = 10;
size = readv(s,v,3);//从服务器读取数据
for(i=0;i<3;i++)
{
if(v[i].iov_len > 0)
write(1,v[i].iov_base,v[i].iov_len);//写到标准输出
}
}
}
}
void sig_int(int signo)
{
printf("Catch a SIGINT signal\n");
close(s);
free(vs);
free(vc);
_exit(0);
}
void sig_pipe(int signo)
{
free(vs);
free(vc);
printf("Catch a SIGPIPE signal\n");
_exit(0);
}
void sig_process_client(int signo)
{
printf("Catch a exit signal\n");
close(s);
_exit(0);
}
最终运行结果:

recvmsg()/sendmsg()函数
与readv()和writev()的服务器处理过程相似,使用消息函数进行IO的服务器处理过程同样适用3个10字节大小的向量缓冲区来保存数据。但是并不是直接对这些向量进行操作,而是将向量挂在消息结构msghdr的msg_iov成员变量上进行操作,并将向量的存储空间长度设为30。在服务器端调用函数recvmsg()从套接字s中接收数据到msg中,将接收到的消息进行处理后,调用sendmsg()函数将相应数据通过套接字s发出。