写在文章前:
这学习linux编程,也有一段时间了。虽然是一个人看书,琢磨。也想把自己看过的做一个总结,一步一步来,总有一天会质变的。不得不说,linux太博大精深了,里面需要学的东西太多了。
echo服务器,可以看成 学习网络编程的“hello world”。
echo服务器,描述起来很简单,服务端收到什么,就给客户端发送什么。也就是这个简单的程序,能够让你从中学到不少东西。多线程,多进程,I/O复用,信号处理这些都会遇到。
第一个echo服务程序,不需要考虑各种问题,只要能够完成功能就行。
先来看看简易流程图
基于TCP Socket编程的流程图类似,服务端主要是,创建socket、绑定IP和端口、监听、接受连接、处理数据包、关闭连接。客户端则主要是,创建socket、连接到服务器、发送请求、关闭连接。
echo服务端代码:
/*
* File: Server.c
* Author: root
*
* Created on 2012年6月20日, 下午1:29
*/
#include
#include
#include
#include
#include
#include
#include
#define SERVERIP "192.168.0.23"
#define SERVERPORT 12345
#define MAXBUFFER 256
int main(int argc, char** argv)
{
int serverFd, connfd,ret;
socklen_t len;
struct sockaddr_in serveraddr,clientaddr;
char readBuf[MAXBUFFER]={0};
char ip[40]={0};
serverFd=socket(AF_INET,SOCK_STREAM,0);//创建socket
if(serverFd < 0)
{
printf("socket error:%s\n",strerror(errno));
exit(-1);
}
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(SERVERPORT);
inet_pton(AF_INET,SERVERIP,&serveraddr.sin_addr);//将c语言字节序转换为网络字节序
ret=bind(serverFd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));//绑定IP和端口
if(ret!=0)
{
close(serverFd);
printf("bind error:%s\n",strerror(errno));
exit(-1);
}
ret=listen(serverFd,5);//监听
if(ret!=0)
{
close(serverFd);
printf("listen error:%s\n",strerror(errno));
exit(-1);
}
len=sizeof(clientaddr);
bzero(&clientaddr,sizeof(clientaddr));
while (1)
{
connfd = accept(serverFd, (struct sockaddr *) &clientaddr, &len);//接受客户端的连接
printf("%s 连接到服务器 \n",inet_ntop(AF_INET,&clientaddr.sin_addr,ip,sizeof(ip)));
if (serverFd < 0)
{
printf("accept error : %s\n", strerror(errno));
continue;
}
while((ret=read(connfd,readBuf,MAXBUFFER)))//读客户端发送的数据
{
write(connfd,readBuf,MAXBUFFER);//写回客户端
bzero(readBuf,MAXBUFFER);
}
if(ret==0)
{
printf("客户端关闭连接\n");
}else
{
printf("read error:%s\n",strerror(errno));
}
close(connfd);
}
close(serverFd);
return 0;
}
echo 客户端代码
/*
* File: Client.c
* Author: root
*
* Created on 2012年6月20日, 下午1:30
*/
#include
#include
#include
#include
#include
#include
#include
#define SERVERIP "192.168.0.23"
#define SERVERPORT 12345
#define MAXBUFFER 256
int main(int argc, char** argv)
{
int clientFd,ret;
struct sockaddr_in serveraddr;
char buf[MAXBUFFER];
clientFd=socket(AF_INET,SOCK_STREAM,0);//创建socket
if(clientFd < 0)
{
printf("socket error:%s\n",strerror(errno));
exit(-1);
}
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(SERVERPORT);
inet_pton(AF_INET,SERVERIP,&serveraddr.sin_addr);
ret=connect(clientFd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));//连接到服务器
if(ret!=0)
{
close(clientFd);
printf("connect error:%s\n",strerror(errno));
exit(-1);
}
while(1)
{
bzero(buf,sizeof(buf));
scanf("%s",buf);
write(clientFd,buf,sizeof(buf));//写数据
bzero(buf,sizeof(buf));
read(clientFd,buf,sizeof(buf));//读数据
printf("echo:%s\n",buf);
}
close(clientFd);
return (EXIT_SUCCESS);
}
在这里需要注意一个问题:网络字节序问题
内存中存储字节有两种方式,一种是小端字节序(低序字节储存在起始地址),第二种是大端字节序(高位字节存储在起始地址)。而且这两种字节序都有在使用。当两台机器通信时,为了避免字节序之间转换问题,系统提供了4个函数
#include
uint32_t ntohl (uint32_t __netlong) ;
uint16_t ntohs (uint16_t __netshort);
uint32_t htonl (uint32_t __hostlong);
uint16_t htons (uint16_t __hostshort);
用这个程序说明一下,ctrl-z,ctrl-c,ctrl-d的差别
ctrl-c 发送 SIGINT 信号给前台进程组中的所有进程。常用于终止正在运行的程序。
从上面那个图可以看出,ctrl+c终止客户端程序
ctrl-z 发送 SIGTSTP 信号给前台进程组中的所有进程,常用于挂起一个进程。
ctrl+z,将程序挂起。fg命令重新启动前台被中断的任务,bg命令把被中断的任务放在后台执行.
ctrl-d 不是发送信号,而是表示一个特殊的二进制值,表示 EOF。
程序不足之处: