文章目录
网络编程相关API
- socket() 创建套接字
- bind() 绑定本机地址和端口
- connect() 建立连接
- listen() 设置监听套接字,把主动套接字变成动套接字
- accept() 接受TCP连接 ,阻塞等待客户端连接请求
- recv(), read(), recvfrom() 数据接收
- send(), write(),sendto() 数据发送
- close(), shutdown() 关闭套接字
socket(创建socket文件描述符)
int socket (int domain, int type, int protocol);
参数说明
-
domain 是地址族
.PF_INET // internet 协议
.PF_UNIX // unix internal协议
.PF_NS // Xerox NS协议
.PF_IMPLINK // -
type // 套接字类型
.SOCK_STREAM // 流式套接字
.SOCK_DGRAM // 数据报套接字
.SOCK_RAW // 原始套接字
protocol 参数通常置为0
地址相关的数据结构
通用地址结构
struct sockaddr
{
u_short sa_family; // 地址族, AF_xxx
char sa_data[14]; // 14字节协议地址
};
Internet协议地址结构
struct sockaddr_in
{
u_short sin_family; // 地址族, AF_INET,2 bytes
u_short sin_port; // 端口,2 bytes
struct in_addr sin_addr; // IPV4地址,4 bytes
char sin_zero[8]; // 8 bytes unused,作为填充
};
IPv4地址结构
// internet address
struct in_addr
{
in_addr_t s_addr; // u32 network address
};
bind (绑定)
int bind (int sockfd, struct sockaddr* addr, int addrLen);
-
sockfd 由socket() 调用返回
-
addr 是指向 sockaddr_in 结构的指针,包含本机IP 地址和端口号
struct sockaddr_in
u_short sin_family // protocol family
u_short sin_port // port number
struct in_addr sin_addr //IP address (32-bits) -
addrLen : sizeof (struct sockaddr_in)
地址结构的一般用法
-
定义一个struct sockaddr_in类型的变量并清空 struct
sockaddr_in myaddr; memset(&myaddr, 0, sizeof(myaddr));
-
填充地址信息
myaddr.sin_family = PF_INET;
myaddr.sin_port = htons(8888);
myaddr.sin_addr.s_addr = inet_addr(“192.168.1.100”);
- 将该变量强制转换为struct sockaddr类型在函数中使用
if (bind(listenfd, (struct sockaddr*)(&myaddr), sizeof(myaddr)) < 0)
{
perror(“fail to bind”); exit(-1);
}
地址转换函数
unsigned long inet_addr(char *address);
- address是以’\0’结尾的点分IPv4字符串。该函数返回32位的地址。如果字符串包含的不是合法的IP地址,则函数返回-1。例如:
struct in_addr addr;
addr.s_addr = inet_addr(" 192.168.1.100 ");
char* inet_ntoa(struct in_addr address);
- address是IPv4地址结构,函数返回一指向包含点分IP地址的静态存储区字符指针。如果错误则函数返回NULL
listen (监听模式)
int listen (int sockfd, int backlog);
- sockfd:监听连接的套接字
- backlog
指定了正在等待连接的最大队列长度,它的作用在于处理可能同时出现的几个连接请求。
DoS(拒绝服务)攻击即利用了这个原理,非法的连接占用了全部的连接数,造成正常的连接请求被拒绝。 一般填5, 测试得知,ARM最大为8 - 返回值: 0 或 -1
- 内核中服务器的套接字fd会维护2个链表:
- 正在三次握手的的客户端链表(数量=2*backlog+1)
- 已经建立好连接的客户端链表(已经完成3次握手分配好了newfd)
- 比如:listen(fd, 5); //表示系统允许11(=2*5+1)个客户端同时进行三次握手
完成listen()调用后,socket变成了监听socket(listening socket).
accept()- 阻塞等待客户端连接请求
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
- 返回值:已建立好连接的套接字或-1
- 头文件
#include <sys/types.h>
#include <sys/socket.h> - sockfd : 监听套接字 ,经过前面socket()创建并通过bind(),listen()设置过的fd
- addr : 对方地址(客户端)
- addrlen:地址长度
listen()和accept()是TCP服务器端使用的函数
connect()-客户端的连接函数
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
- 返回值:0 或 -1
- 头文件:
#include <sys/types.h>
#include <sys/socket.h> - sockfd : socket返回的文件描述符
- serv_addr : 服务器端的地址信息
- addrlen : serv_addr的长度
connect()是客户端使用的系统调用。
send()-发送数据
ssize_t send(int socket, const void *buffer, size_t length, int flags);
- 返回值:
成功:实际发送的字节数
失败:-1, 并设置errno - 头文件:
#include <sys/socket.h> - buffer : 发送缓冲区首地址
- length : 发送的字节数
- flags : 发送方式(通常为0)
recv()-接受数据
ssize_t recv(int socket, const void *buffer, size_t length, int flags);
返回值:
成功:实际接收的字节数
失败:-1, 并设置errno
头文件:
#include <sys/socket.h>
buffer : 发送缓冲区首地址
length : 发送的字节数
flags : 接收方式(通常为0)
read()/write()
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
- read()和write()经常会代替recv()和send(),通常情况下,看程序员的偏好
- 使用read()/write()和recv()/send()时最好统一
sendto(),recvfrom()
ssize_t sendto(int socket, void *message, size_t length, int flags, struct sockaddr *dest_addr, socklen_t dest_len);
ssize_t recvfrom(int socket, void *buffer, size_t length, int flags, struct sockaddr *address, socklen_t *address_len);
- 这两个函数一般在使用UDP协议时使用
close-套接字的关闭
int close(int sockfd);
关闭双向通讯
int shutdown(int sockfd, int howto);
- TCP连接是双向的(是可读写的),当我们使用close时,会把读写通道都关闭,有时侯我们希望只关闭一个方向,这个时候我们可以使用shutdown。
- 针对不同的howto,系统回采取不同的关闭方式。
- shutdown()的howto参数
howto = 0
关闭读通道,但是可以继续往套接字写数据。
howto = 1
和上面相反,关闭写通道。只能从套接字读取数据。
howto = 2
关闭读写通道,和close()一样
TCP编程API
头文件 net.h
#ifndef __NET_H__
#define __NET_H__
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#define SERV_PORT 5001
#define SERV_IP_ADDR "192.168.2.231"
#define BACKLOG 5
#define QUIT_STR "quit"
#endif
服务端 Server.c
#include "net.h"
int main(void)
{
int fd = -1;
struct sockaddr_in sin;
/* 1. 创建socket fd*/
if ( (fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
exit(1);
}
/*2. 绑定 */
/*2.1 填充struct sockaddr_in结构体变量 */
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT); //网络字节序的端口号
#if 1
sin.sin_addr.s_addr = inet_addr(SERV_IP_ADDR);
#else
if(inet_pton(AF_INET,SERV_IP_ADDR,(void *)sin.sin_addr.s_addr) < 0)
{
//AF_INET IPV4编程
perror("inet_pton");
exit(1);
}
#endif
/*2.2 绑定 */
if ( bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0 )
{
perror("bind");
exit(1);
}
/*3. 调用listen()把主动套接字变成被动套接字 */
if (listen(fd, BACKLOG) < 0)
{
perror("listen");
exit(1);
}
printf("Server starting....OK!\n");
int newfd = -1;
/*4. 阻塞等待客户端连接请求 */
newfd = accept(fd, NULL, NULL);
if (newfd < 0) {
perror("accept");
exit(1);
}
/*5. 读写 */
//..和newfd进行数据读写
int ret = -1;
char buf[BUFSIZ];
while (1)
{
bzero(buf, BUFSIZ);
do {
ret = read(newfd, buf, BUFSIZ - 1);
} while (ret < 0 && EINTR == errno);
if (ret < 0) {
perror("read");
exit(1);
}
if (!ret) { //对方已经关闭
break;
}
printf("Receive data: %s\n", buf);
if ( !strncasecmp(buf, QUIT_STR, strlen(QUIT_STR))) { //用户输入了quit字符
printf("Client is exiting!\n");
break;
}
}
close(newfd);
close(fd);
return 0;
}
客户端 Client.c
#include "net.h"
int main(void)
{
int fd = -1;
struct sockaddr_in sin;
/* 1. 创建socket fd*/
if ( (fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
exit(1);
}
/*2.连接服务器 */
/*2.1 填充struct sockaddr_in结构体变量 */
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT); //网络字节序的端口号
#if 0
sin.sin_addr.s_addr = inet_addr(SERV_IP_ADDR);
#else
if ( inet_pton(AF_INET, SERV_IP_ADDR, (void *)&sin.sin_addr) != 1)
{
perror("inet_pton");
exit(1);
}
#endif
if ( connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
perror("connect");
exit(1);
}
printf("Client staring...OK!\n");
/*3. 读写数据 */
char buf[BUFSIZ];
int ret = -1;
while (1)
{
bzero(buf, BUFSIZ);
if ( fgets(buf, BUFSIZ - 1, stdin) == NULL)
{
continue;
}
do {
ret = write(fd, buf, strlen(buf));
} while (ret < 0 && EINTR == errno);
if ( !strncasecmp(buf, QUIT_STR, strlen(QUIT_STR))) { //用户输入了quit字符
printf("Client is exiting!\n");
break;
}
}
/*4.关闭套接字 */
close(fd);
}