网络编程–各种函数使用方法
1、TCP/IP协议
传输控制/网际协议(Transfer Control Protocol/Internet Protocol) 又称作网络通讯协议
四个层次:网络接口层、网际层、传输层、应用层
2、网络编程常用函数:
网络编程头文件:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <unistd.h>
/*****************socket() -- 创建套接字*********************/
int socket(int domain, int type, int protocol);
domain是地址族
PF_INET // internet 协议 // AF_INET
PF_UNIX // unix internal协议
PF_NS // Xerox NS协议
PF_IMPLINK // Interface Message协议
type //套接字类型
SOCK_STREAM // 流式套接字
SOCK_DGRAM // 数据报套接字 SOCK_RAW // 原始套接字
protocol 参数通常置为0
返回值:
成功:套接字,linux系统下特殊的文件描述符
失败:-1(系统调用失败都是-1,错误码保存在进程的全局变量errno中)
例:int sockfd = socket(PF_INET, SOCK_STREAM, 0);
/*****************bind() -- 绑定本机地址和端口*********************/
int bind(int sockfd, struct sockaddr *my_addr, int addrlen) ;
头文件:
#include <sys/types.h>
#include <sys/socket.h>
sockfd: socket调用返回的文件描述符
addrlen: sockaddr地址结构的长度 返回值:0或-1
例:int ret = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
Internet协议地址结构
struct sockaddr_in // struct sockaddr
{
u_short sin_family; // 地址族, AF_INET,2 bytes
u_short sin_port; // 端口,2 bytes
struct in_addr sin_addr; // IPV4地址,4 bytes // .sin_addr.s_addr =
char sin_zero[8]; // 8 bytes unused,作为填充
};
端口号:1-65535,其中0不使用
1-1023: 系统端口,这些端口只有系统特许的进程才能使用;
1024-5000: 一般的应用程序使用1024到4999来进行通讯;
5001-65535: 服务器(非特权)端口,用来给用户自定义端口.
地址结构的一般用法:
1、 定义一个struct sockaddr_in类型的变量并清空
struct sockaddr_in myaddr;
memset(&myaddr, 0, sizeof(myaddr));
2、 填充地址信息
myaddr.sin_family = PF_INET;
myaddr.sin_port = htons(8888); //8888
myaddr.sin_addr.s_addr = inet_addr(“192.168.1.100”); 服务器中一般填 INADDR_ANY
//myaddr.sin_addr.s_addr = INADDR_ANY;
//myaddr.sin_zero = {0};
3、 将该变量强制转换为struct sockaddr类型在函数中使用
bind(listenfd, (struct sockaddr*)(&myaddr), sizeof(myaddr));
/*****************listen() -- 设置监听套接字*********************/
int listen (int sockfd, int backlog);
sockfd:监听连接的套接字
backlog:
指定了正在等待连接的最大队列长度,它的作用在于处理可能同时出现的几个连接请求。
DoS(拒绝服务)攻击即利用了这个原理,非法的连接占用了全部的连接数,造成正常的连接请求被拒绝。
返回值: 0 或 -1
完成listen()调用后,socket变成了监听socket(listening socket).
/*****************accept() -- 接受TCP连接*********************/
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) ;
头文件
#include <sys/types.h>
#include <sys/socket.h>
sockfd : 监听套接字
addr : 对方地址
addrlen 长度地址
返回值:已建立好连接的套接字(读写套接字)或-1
listen()和accept()是TCP服务器端使用的函数
/*****************connect() -- 建立连接*********************/
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
头文件:
#include <sys/types.h>
#include <sys/socket.h>
sockfd : socket返回的文件描述符
serv_addr : 服务器端的地址信息
addrlen : serv_addr的长度
返回值:0 或 -1
connect()是客户端使用的系统调用
/*****************recv(),read() -- 数据接收*********************/
ssize_t recv(int socket, const void *buffer, size_t length, int flags);
返回值:
成功:实际接收的字节数
失败:-1, 并设置errno
头文件:
#include <sys/socket.h>
buffer : 发送缓冲区首地址
length : 发送的字节数
flags : 接收方式(通常为0)
ssize_t read(int fd, void *buf, size_t count);
/*****************send(),write() -- 数据发送*********************/
ssize_t send(int socket, const void *buffer, size_t length, int flags);
返回值:
成功:实际发送的字节数
失败:-1, 并设置errno
头文件:
#include <sys/socket.h>
buffer : 发送缓冲区首地址
length : 发送的字节数
flags : 发送方式(通常为0)
ssize_t write(int fd, const void *buf, size_t count);
/*****************close() -- 关闭套接字*********************/
int close(int sockfd);
关闭双向通讯;
TCP服务器端流程:
socket -> bind -> lisent-> accept -> send/recv -> close(关闭后回到accept之前)
TCP客户端流程:
socket -> bind(可选) -> connect -> send/recv -> close
循环服务器模型:
TCP服务器
1、 TCP服务器端运行后等待客户端的连接请求
2、 TCP服务器接受一个客户端的连接后开始处理,完成了客户的所有请求后断开连接
3、 TCP循环服务器一次只能处理一个客户端的请求
4、 只有在当前客户的所有请求都完成后,服务器才能处理下一个客户的连接/服务请求
5、 如果某个客户端一直占用服务器资源,那么其它的客户端都不能被处理。TCP服务器一般很少采用循环服务器模型
TCP循环服务器流程如下:
socket(...);
bind(...);
listen(...);
while(1)
{
accept(...);
while(1)
{
recv(...);
process(...);
send(...);
}
close(...);
}