分类:
2012-10-19 19:13:46
原文地址:Linux网络基础编程 作者:rojian
Socket是bsd提供的网络应用编程的接口,现在它已经是网络编程的标准;是一种特殊的进程间通信方式,不同机器上的进程都可以使用这种方式进行通信。
l 网络中的数据传输是一种I/O操作
l Socket也是一种文件描述符,它代表了一个通信管道的一个端点
l read,write,close操作可应用于Socket描述符
l 在socket类型的文件描述符上,可以完成建立连接,数据传输等操作
l 常用的Socket类型有两种
ü 流式Socket-SOCK_STREAM,提供面向连接的Socket
ü 数据报式Socket-SOCK_DGRAM,提供面向无连接的Socket
4.1.2、字节序(字节的顺序)l 字节序是一个处理器的架构特性
ü 大端格式
ü 小端格式
4.1.3、字节序转换函数#includeØ 以上返回网络字节序
uint32_t htonl(uint32_t hostint32);
uint16_t htons(uint16_t hostint16);
Ø 以上返回主机字节序
uint32_t ntohl(uint32_t netint32);
uint16_t ntohs(uint16_t netint16);
4.1.4、地址结构地址标识了特定通信域中的套接字端点,地址格式与特定通信域相关,为了使不同格式地址能被传入套接字函数,地址被强制转换成通用的地址结构sockaddr表示.
struct sockaddr{
sa_family_t sa_family;//协议
char sa_data[14];//ip地址
};
因特网地址定义在<netinet/in.h>中.在IPV4因特网域(AF_INET)中,套接字地址用如下结构sockaddr_in表示.
struct in_addr{
in_addr_t s_addr;
};
struct sockaddr_in{
sa_family_t sin_family;
in_port_t sin_port;//2
struct in_addr sin_addr;//4
unsigned char sin_zero[8];//8
};
4.1.5、网络地址与ip字符串转换
《1》inet_addr():
将一个点间隔地址转换成一个in_addr,返回一网络字节顺序表示的Internet地址。使用:server_addr.sin_addr.s_addr = inet_addr("192.168.0.10");
《2》inet_aton()
将字符串表示的网络地址转换为该地址数值的整数表示,返回的数字总是按照网络字节顺序的。
inet_addr与inet_aton的区别:
inet_aton认为255.255.255.255是无效的,不能对这个地址操作。而inet_addr可以。
《3》inet_ntoa()
给定一个数字网络地址,返回作为字符串的该地址的电地址表示。
《4》htons或htonl
将以主机字节顺序的数转换为一网络字节书序表示的数。htons转16位的(host to network short), htonl转32位的(host to network long);
通常用法:
server_addr.sin_port = htons(2222); //端口号
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY=0 所以等效于:server_addr.sin_addr.s_addr = INADDR_ANY;
INADDR_ANY: 表示任意的IP地址,一般为内核指定的,大多数系统取0。
htonl()简单说是一个把本机IP转化为网络协议中规定的格式的函数.也 就是所谓的大端模式或小端模式。
《5》常用函数inet_addr()、inet_aton()、inet_ntoa() 和 htons()。
《6》struct in_addr以一个32位无符号数来表示,通常需要用到点分十进制数串 与它之间的转换:int inet_pton(int family, const char *strptr, void *addrptr);
成功返回1,否则返回0
《7》const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
len代表strptr缓冲区的长度
#define INET_ADDRSTRLEN 16
4.2、tcp传输--面向连接#include
4.2.1、创建套接字
创建套接字是进行任何网络通信时必须做的第一步,创建一个用于网络通信的I/O描述符(套接字)
v 格式:int socket(int family, int type,int protocol);
v 参数:
u family:协议族
AF_INET,AF_INET6,AF_LOCAL,AF_ROUTE,AF_KEY
u type:套接口类型
SOCK_STREAM,SOCK_DGRAM,SOCK_SEQPACKET, SOCK_RAW
u protocol:协议类别
0,IPPROTO_TCP,IPPROTO_UDP,IPPROTO_SCTP
v 返回值:套接字
做为服务器需要具备的条件:
ü 具备一个可以确知的地址,以便让别人找到我
ü 使用socket创建一个套接字时,系统不会分配一个理想的端口
ü 让操作系统知道你是一个服务器,而不是一个客户端
ü 使用socket创建的是主动套接字,但是作为服务器,需要被动等待别人连接
ü 等待连接的到来
ü 对于面向连接的TCP协议来说,连接的建立才真正意味着数据通信的开始
ü 使用一个确知的端口来接收客户端的连接
4.2.2、绑定套接字bind函数将一个地址绑定到套接字
v 格式:int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);
v 参数:
sockfd:socket套接口描述字
myaddr:指向特定于协议的地址结构指针
addrlen:该地址结构的长度
v 返回值:0,成功;其他,失败
v 例子:
#include
unsigned short port = 8000;
struct sockaddr_in serverAddr;
bzero(&serverAddr, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
serverAddr.sin_addr.s_addr=htonl(INADDR_ANY);//inet_addr("192.168.220.13")
bind(listenfd, (struct sockaddr*)&serverAddr,sizeof(struct sockaddr));
4.2.4、监听套接字:让套接字成为被动的Listen函数可以将套接口由主动修改为被动,操作系统为该套接口设置一个连接队列,来记录所有连接到该套接口的连接
v 格式:int listen(int sockfd, int backlog);
v 参数:
sockfd:socket套接口描述字
backlog:连接队列的长度 //默认为5
v 返回值:0,成功;其他,失败
4.2.5、接收连接,并创建通信套接字等待连接的到来,Accept函数从连接队列中取出一个已经建立的连接,同时建立通信套接字;如果没有任何连接可用,则进入睡眠等待
v 格式:int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
v 参数:
sockfd:socket套接口描述字
cliaddr: 客户端地址
addrlen:客户端地址结构体长度
v 返回值:已连接的套接口
注:accept函数返回的是一个套接口,这个套接口代表了当前这个连接
v 例子:
char ip[INET_ADDRSTRLEN];
unsigned short port;
int connfd;
struct sockaddr_in cliAddr;
int addrlen;
bzero(& cliAddr, sizeof(cliAddr));
connfd = accept(listenfd, (struct sockaddr*)&cliAddr, &addrlen);
printf(“client ip=%s\n”, inet_ntop(AF_INET, &cliAddr.sin_addr.s_addr, ip, INET_ADDRSTRLEN));
read(connfd, buf, 1024);
作为客户端需要具备的条件:知道服务器的IP地址以及端口号
4.2.6、客户端需要主动跟服务器建立连接连接建立才可以开始传输数据(对于TCP协议)
v 格式:int connect(int sockfd,const struct sockaddr *addr,socklen_t len);
v 参数:
sockfd:socket套接口描述字
addr: 服务器地址
addrlen:服务器地址结构体长度
v 返回值:0,成功;其他,错误
connect函数建立连接之后不会产生新的套接口
#include
char *ip = “192.168.0.1”;
unsigned short port = 8000;
struct sockaddr_in serverAddr;
bzero(&serverAddr, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
//serverAddr.sin_addr.s_addr=inet_addr("192.1680.1");或
inet_pton(AF_INET, ip, &serverAddr.sin_addr.s_addr);
connect(sockfd, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr));
当连接建立后,通信的两端便具备了两个套接口
套接口也是一种文件描述符,所以read、write函数可以用于从这个通信管道取出或向其写入数据
int fd;
char buf[100];
fd = socket(AF_INET, SOCK_STREAM, 0);
connect(fd, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr));
……
write(fd, buf, 100);
read(fd, buf, 100);
close(fd);
4.2.7、套接字提供的发送接收函数v 发送函数
ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);//tcp
ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags,
const struct sockaddr *destaddr, socklen_t destlen);//ip
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
v 接收函数
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);//tcp
ssize_t recvfrom(int sockfd, void*restrict buf, size_t len, int flags,
struct sockaddr *restrict addr, socklen_t *restrict addrlen);//udp
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
4.2.8、使用close函数即可关闭套接字关闭一个代表已建立连接的套接字将导致另一端接收到一个0长度的数据包
ü 做服务器时
关闭socket创建的套接字将导致服务器无法继续接受新的连接,但不会影响 已经建立的连接;
关闭accept返回的套接字将导致它所代表的连接被关闭,但不会影响服务器 的监听
ü 做客户端时