- 创建一个socket , 使用socket()
int socket(int domain, int type, int protocol);
功能: 创建一个socket 文件
参数:
domain :这个socket 是用网络通信还是做进程间通信
AF_UNIX, AF_LOCAL Local communication 进程间通信
AF_INET IPv4 Internet protocols
AF_INET6 IPv6 Internet protocols
type :
SOCK_STREAM : 使用tcp协议的socket
SOCK_DGRAM : 使用udp协议的socket
SOCK_RAW : 没有协议的socket , Provides raw network protocol access.
protocol :通信方式 , 默认为0
返回值:
On success, a file descriptor for the new socket is returned.
On error, -1 is returned, and errno is set appropriately.
- 给socket 一个固定的ip和端口
做服务器必须要一个固定的ip 还要有一个固定的端口
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:给socket 一个名字
参数: 要操作socket
addr: 要给名字的地址结构体指针
struct sockaddr {
sa_family_t sa_family; // 地址协议, 用于网络还是本地通信
char sa_data[14]; // 地址信息, 这个是通用形式,
}
对于网络通信需要使用另外一个结构体描述:
struct sockaddr_in {
__kernel_sa_family_t sin_family; /* Address family / 2字节
__be16 sin_port; / Port number / 2字节
struct in_addr sin_addr; / Internet address */ 4字节
char sin_zero[8]; // 8 bytes unused,作为填充
};
IPv4地址结构
// internet address
struct in_addr
{
in_addr_t s_addr; // u32 network address
};
addrlen : 地址结构的大小
返回值:
On success, zero is returned.
On error, -1 is returned, and errno is set appropriately.
- inet_addr 把一个点分十进制的ip(“192.168.1.10”)转换成网络ip地址(32位无符号的地址)
in_addr_t inet_addr(const char *cp);
参数:
cp : 字符串类型的ip地址 “192.168.1.19”
返回值:
错误: -1
成功过: 转换后的ip
- listen 改变socket的属性, 让这个socket变成服务器socket , 处于监听状态
int listen(int sockfd, int backlog);
sockfd:监听连接的套接字
backlog
指定了正在等待连接的最大队列长度,它的作用在于处理可能同时出现的几个连接请求。
DoS(拒绝服务)攻击即利用了这个原理,非法的连接占用了全部的连接数,造成正常的连接请求被拒绝。
返回值: 0 或 -1
-
查看建立的socket 信息
netstat -nat -
accept 接收客户端的请求
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd : 要操作的socket
addr : 保存对方的地址(对方的ip和对方端口)
addrlen : 对方地址的大小
返回值 :
On success, these system calls return a nonnegative integer that is a descriptor for the accepted socket.
成功连接后, 会新创建一个socket 和客户端进行一对一的通信 ,返回值就是问价描述符
On error, -1 is returned, and errno is set appropriately.
-
telnet 192.168.1.57 8000
测试服务器连接 -
tcp 服务器创建流程
socket()
bind()
listen()
while(1)
{
newsockfd = accept()
printf()
close(newsockfd);
}
tcp 客户端创建流程
socket()
connect(); // 连接服务器
close(sockfd)
-
连接服务器 connect
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:发起一次连接
参数:
sockfd :要操作的socket
addr :发链接的服务器地址
addrlen : 地址的大小
返回值:
成功: 0
失败: -1 -
字节序
0x11223344
31—28 27—24 23----20 19—16 15—12 11—8 7----4 3----0
高地址 | | | 地地址
小端: 低地址存低字节
11 22 33 44
大端: 低地址存高字节
44 33 22 11
根据应用分为:
主机字节序 :
intel : 小端
poerpc: 大端
网络字节序 : 大端
主机字节序转网络字节序:
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
网络转主机:
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
tcp 文件服务器
-
list
-
get filename
-
put filename
-
quit
-
sendto 发送数据, 主要应用于udp通信中
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
参数:
sockfd :要发送数据socket
buf : 数据保存的内存地址
len :发送数据的长度
flags :发送方式, 一般为0
dest_addr : 要发送数据的地址, 这个地址sockaddr_in 类型的地址
addrlen : 地址的长度
On success, these calls return the number of characters sent.
On error, -1 is returned, and errno is set appropriately.
- recvfrom 接收数据, 主要应用于udp通信中
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
参数: 要读数据的socket
buf : 数据存放的内存地址
len :要读多少个字节的数据
flags :发送方式, 一般为0
src_addr : 发送方的地址
addrlen : 发送方的地址长度
返回值:
These calls return the number of bytes received,
-1 if an error occurred.
思路: udp服务器实现
server.c
socket(AF_INET,SOCK_DGRAM,0);
bind()
while(1)
{
recvfrom(buf);
printf("%s",buf)
strcpy(buf,“服务器已经收到,拜拜”);
sendto(buf);
}
client.c
socket(AF_INET,SOCK_DGRAM,0);
while(1)
{
printf(">:")
fgets(buf);
sendto(buf);
recvfrom(buf);
printf(buf);
}
IO多路复用
- select 或poll
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
功能: 可以同时对多个IO进行监控(时候可读是否可写)
参数:
nfds: 一共要监控的文件个数 ,最大的文件描述符
readfds :可读监控表
writefds: 可写的监控表
exceptfds : 其他的监控表
timeout :超时时间
NULL:表示使用阻塞机制, 检查完后,不满足会阻塞
不为NULL, 为具体的超时时间
返回值:
成功: 可以操作io的个数
失败:错误 -1
void FD_CLR(int fd, fd_set *set); 把一个文件从表中清除
int FD_ISSET(int fd, fd_set *set); 判断一个文件时候准备就绪, 成功为真, 失败为假
void FD_SET(int fd, fd_set *set); 把一个文件加入表中
void FD_ZERO(fd_set *set); 初始化一个表
思路: 同时监控 accept 和fgets(stdin)
并发服务器:
tcp 并发 :
socket()
bind()
listen()
while(1)
{
newsockfd = accept()
if(fork() == 0 )
{
while(1)
{
read();
printf();
write();
}
exit(0);
}
close(newsockfd);
}
select 并发
I/O多路复用并发服务器
DNS: 是把www.xxx.com 这个域名给转成成ip , 这个转换需要dns服务器,这个服务器实现的功能就是dns
常用的dns服务器有: 114.114.114.114
google : 8.8.8.8
gethostbyname() 根据主机名取得主机信息