int socket(int domain, int type,int protocol)
说明:
1. domain:通讯协族(AF_UNIX | AF_INET | ...etc.).
AF_UNIX用于Unix系统IPC ; AF_INET用于Internet,允许远程主机间通信
当man socket时发现domain选项是PF_*,是因为glibc是posix的实现,不过我们都可以使用的
2.
type:通讯协议(SOCK_STREAM | SOCK_DGRAM | ...etc.)
SOCK_STREAM: TCP协议 ; SOCK_DGRAM: UDP协议
3.
protocol: 由于指定了type,所以这里一般只要用0来代替
4.
成功,返回文件描述符;失败,返回 -1,看error可知道出错的详细情况.
/********************************************************************************************************************************************/
int bind(int sockfd, struct sockaddr *my_addr, int addrlen)
说明:
1. sockfd:文件描述符.
2. addrlen: struct sockaddr的长度.
3. my_addr:指向sockaddr的指针.
struct sockaddr{
unisgned short as_family;
char sa_data[14];
};
由于系统的兼容性,一般使用struct sockaddr_in来代替
struct sockaddr_in{
unsigned short sin_family; //一般为AF_INET(internet)
unsigned short int sin_port; //要监听的端口号
struct in_addr sin_addr; //一般为INADDR_ANY表示可以和任何的主机通信
unsigned char sin_zero[8]; //用bzero填充
};
struct in_addr
{
unsigned long s_addr;
};
4.
成功返回0,失败返回 -1,看error可知道出错的详细情况.
/********************************************************************************************************************************************/
int listen(int sockfd, int backlog);
1. sockfd:调用socket() 返回的套接字文件描述符
2. backlog:队列允许的连接数目。解释:进入的连接在队列等待accept(),其数目限制于队列的允许。多数系统允许数目20。
3. 发生错误的时候返回-1,并设置全局错误变量errno
/********************************************************************************************************************************************/
int accept(int sockfd, struct sockaddr *addr,int *addrlen)
说明:client通过listen()的端口号connect()到server,其连接进入等待accept()的队列中.
1. sockfd:listen后的文件描述符.
2. addr, addrlen用来给客户端的程序填写,服务器端只要传递指针就可以了.
3. 成功时返回最后的服务器端的fd,此时可以向该fd写信息了.失败时返回-1
/********************************************************************************************************************************************/
int connect(int sockfd, struct sockaddr * serv_addr,int addrlen)
1. sockfd
2. serv_addr:保存目的地端口和IP地址
3. addrlen:sizeof(struct sockaddr)。
4. 成功返回0,失败时返回-1.
/**********************************************************************************************************************************************/
===================
|| 服务器和客户机的信息函数 ||
===================
字节序转换函数
unsigned long int htonl(unsigned long int hostlong) //将本机器的long数据转化为网络上的long
unsigned short int htons(unisgned short int hostshort)
unsigned long int ntohl(unsigned long int netlong)
unsigned short int ntohs(unsigned short int netshort)
h:host
n:network
s:short
l:long
/**********************************************************************************************************************************************/
IP与域名的转换:在网络上标志一台机器可以用IP或者是用域名.
struct hostent *gethostbyname(const char *hostname) //将机器名(如 linux.yessun.com)转换为一个结构指针,此结构储存了域名的信息
struct hostent *gethostbyaddr(const char *addr,int len,int type) //将32位IP地址转换为结构指针.
说明:
这两个函数失败时返回NULL 且设置h_errno错误变量,调用h_strerror()可以得到详细的出错信息
struct hostent{
char *h_name; /* 主机名称:ip或域名*/
char *h_aliases; /* 主机的别名*/
int h_addrtype; /* 主机地址类型(AF_INET...)*/
int h_length; /*主机地址长度,对于IP4是4字节32位*/
char **h_addr_list; /*主机的IP地址列表 */
}
#define h_addr h_addr_list[0] /* 主机的第一个IP地址*/
/**********************************************************************************************************************************************/
字符串IP和32位IP转换.
int inet_aton(const char *cp,struct in_addr *inp) //将a.b.c.d的IP转换为32位的IP,存储在inp指针里面
char *inet_ntoa(struct in_addr in) //将32位IP转换为a.b.c.d的格式
/**********************************************************************************************************************************************/
获得服务信息函数(端口 | IP | 服务信息...etc.)
int getsockname(int sockfd,struct sockaddr *localaddr,int *addrlen)
int getpeername(int sockfd,struct sockaddr *peeraddr, int *addrlen)
struct servent *getservbyname(const char *servname,const char *protoname) //得到指定服务的端口号
struct servent *getservbyport(int port,const char *protoname) //得到指定端口号的服务
说明:
一般很少用这几个函数.对应客户端,当我们要得到连接的端口号时在connect调用成功后使用可得到系统分配的端口号.
对于服务端,用INADDR_ANY填充后,可在accept调用成功后得到IP地址.
struct servent
{
char *s_name; //服务名
char **s_aliases; //别名列表
int s_port; //端口号
char *s_proto; //使用的协议
};
一个例子:
========================getServerInfo_internet.c========================
#include
#include
#include
int main(int argc ,char **argv)
{
struct sockaddr_in addr;
struct hostent *host;
char **alias;
if(argc<2)
{
fprintf(stderr,"Usage:%s hostname|ip..\n\a",argv[0]);
exit(1);
}
argv++;
for(;*argv!=NULL;argv++)
{
if(inet_aton(*argv,&addr.sin_addr)!=0)
{
host=gethostbyaddr((char *)&addr.sin_addr,4,AF_INET);
printf("Address information of Ip %s\n",*argv);
}
else
host=gethostbyname(*argv); printf("Address information of host %s\n",*argv);
if(host==NULL)
{
fprintf(stderr,"No address information of %s\n",*argv);
continue;
}
printf("Official host name %s\n",host->h_name);
printf("Name aliases:");
for( alias = host->h_aliases; *alias!=NULL ; alias++)
printf("%s ,",*alias);
printf("\n");
printf("\nIp address:\n");
alias=host->h_addr_list;
for(; *alias!=NULL; alias++)
printf("%s ,",inet_ntoa(*(struct in_addr *)(*alias)));
printf("\n");
}
}
/*****************************以下未读过**********************************/
一旦我们建立了连接,我们的下一步就是进行通
信了.在Linux下面把我们前面建立的通道看成是文件描述符,
这样服务器端和客户端进行通信时候,只要往文件描述符里面读写东西了.
就象我们往文件读写一样.
4.1 写函数write
ssize_t
write(int fd,const void *buf,size_t nbytes)
write函数将
buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节数.
失败时返回-1. 并设置errno变量.
在网络程序中,当我们向套接字文件描述符写时有俩种可能.
1)write的返回值大于0,表示写了部分或者是全部的数据.
2)返回的值小于0,此时出现了错误.我们要根据错误类型来处理.
如果错误为EINTR表示在写的时候出现了
中断错误.
如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接).
为了处理以上的情况,我们
自己编写一个写函数来处理这几种情况.
int my_write(int fd,void
*buffer,int length)
{
int bytes_left;
int
written_bytes;
char *ptr;
ptr=buffer;
bytes_left=length;
while(bytes_left>0)
{
/* 开始写*/
written_bytes=write(fd,ptr,bytes_left);
if(written_bytes<=0) /* 出错了*/
{
if(errno==EINTR) /* 中断错误 我们继续写*/
written_bytes=0;
else /*
其他错误 没有办法,只好撤退了*/
return(-1);
}
bytes_left-=written_bytes;
ptr+=written_bytes;
/* 从剩下的地方继续写 */
}
return(0);
}
4.2
读函数read
ssize_t read(int fd,void *buf,size_t
nbyte) read函数是负责从fd中读取内容.当读成功时, read返回实际所读的字节数,如果返回的值是0
表示已经读到文件的结束了,小于0表示出现了错误.如果错误为EINTR说明读是由中断引起的,
如果是ECONNREST表示网络连接出了问题. 和上面一样,我们也写一个自己的读函数.
int
my_read(int fd,void *buffer,int length)
{
int
bytes_left;
int bytes_read;
char *ptr;
bytes_left=length;
while(bytes_left>0)
{
bytes_read=read(fd,ptr,bytes_read);
if(bytes_read<0)
{
if(errno==EINTR)
bytes_read=0;
else
return(-1);
}
else if(bytes_read==0)
{
break;
}
bytes_left-=bytes_read;
ptr+=bytes_read;
}
return(length-bytes_left);
}
4.3
数据的传递
有了上面的两个函数,我们就可以向客户端或者是服务端传递数据了.比如我们要传递一个结构.可以使用如下方式
/* 客户端向服务端写 */
struct my_struct
my_struct_client;
write(fd,(void
*)&my_struct_client,sizeof(struct my_struct);
/*
服务端的读*/
char buffer[sizeof(struct my_struct)];
struct *my_struct_server;
read(fd,(void
*)buffer,sizeof(struct my_struct));
my_struct_server=(struct my_struct *)buffer;
在
网络上传递数据时我们一般都是把数据转化为char类型的数据传递.接收的时候也是一样的
注意的是我们没有必要在网络上传递指针(因为传递指针是没有任何意义的,我们必须传递指针所指向的内容)