一、基本套接字函数
socket,connect,bind,listen,accept,close
函数socket创建的套接字是主动套接字,可以用来进行主动连接(调用connect),但是不能接收连接请求,
而服务器的套接字必须能够接受客户机的请求,函数listen的作用就是将一个尚未连接的主动套接字转换成一
个被动套接字(称之为倾听套接字):告诉TCP协议,可以接受连接请求。
倾听套接字是专门用来接收客户机连接请求,完成3次握手操作而用的,TCP不能使用它来标识TCP连接,这样就
需要创建一个新的套接字来标识这个要接收的连接,并将他的描述符返回给应用程序,此即accept的作用。
说明:一个服务器进程通常只需要创建一个倾听套接字,在服务器进程的整个活动期间,用它来接收所有客户机的
连接请求,在服务器进程终止前close这个倾听套接字,而对于每个accept的连接,TCP都创建一个新的连接套接字
来标识这个连接,当服务器处理完这个客户机的请求时,就可以close这个套接字。
二、读写数据函数
read,write,send,receive,sendto,recvfrom(以后介绍)
与读写文件操作类似
read操作时:3种返回结果:>0,=0,<0;分别对应正常返回,读到文件结束符和读错误。
write操作时,2种返回结果:>0,<0;分别表示正常与错误。
可以编写两个读写函数包含错误处理,以方便使用,如
#include<errno.h> #include<unistd.h> int read_all(int fd,void*buf,int n) { int nleft; int nbytes; char *ptr; ptr=buf; nleft=n; for(;nleft>0;){ nbytes=read(fd,ptr,nleft); if(nbytes<0){ if(errno==EINTR)nbytes=0; else return -1; } else if(nbytes==0)break; nleft-=nbytes; ptr+=nbytes; } return n-nleft; } int write_all(int fd,void*buf,int n) { int nleft; int nbytes; char *ptr; ptr=buf; nleft=n; for(;nleft>0;){ nbytes=write(fd,ptr,nleft); if(nbytes<=0){ if(errno==EINTR)nbytes=0; else return -1; } nleft-=nbytes; ptr+=nbytes; } return n; }三、其他函数
getsockname和getpeername
分别返回套接字的本地地址和对应的远程地址。
1.域名查找函数
gethostbyname,gethostbyaddr,uname,gethostname
struct hostent* gethostbyname(const char*hostname);
查询指定的域名地址所对应的IP地址,成功返回一个hostent指针结构,否则返回NULL。
struct hostent{ char *h_name;//主机正式名称 char **h_aliases;//别名列表 int h_addrtype;//地址类型,AF_INET for ipv4 int h_length;//地址长度,32bit for ipv4 char **h_addr_list;//主机的ip地址列表 }; #define h_addr h_addr_list[0];
2.查询指定的IP地址对应的域名
#include<netdb.h>
struct hostent *gethostbyaddr(const char *addr,size_t len,int family)
参数addr实际上是一个指向in_addr结构类型的指针,包含要查询的ip地址,len指示其长度family=AF_INET。返回的结果位于hostent结构的h_name域。
【在网络程序中为了便于使用,应能处理ip地址和域名地址两种地址形式,对于一个给定的地址,一般先假设是个ip地址,用inet_aton处理,如果失败,在进行域名查找。比如下面的通用地址处理函数】
int addr_generic(char*address,struct in_addr *inaddr){
struct hostent *he;
if(inet_aton(address,inaddr)==1)return 1;
he=gethostbyname(address);
if(he!=NULL){
*inaddr=*(struct in_addr*)he->he_addr->list[0];
return 1; }
else return 0;
}
3.uname与gethostname返回本机器的域名地址
一般用uname获取本机的名称,作为gethostname的参数获取本机ip。
如uname(&myname);//struct utsname myname;
he=gethostname(myname.nodename)
4.服务有关的函数
getservbyname,getservbyport
struct servent*getservbyname(const char*servname,const char*protostrname);//查找服务的端口号
struct servent *getservbyport(int port,const char *protoname)//查找端口的服务
端口均是网络字节顺序的。
struct servent{
char *s_name;
char **s_aliases;
int s_port;
char *s_proto;
};
四、网络数据格式
文本字符串,二进制数据无需进行特殊的处理,整数的发送和接收需要进行一些处理。
16位,32位可以借助htons&ntohs和htonl&ntohl转换,64位的则需要借助函数vsprintf和sscanf
比如:void sender(int sockfd,long data)
{ char buf[10];
sprintf(buf,"%ld",data);
write(sockfd,buf,strlen(buf));
}
void receiver(int sockfd,long *data)
{
char buf[10];
read(sockfd,buf,sizeof(buf));
sscanf(buf,"ld",data);
}
指针数据的传输:只能传输指针所指的具体内容;
结构体数据的传输:发送方依次传送结构体中各成员变量,而接收方使用相同的顺序接收个成员变量。
浮点数据的传输:可以字符串形式发送和接收。
本文参考《linux网络编程》