目录
sockaddr地址结构
IP+port:在网络环境中唯一标识一个进程。
man 7 ip
struct sockaddr {
sa_family_t sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};
struct sockaddr_in {
sa_family_t sin_family; /* Address family */ //地址结构类型
in_port_t sin_port; /* Port number */ //端口号
struct in_addr sin_addr; /* Internet address */ //IP地址
};
struct in_addr { /* Internet address. */
uint32_t s_addr;
};
用法:
struct sockaddr_in addr;
addr.sin_family = IP地址的类型; //IPv4:AF_INET IPv6:AF_INET6
addr.sin_port = htons(端口号);
//s_addr的获取方式1
int dst;
inet_pton(IP地址的类型, "IP地址", (void *)&dst);
addr.sin_addr.s_addr = dst;
//s_addr的获取方式2
addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY:取出系统中有效的任意IP地址,二进制类型。
bind(fd, (struct sockaddr *)&addr, size);
网络套接字函数
socket模型创建流程图
server(服务器端)
socket():创建socket。
bind():绑定服务器地址结构。
listen():设置监听上限。
accept():阻塞监听客户端连接。
read(fd):读socket获取客户端数据。
write(fd)
close()
client(客户端)
socket():创建socket。
connect():与服务器建立连接。
write():写数据到socket。
read():读转换后的数据。
显示读取结果。
close()。
socket
创建一个套接字。
man socket
参数domain
协议类型。
AF_INET:IPv4
AF_INET6:IPv6
AF_UNIX:本地套接字
参数type
数据传输协议。
SOCK_STREAM:流式协议,TCP传输
SOCK_DGRAM:UDP传输
参数protocol
0:默认值
返回值
成功:新套接字所对应的文件描述符
失败:-1
bind
给socket绑定一个地址结构(IP+port)。如果不使用bind绑定客户端地址结构, 采用“隐式绑定”。
man bind
参数sockfd
新套接字所对应的文件描述符,socket函数返回值。
参数addr
构造出IP地址加端口号,地址结构。
struct sockaddr_in addr;
addr.sin_family = IP地址的类型; //IPv4:AF_INET IPv6:AF_INET6
addr.sin_port = htons(端口号);
//s_addr的获取方式1
int dst;
inet_pton(IP地址的类型, "IP地址", (void *)&dst);
addr.sin_addr.s_addr = dst;
//s_addr的获取方式2
addr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY:取出系统中有效的任意IP地址,二进制类型。
bind(fd, (struct sockaddr *)&addr, size);
参数addrlen
地址结构的大小。
sizeof(addr)
返回值
成功:0
失败:-1
listen
设置同时与服务器建立连接的上限数。同时进行3次握手的客户端数量。
man listen
参数sockfd
新套接字所对应的文件描述符,socket函数返回值。
参数backlog
上限数值。最大值128。数值越大,服务器丢客户端的概率就越小,连接速率也越快。上千万级数量的客户端同时请求连接单进程的服务器时,效果最明显。
返回值
成功:0
失败:-1
accept
阻塞等待客户端建立连接。
man 2 accept
参数sockfd
新套接字所对应的文件描述符,socket函数返回值。
参数addr
传出参数。成功与服务器建立连接的那个客户端的地址结构(IP+port)。
参数addrlen
传入传出参数,地址长度。
socklen_t clit_addr_len = sizeof(addr);
&clit_addr_len
传入:addr的大小。
传出:客户端addr的实际大小。
返回值
成功:能与客户端进行数据通信的socket对应的文件描述。
失败:-1。
connect
使用现有的socket与服务器建立连接。
man 2 connect
参数sockfd
新套接字所对应的文件描述符,socket函数返回值。
参数addr
传入参数。服务器的地址结构。
参数addrlen
服务器的地址结构的大小。
返回值
成功:0
失败:-1
测试代码1
服务器从客户端读取数据,然后将数据回送给客户端。
/*测试1服务器端代码,网络调试助手作客户端*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
int flag;
int fd_FWQ; //服务器文件描述符
int fd_KFD; //客户端文件描述符
char data[1024]; //读取的数据
int ZiJieShu; //字节数
struct sockaddr_in DiZhi_JieGou_FWQ; //服务器地址结构
struct sockaddr_in DiZhi_JieGou_KHD; //客户端地址结构
socklen_t KeHuDuan_DaXiao; //客户端大小
char KHD_IP[1024]; //客户端IP
char FWQ_IP[1024]; //服务器IP
fd_FWQ = socket(AF_INET, SOCK_STREAM, 0); //创建服务器套接字
if (fd_FWQ == -1)
{
perror("创建服务器套接字错误");
exit(1);
}
DiZhi_JieGou_FWQ.sin_family = AF_INET; //IPv4
DiZhi_JieGou_FWQ.sin_port = htons(8080); //端口号8080
DiZhi_JieGou_FWQ.sin_addr.s_addr = htonl(INADDR_ANY); //获取系统中任意有效的IP地址
flag = bind(fd_FWQ, (struct sockaddr *)&DiZhi_JieGou_FWQ, sizeof(DiZhi_JieGou_FWQ)); //绑定服务器的地址结构
if (flag == -1)
{
perror("绑定服务器地址结构错误");
exit(1);
}
printf("服务器IP:%s,端口号:%d\n",
inet_ntop(AF_INET, &DiZhi_JieGou_FWQ.sin_addr.s_addr, FWQ_IP, sizeof(FWQ_IP)),
ntohs(DiZhi_JieGou_FWQ.sin_port));
flag = listen(fd_FWQ, 128); //设置连接服务器上限数
if (flag == -1)
{
perror("设置连接上限数错误");
exit(1);
}
KeHuDuan_DaXiao = sizeof(DiZhi_JieGou_KHD);
fd_KFD = accept(fd_FWQ, (struct sockaddr *)&DiZhi_JieGou_KHD, &KeHuDuan_DaXiao); //阻塞监听客户端连接
if (fd_KFD == -1)
{
perror("阻塞监听客户端连接错误");
exit(1);
}
printf("客户端IP:%s,端口号:%d\n",
inet_ntop(AF_INET, &DiZhi_JieGou_KHD.sin_addr.s_addr, KHD_IP, sizeof(KHD_IP)), //网络转换成十进制本地IP
ntohs(DiZhi_JieGou_KHD.sin_port)); //网络转换成本地端口
while (1)
{
ZiJieShu = read(fd_KFD, data, sizeof(data));
write(STDOUT_FILENO, data, ZiJieShu); //终端显示
write(fd_KFD, data, ZiJieShu);
}
close(fd_KFD);
close(fd_FWQ);
return 0;
}
测试结果
服务器IP为0.0.0.0,表示所有地址、不确定地址、任意地址。
虚拟机端:
本机端:
手机端:
测试代码2
客户端与服务器的通讯。
/*测试2客户端代码,用网络调试助手作服务器*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#define FWQ_IP "10.3.22.215" //服务器IP
int main(int argc, char *argv[])
{
int fd_FWQ; //服务器
int flag;
struct sockaddr_in FWQ_DiZhi; //服务器地址
int ZiFuShu; //字符数
char data[1024]; //数据
fd_FWQ = socket(AF_INET, SOCK_STREAM, 0); //创建服务器套接字
if (fd_FWQ == -1)
{
perror("创建服务器套接字错误");
exit(1);
}
FWQ_DiZhi.sin_family = AF_INET; //IPv4
FWQ_DiZhi.sin_port = htons(10500); //端口号8080
flag = inet_pton(AF_INET, FWQ_IP, &FWQ_DiZhi.sin_addr); //十进制IP转换网络IP
if (flag == -1)
{
perror("十进制IP转换网络IP错误");
exit(1);
}
flag = connect(fd_FWQ, (struct sockaddr *)&FWQ_DiZhi, sizeof(FWQ_DiZhi));
if (flag == -1)
{
perror("连接服务器错误");
exit(1);
}
printf("连接服务器完成。\n");
while (1)
{
ZiFuShu = read(fd_FWQ, &data, sizeof(data));
write(fd_FWQ, "客户端接收到的数据是:", sizeof("客户端接收到的数据是:")); //将数据发回给服务器
write(fd_FWQ, data, ZiFuShu);
write(STDOUT_FILENO, "服务器发送的数据是:", sizeof("服务器发送的数据是:")); //显示接收到服务器的数据
write(STDOUT_FILENO, data, ZiFuShu);
sleep(1);
}
close(fd_FWQ);
return 0;
}
测试结果
测试代码3
客户端与服务器实现数据传输。
/*
测试3服务器端代码
CeShi3_FWQ.c
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
int flag;
int fd_FWQ; //服务器文件描述符
int fd_KFD; //客户端文件描述符
char data[1024]; //读取的数据
int ZiJieShu; //字节数
struct sockaddr_in DiZhi_JieGou_FWQ; //服务器地址结构
struct sockaddr_in DiZhi_JieGou_KHD; //客户端地址结构
socklen_t KeHuDuan_DaXiao; //客户端大小
char KHD_IP[1024]; //客户端IP
char FWQ_IP[1024]; //服务器IP
fd_FWQ = socket(AF_INET, SOCK_STREAM, 0); //创建服务器套接字
if (fd_FWQ == -1)
{
perror("创建服务器套接字错误");
exit(1);
}
DiZhi_JieGou_FWQ.sin_family = AF_INET; //IPv4
DiZhi_JieGou_FWQ.sin_port = htons(8080); //端口号8080
DiZhi_JieGou_FWQ.sin_addr.s_addr = htonl(INADDR_ANY); //获取系统中任意有效的IP地址
flag = bind(fd_FWQ, (struct sockaddr *)&DiZhi_JieGou_FWQ, sizeof(DiZhi_JieGou_FWQ)); //绑定服务器的地址结构
if (flag == -1)
{
perror("绑定服务器地址结构错误");
exit(1);
}
printf("服务器IP:%s,端口号:%d\n",
inet_ntop(AF_INET, &DiZhi_JieGou_FWQ.sin_addr.s_addr, FWQ_IP, sizeof(FWQ_IP)),
ntohs(DiZhi_JieGou_FWQ.sin_port));
flag = listen(fd_FWQ, 128); //设置连接服务器上限数
if (flag == -1)
{
perror("设置连接上限数错误");
exit(1);
}
KeHuDuan_DaXiao = sizeof(DiZhi_JieGou_KHD);
fd_KFD = accept(fd_FWQ, (struct sockaddr *)&DiZhi_JieGou_KHD, &KeHuDuan_DaXiao); //阻塞监听客户端连接
if (fd_KFD == -1)
{
perror("阻塞监听客户端连接错误");
exit(1);
}
printf("客户端IP:%s,端口号:%d\n",
inet_ntop(AF_INET, &DiZhi_JieGou_KHD.sin_addr.s_addr, KHD_IP, sizeof(KHD_IP)), //网络转换成十进制本地IP
ntohs(DiZhi_JieGou_KHD.sin_port)); //网络转换成本地端口
while (1)
{
ZiJieShu = read(fd_KFD, data, sizeof(data));
if (ZiJieShu > 0)
{
write(STDOUT_FILENO, "服务器接收到的数据为:", sizeof("服务器接收到的数据为:"));
write(STDOUT_FILENO, data, ZiJieShu); //终端显示
write(fd_KFD, "你好客户端,我是服务器,接收到的数据为:", sizeof("你好客户端,我是服务器,接收到的数据为:"));
write(fd_KFD, data, ZiJieShu);
}
}
close(fd_KFD);
close(fd_FWQ);
return 0;
}
/*
测试3客户端代码
CeShi3_KHD.c
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <semaphore.h>
#include <pthread.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#define FWQ_IP "127.0.0.1" //服务器IP
int main(int argc, char *argv[])
{
int fd_FWQ; //服务器
int flag;
struct sockaddr_in FWQ_DiZhi; //服务器地址
int ZiFuShu; //字符数
char data[1024]; //数据
fd_FWQ = socket(AF_INET, SOCK_STREAM, 0); //创建服务器套接字
if (fd_FWQ == -1)
{
perror("创建服务器套接字错误");
exit(1);
}
FWQ_DiZhi.sin_family = AF_INET; //IPv4
FWQ_DiZhi.sin_port = htons(8080); //端口号8080
flag = inet_pton(AF_INET, FWQ_IP, &FWQ_DiZhi.sin_addr); //十进制IP转换网络IP
if (flag == -1)
{
perror("十进制IP转换网络IP错误");
exit(1);
}
flag = connect(fd_FWQ, (struct sockaddr *)&FWQ_DiZhi, sizeof(FWQ_DiZhi));
if (flag == -1)
{
perror("连接服务器错误");
exit(1);
}
printf("连接服务器完成。\n");
while (1)
{
write(fd_FWQ, "你好服务器,我是客户端,你好,世界!\n", sizeof("你好服务器,我是客户端,你好,世界!\n")); //将数据发给服务器
printf("向服务器发送的数据是:你好服务器,我是客户端,你好,世界!\n");
ZiFuShu = read(fd_FWQ, &data, sizeof(data));
if (ZiFuShu > 0)
{
write(STDOUT_FILENO, "服务器发送的数据是:", sizeof("服务器发送的数据是:")); //显示接收到服务器的数据
write(STDOUT_FILENO, data, ZiFuShu);
}
sleep(1);
}
close(fd_FWQ);
return 0;
}