Socket编程原理
Socket是网络通信端点的一种抽象,它提供了一种发送和接收数据的机制。
- 流socket(SOCK_STREAM):双向、有序、无重复、并且无记录边界
- 数据报Socket(SOCK_DGRAM):双向、保留记录边界
面向连接的操作比无连接的操作效率低,但数据的安全性更高。
Socket通信流程
IP地址标志主机,端口号标志进程,IP加上端口号才能确定连接目标。
一个网络连接需要以下5种信息:
- 本地协议端口:指出接收报文或数据的进程。
- 本地主机地址:指出接收数据包的主机。
- 远程协议端口:指出目的进程或成。
- 远程主机地址:指出目的主机。
- 协议:指出程序在网络上传输数据时使用的协议。
Socket函数(Winsock)
1.Socket:用于创建一个Socket套接字。
SOCKET Socket( int af, //使用的协议族 int type, //Socket类型 int protocol //使用的协议地址 );
Socket协议族在计算机里表示为一个整数,可以取值AF_INET
Socket类型有两种:SOCK_STREAM和SOCK_DGRAM代表流Socket和数据报Socket
函数如果成功,返回一个Socket描述字,否则,返回INVALID_SOCKET。
SOCKET s = socket(AF_INET,SOCK_STREAM,0);
2.Connect:用于尝试与远端建立一个Socket连接。
int Connect( SOCKET s, //socket描述符 const struct sockaddr* name, //远端的地址 int namelen //远端地址的长度 );
远端地址是一个SOCKADDR的结构
struct sockaddr_in( short sin_family, //socket族 u_short sin_port, //端口 struct in_addr sin_addr, //IP地址 char sin_zero[8] //结构长度 );
函数如果连接成功,返回0,否则返回SOCKET_ERROR.对于非阻塞模式的Socket连接,返回结果通常是SOCKET_ERROR,并且错误代码为WSAEWOULDBLOCK,表示连接正在进行,而不是一个真正的错误。
建立连接通常是客户端发出连接请求:
SOCKET s; SOCKADDR_IN ServerAddr; ServerAddr.sin_family=AF_INET; ServerAddr.sin_port=htons(Port); ServerAddr.sin_addr.s_addr=inte_addr("127.0.0.1"); connect(s,(SOCKADDR*)&ServerAddr,sizeof(ServerAddr));
3.Send:用于在某个Socket上向远端发送数据
int send( SOCKET s, //Socket套接字 const char* buf, //存放发送数据的缓冲区 int len, //将要发送的数据长度 int flags //发送时使用的附加参数 );
如果发送成功,返回值为成功发送的字节数,否则返回SOCKET_ERROR。
4.Recv:与发送数据相对应的就是接收数据,函数Recv用于接收端发送的数据,函数原型为:
int recv( SOCKET s, //Socket套接字 char* buf, //存放接收数据的缓冲区 int len, //将要接收的数据长度 int flags //接收时使用的附加参数 );
接收数据成功,函数返回接收的字节数,否则,返回SOCKET_ERROR。
5.Closesocket:用于关闭不再需要的Socket
int closesocket( SOCKET s; //socket套接字 );
关闭成功,返回0,否则返回SOCKET_ERROR。
6.Listen:用于在某个Socket上建立监听
int listen( SOCKET s, //Socket套接字 int backlog //缓存队列的长度 );
如果建立监听成功,返回0;否则返回SOCKET_REEOR
backlog参数。设为SOMAXCONN,表示取系统连接的最大值。
7.Accept:用于接收一条新的连接。
SOCKET accept( SOCKET s,//监听中的Socket struct sockaddr* addr ,//表示地址结构体的指针 int* addrlen//地址结构体的长度 );
函数执行成功,返回0;否则,返回SOCKET_ERROR.
8.Bind:用于给一个Socket套接字分配一个本地协议地址
int bind( SOCKET s,//Socket套接字 const struct socketaddr* name,//表示地址结构体的指针 int namelen//地址结构体的长度 );
函数执行成功,返回0;否则,返回SOCKET_ERROR.
9.Select:用于检测Socket状态,主要用于高级的网络通信模型
int select( int nfds,//winsocj中此参数无意义 fd_set* readfds,//进行可读检测的Socket fd_set* writefds,//进行可写检测的Socket fd_set* exceptfds,//进行异常检测的Socket const struct timeval* timeout//非阻塞模式中设置最大等待时间 );
函数执行成功,返回0;否则,返回SOCKET_ERROR.
IP地址转换
- 无符号整数:127.0.0.1
- ASCII地址:"127.0.0.1"
- 域名:localhost
ASCII地址->整数地址
#include <arpa/inet.h> int inet_aton(const char* straddr,struct in_addr* adrp);
返回:0表示转换不成功;1表示转换成功。
整数地址->ASCII地址
#include <arpa/inet.h> char *inet_ntoa(struct in_addr inaddr);
返回:NULL编程转换不成功;其他返回值表示转换成功。
域名地址->整数地址
#include <netdb.h> struct hostent *gethostname(const char *name);
整数地址->域名地址
#include <netdb.h> struct hostent *gethostbyaddr(const char *addr,int len,int family);
字节转换
存储字节的格式:
- 网路字节顺序:高字节在前(Big Endian)
- 本机字节顺序:低字节在前(Little Endian)
函数命名规律:
h代表字节顺序(本机顺序),n代表网络顺序(network)
//本地字节转化为网络字节顺序(长整数) u_long PASCAL FAR htonl(IN u_long hostlong); //本地字节转化为网络字节顺序(短整数) u_short PASCAL FAR htons(IN u_short hostshort); //网络字节转化为本地字节顺序(长整数) u_long PASCAL FAR ntohl(IN u_long netlong); //网络字节转化为本地字节顺序(短整数) u_short PASCAL FAR ntohs(IN u_short netshort);