一、Socket编程步骤
TCP服务器编程步骤:
1、创建Socket套接字,用Socket ()函数
2、设置Socket属性,用setsockopt()也可以不用
3、为套接字添加信息(IP地址和端口号)用bind()函数
4、开启监听,用listen()函数
5、监听到有客户端接入,接受一个连接,用accept()函数
6、交换数据,用函数send()和recv(),或者用read(),write()
7、关闭套接字,断开连接
TCP客户端编程步骤:
1、创建一个Socket,用Socket函数
2、设置Socket属性,用setsockopt()也可以不用
3、连接服务器,用connect()函数
4、交换数据,用函数send()和recv(),或者用read(),write()
5、关闭网络连接
二、API解析
头文件
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
1、连接协议(Socket函数)
作用: 用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源的函数
函数原型
int socket(int domain, int type, int protocol);
domain: 指明所使用的协议族,通常为AF_INET 表示互联网协议族(TCP/IP协议族)
常用的domain类型有:
AF_INET IPv4因特网域
AF_INET6 IPv6因特网域
AF UNIX Unix 域
AF ROUTE 路由套接字
AFKEYE 钥套接字
AF UNSPEC 未拖定
type: 指定socket类型。
SOCK_STREAM
TCP: 流式套接字提供可靠的、面向连接的通信流:它使用TCP协议,从而保证了数据传输的正确性和顺序性
SOCK_DGRAM
UDP: 数据报套接字定义了一种无连按的服,数据通过相互独立的报文进行传输, 是无序的,并且不保证是可靠,无差错的。它使用数据报协议UDP。
SOCK_RAW
允许程序使用低层协议,原始科接字允许对底层协议如IP或ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开友。
protocol通常赋值为 0 ,0表示选择type类型对应得默认协议。
IPPROTO_TCP :TCP传输协议
IPPTOTO_UDP: UDP 传输协议
IPPROTO_SCTP :STCP传输协议
IPPROTO_TIPC :TIPC传输协议
返回值: 成功时返回非负整数,形如文件描述符,失败返回 -1。
2、地址准备(bind函数)
作用:用于绑定IP地址和端口号到Sockfd
函数原型
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd: 一个socket描述符,由socket函数调用返回
addr: 是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针,指向要邦定给sockfd的协议地址结构,这个地址结构根据地址创建,socket时的地址协议族的不同而不同。
IPV4对应的是:
struct sockaddr{
unisgned short as family; //协议族
char sa data[14]; //IP+端口
};
但是常用的为
struct sockaddr_in{
sa_family_t sin_family; //协议族
in_port t sin_port; //端口号
struct in_addr sin_addr;//IP地址结构体
unsigned char sin_zero[8];//填充 没有实际意义,
//只是为跟sockaddh结构在内存中对齐这样两者才能相互转换
};
addrlen: 第二个参数中结构体的大小,一般用sizeof计算,即sizeof(struct sockaddr_in);
3、监听 (listen函数)
作用: 在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接。规定内核为相应套接字排队的最大连接数。
listen函数一般在调用bind之后-调用accept之前调用。
函数原型
int listen(int sockfd, int backlog);
sockfd: socket系统调用返回的服务器端,socket描述符
backlog: 指定在请求队列中允许的最大请求数
4、连接(accept函数)
作用: accept函数由TCP服务器调用,用于从已完成连接队列队头返回下一个已完成连接。如果已完成连接队列为空,那么进程被投入睡眠。
函数原型
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd: socket系统调用返回的服务器端,socket描述符。
addr: 用来返回已经连接的客户端的协议地址。
addrlen: 客户端地址长度
返回值: 该函数的返回值是一个新的套接字描述符。连接成功后对数据的操作,使用的都是这个新的套接字描述符。
返回值是表示已连接的套接字描述符,而第一个参数是服务器监听接字描述行。一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建一个已连套接字(表示TCP三次握手已完成),当服务器完成对某个给定客户的服务时,相应的已连接套接字就会被关闭。
5、TCP数据收发(第一套)
这一套函数和文件的读写函数相同
头文件
#include <unistd.h>
函数原型
ssize_t write(int fd, const void *buf, size_t count);
ssize_t read(int fd, void *buf, size_t count);
write: 将buf中的count个字节写入到文件描述符fd中,成功返回写入的字节数,
read: 从fd中读取count个字节到buf中,返回实际所读的字节数,
6、TCP数据收发(第二套)
(1)发送
将buf中的nbytes个字节写入到socket描述符sockfd中,成功时返回写的字节数。
函数原型
ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);
sockfd: sockfd为已建立好连接的Socket文件描述符
buf: buf指向存放待发送数据的缓冲区
nbytes: 待发送数据的长度
flags: 控制选项,一般为0
(2)接收
从sockfd中读取nbytes个字节到buf中,返回实际所读的的字节数。
函数原型
ssize_t read(int sockfd,void *buf,size_t nbyte);
sockfd: sockfd为已建立好连接的文件描述符
buf: buf指向存放待接收数据的缓冲区
nbytes: nbytes为待接收数据的长度
返回值: 函数返回读或写的字节个数,出错则返回-1
其他的网络I/O还有一些函数如:recv() /send()、readv()/witev()、recvmsg()/sendnsg()、recvfrom()/sendto()
7、客户端的连接(connect函数)
作用: 用于绑定之后client端(客户端),与服务器建立连接参数
函数原型:
int connect (int sockfd, struct sockaddr * addr, socklen_t addrlen);
sockfd: 套接字描述符
addr: 指向数据结构sockaddr的指针,其中包括目的端口和IP地址
addrlen: 参数二sockaddr的长度,可以通过sizeof(struct sockaddr)获得
返回值: 成功则返回0,失败返回非0,错误码Error()
8、地址转化API:
(1)字符串转网络
作用: 把字符串形式的”192.168.1.123"转为网络能识别的格式
函数原型:
int inet_aton(const char* straddr,struct in_addr *addrp);
参数描述:
straddr: 输入参数straddr包含ASCII表示的IP地址。
addrp: 输出参数addrp是将要用新的IP地址更新的结构。
返回值: 如果这个函数成功,函数的返回值非零,如果输入地址不正确则会返回零。使用这个函数并没有错误码存放在errno中,所以它的值会被忽略。
(2)网络转字符串
作用: 把网络格式的ip地址转为字符串形式、一个32位网络字节序的二进制IP地址转换成相应的点分十进制的IP地址(返回点分十进制的字符串在静态内存中的指针)。
函数原型
char* inet_ntoa(struct in_ addr inaddr);
inaddr: 输入参数straddr包含ASCII表示的IP地址。
返回的字符串存放在WINDOWS套接口实现所分配的内存中。
返回值: 若无错误发生,inet_ntoa()返回一个字符指针。否则,返回NULL。其中的数据应在下一个WINDOWS套接口调用前复制出来。