目录
1、TCP
1.1 TCP建立连接的流程图
1.2 TCP函数
1.2.1 socket
1.2.2 bind
1.2.3 listen
1.2.4 accept
1.2.5 recv
1.2.6 send
1.2.7 connnect
1.2.8 setsockopt、getsockopt
1.3 应用程序:服务器
1.4 应用程序:客户端
2、UDP
2.1 UDP建立连接的流程图
2.2 UDP函数
2.2.1 socket
2.2.2 bind
2.2.3 recvfrom
2.2.4 sendto
2.3 应用程序:服务器
2.4 应用程序:客户端
1、TCP
1.1 TCP建立连接的流程图
1.2 TCP函数
1.2.1 socket
int socket(int domain, int type, int protocol);
/*
功能:创建套接字
头文件:
#include <sys/types.h>
#include <sys/socket.h>
参数:
@domain:指定传输时候的地址族(协议族)
Name Purpose Man page
AF_UNIX, AF_LOCAL Local communication unix(7)
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7);
@type:套接字类型
SOCK_STREAM:字节流式套接字,流式套接字。--->TCP
SOCK_DGRAM:数据报式套接字,报式套接字。---->UDP
SOCK_RAW:原始套接字,传输协议需要自定义,在第三个参数中定义
@protocol:传输协议,默认协议填0;
IPPROTO_TCP IPPROTO_UDP
返回值:
成功,返回套接字的文件描述符
失败,返回-1,更新errno
*/
1.2.2 bind
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
功能:将IP地址和端口与套接字绑定;
头文件:
#include <sys/types.h>
#include <sys/socket.h>
参数:
@sockfd:socket的返回值;
@addr:需要绑定到套接字上的地址信息;
通用结构体,地址信息结构体。实际的结构体根据地址族的不同,指定不同类型;
AF_INET: man 7 ip 查找;
struct sockaddr_in {
sa_family_t sin_family; // address family: AF_INET
in_port_t sin_port; // 端口的网络字节序
struct in_addr sin_addr; // internet address
};
struct in_addr {
uint32_t s_addr; // IP的网络字节序
};
AF_INET6:man 7 ipv6;
@addrlen:实际结构体的大小;
sizeof(struct sockaddr_in);
返回值:
成功,返回0;
失败,返回-1,更新errno;
*/
1.2.3 listen
int listen(int sockfd, int backlog);
/*
功能:将套接字设置为被动监听状态;
头文件:
#include <sys/types.h>
#include <sys/socket.h>
参数:
@sockfd:socket函数返回的流式套接字;
@backlog:允许同一时间连接的客户端个数;
调用listen函数后,内核会维护两个队列:已完成连接队列,未完成连接队列;
backlog:未完成连接队列的容量;
返回值:
成功,返回0;
失败,返回-1,更新errno;
*/
1.2.4 accept
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
/*
功能:阻塞函数,会从已完成连接的队列头中获取一个客户端信息,并生成一个新的文件描述符,用于通信;
头文件:
#include <sys/types.h>
#include <sys/socket.h>
参数:
@sockfd:被listen设置成监听状态的文件描述符;
@addr:存储连接成功的客户端的地址信息;当不想获取的时候,填NULL;
通用结构体,地址信息结构体。实际的结构体根据地址族的不同,指定不同类型;
@addrlen:地址信息结构体的大小,注意是指针类型; 第二个参数填NULL,则当前参数填NULL;
返回值:
成功,返回新的文件描述符,该文件描述符用于与客户端交互;
失败,返回-1,更新errno;
*/
1.2.5 recv
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
/*
功能:从套接字中读取数据;
头文件:
#include <sys/types.h>
#include <sys/socket.h>
参数:
@sockfd:accept函数的返回值;
@buf:存储接收到的数据,可以接收任意类型,但是需要注意,收发格式一致;
@len:指定要读取多少个字节;
@flags:0,阻塞方式接收;
返回值:
>0, 成功,读取到的字节数;
=-1, 函数运行失败;更新errno;
=0; 对方关闭;
*/
1.2.6 send
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
/*
功能:发送数据;
头文件:
#include <sys/types.h>
#include <sys/socket.h>
参数:
int sockfd:accept返回的新的文件描述符;
void *buf:存储要发送的数据;可以是任意类型数据;
size_t len:指定要发送多少个字节;
int flags:发送标识
0:阻塞方式发送,当接收缓冲区满的时候,该函数阻塞;
返回值:
>0, 成功发送的字节数;
=-1, 函数调用出错;
*/
1.2.7 connnect
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
功能:连接服务器
头文件:
#include <sys/types.h>
#include <sys/socket.h>
参数:
@sockfd:socket函数创建的文件描述符;
@addr:指定要连接的服务器,填服务器的地址信息;
AF_INET: man 7 ip
struct sockaddr_in {
sa_family_t sin_family; // address family: AF_INET
in_port_t sin_port; //端口号的网络字节序
struct in_addr sin_addr; //ip地址的网络字节序
};
struct in_addr {
uint32_t s_addr; //网络字节序
};
@addrlen:第二个地址信息结构体的大小;
返回值:
成功,返回0;
失败,返回-1,更新errno;
*/
1.2.8 setsockopt、getsockopt
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
int getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen);
/*
功能:
头文件:
#include <sys/types.h>
#include <sys/socket.h>
参数:
@sockfd:套接字的文件描述符
@level: 选项定义的层次,通常是SOL_SOCKET
@optname:需设置的选项
@optval:指向在其中指定所请求选项值的缓冲区的指针
@optlen:指向的缓冲区的大小(以字节为单位)
返回值:成功返回0。
失败返回SOCKET_ERROR值,并且可以通过调用 WSAGetLastError 来检索特定的错误代码。
此处放上一些常用的:
level:
SOL_SOCKET: 通用套接字选项;
IPPROTO_TCP TCP选项
IPPROTO_UDP UDP选项
IPPROTO_IP IP选项;
optname:
SOL_SOCKET
SO_REUSEADDR 允许端口快速重用;
SO_BROADCAST 广播;
SO_RCVTIMEO 接收超时时间
SO_SNDTIMEO 发送超时时间
SO_SNDBUF 发送缓冲区大小
SO_RCVBUF 接收缓冲区大小;
*/
选项太多,具体的参考文档:
使用此函数的文章:
linux网络编程系列(五)--setsockopt的常用选项 - 知乎
为了防为此文章挂掉,因为我觉得写的很好,所以我抄了一份
setsockopt的常用选项_凛冬将至__的博客-CSDN博客
setsockopt 的参考文档:
setsockopt 函数 (winsock.h) - Win32 apps | Microsoft Learn
level 的参考文档:
套接字选项 - Win32 apps | Microsoft Learn
optname 的参考文档:
1.3 应用程序:服务器
#define PORT 6666
#define IP "192.168.1.12"
int main(int argc, const char *argv[])
{
//创建流式套接字 socket
int sfd = socket(AF_INET, SOCK_STREAM, 0);
//允许端口快速重用
int reuse = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))
//填充地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET; //IPV4地址族
sin.sin_port = htons(PORT); //端口号的网络字节序,1024~49151
sin.sin_addr.s_addr = inet_addr(IP); //IP地址的网络字节序,终端输入ifconfig
//绑定服务器的地址信息结构体
bind(sfd, (struct sockaddr*)&sin, sizeof(sin))
//将套接字设置为被动监听状态
listen(sfd, 10);
//获取新的文件描述符,当有客户端连接成功,解除阻塞;
int newfd = accept(sfd, NULL, NULL);
char buf[128] = "";
ssize_t res = -1;
while(1)
{
bzero(buf, sizeof(buf));
//读取数据
res = recv(newfd, buf, sizeof(buf), 0);
if(res < 0) {
return -1;
} else if(0 == res) {
break;
}
send(newfd, buf, sizeof(buf), 0);
}
//关闭文件描述符
close(newfd);
close(sfd);
return 0;
}
1.4 应用程序:客户端
int main(int argc, const char *argv[])
{
if(argc < 3) {
fprintf(stderr, "请输入IP 及 端口\n");
return -1;
}
//创建流式套接字
int sfd = socket(AF_INET, SOCK_STREAM, 0);
//绑定客户端的IP和端口---->非必须
//填充服务器的地址信息结构体
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(atoi(argv[2])); //外部传参输入端口号,需要转换成整型
sin.sin_addr.s_addr = inet_addr(argv[1]); //外部传参输入IP
//连接服务器
connect(sfd, (struct sockaddr*)&sin, sizeof(sin));
char buf[128] = "";
ssize_t res = -1;
//循环接收发送
while(1)
{
bzero(buf, sizeof(buf));
printf("请输入>>>");
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = 0; //将结尾的'\n'修改成'\0'
send(sfd, buf, sizeof(buf), 0);
bzero(buf, sizeof(buf));
res = recv(sfd, buf, sizeof(buf), 0);
if(res < 0) {
return -1;
} else if(0 == res) {
break;
}
printf(":%s\n", buf);
}
//关闭文件描述符
close(sfd);
return 0;
}
2、UDP
2.1 UDP建立连接的流程图
2.2 UDP函数
2.2.1 socket
int socket(int domain, int type, int protocol);
/*
功能:创建套接字
头文件:
#include <sys/types.h>
#include <sys/socket.h>
参数:
@domain:指定传输时候的地址族(协议族)
Name Purpose Man page
AF_UNIX, AF_LOCAL Local communication unix(7)
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7);
@type:套接字类型
SOCK_STREAM:字节流式套接字,流式套接字。--->TCP
SOCK_DGRAM:数据报式套接字,报式套接字。---->UDP
SOCK_RAW:原始套接字,传输协议需要自定义,在第三个参数中定义
@protocol:传输协议,默认协议填0;
IPPROTO_TCP IPPROTO_UDP
返回值:
成功,返回套接字的文件描述符
失败,返回-1,更新errno
*/
2.2.2 bind
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
/*
功能:将IP地址和端口与套接字绑定;
头文件:
#include <sys/types.h>
#include <sys/socket.h>
参数:
@sockfd:socket的返回值;
@addr:需要绑定到套接字上的地址信息;
通用结构体,地址信息结构体。实际的结构体根据地址族的不同,指定不同类型;
AF_INET: man 7 ip 查找;
struct sockaddr_in {
sa_family_t sin_family; // address family: AF_INET
in_port_t sin_port; // 端口的网络字节序
struct in_addr sin_addr; // internet address
};
struct in_addr {
uint32_t s_addr; // IP的网络字节序
};
AF_INET6:man 7 ipv6;
@addrlen:实际结构体的大小;
sizeof(struct sockaddr_in);
返回值:
成功,返回0;
失败,返回-1,更新errno;
*/
2.2.3 recvfrom
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
/*
功能:接收数据包,并且可以获取到该数据包是谁发送的,存储在地址信息结构体中;
头文件:
#include <sys/types.h>
#include <sys/socket.h>
参数:
@sockfd:accept函数的返回值;
@buf:存储接收到的数据,可以接收任意类型,但是需要注意,收发格式一致;
@len:指定要读取多少个字节;
@flags:0,阻塞方式接收;
@src_addr:存储数据包发送端的IP和端口;
如果不想接收,填NULL,最后一个参数也填NULL
@addrlen:地址信息结构体的大小,注意是指针类型;
返回值:
>0, 成功,读取到的字节数;
=-1, 函数运行失败;更新errno;
=0; 对方关闭;(只适用于tcp)
*/
2.2.4 sendto
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
/*
功能:发送数据给指定的IP和端口;
头文件:
#include <sys/types.h>
#include <sys/socket.h>
参数:
@sockfd:accept返回的新的文件描述符;
@buf:存储要发送的数据;可以是任意类型数据;
@len:指定要发送多少个字节;
@flags:发送标识
0:阻塞方式发送,当接收缓冲区满的时候,该函数阻塞;
@dest_addr:指定该数据包该发送给谁;
@addrlen:地址信息结构体的大小;
返回值:
>0, 成功发送的字节数;
=-1, 函数调用出错;
*/
2.3 应用程序:服务器
#define PORT 8888
#define IP "192.168.1.12"
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
//填充服务器本身的地址信息
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
//绑定服务的IP和端口
bind(sfd, (struct sockaddr*)&sin, sizeof(sin));
char buf[128] = "";
ssize_t res = 0;
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
while(1)
{
bzero(buf, sizeof(buf));
//接收
res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, &addrlen);
printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);
//发送
strcat(buf, "*_*");
sendto(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, sizeof(cin));
}
//关闭
close(sfd);
return 0;
}
2.4 应用程序:客户端
#define PORT 8888
#define IP "192.168.1.12"
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
//绑定IP和端口---->非必须绑定
//填充服务器的地址信息
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
char buf[128] = "";
ssize_t res = 0;
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
while(1)
{
bzero(buf, sizeof(buf));
//发送
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = 0;
sendto(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, sizeof(sin));
bzero(buf, sizeof(buf));
//接收
res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, &addrlen);
printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);
}
//关闭
close(sfd);
return 0;
}