套接字通信原理
进程创建套接字开辟一片空间,返回套接字的文件描述符给进程,两主机的进程操控套接字文件描述符来进行发送和接收 两个套接字之间网络传输
通信原理图

相关函数
socket
int socket(int domain, int type, int protocol)
功能:创建通信的端点
参数:domain通信域
|-------------------|------|---------|
| AF_UNIX, AF_LOCAL | 本地通信 | unix(7) |
| AF_INET | IPv4 | ip(7) |
| AF_INET6 | IPv6 | ipv6(7) |type传输层协议类型
SOCK_STREAM :使用的是TCP通信协议
SOCK_DGRAM :使用UDP通信协议
protocol指定协议:参数2有多个协议时 指定传输协议,只有一种通信方式,参数3写0即可
返回值:成功返回创建出的套接字文件描述符,最小未分配原则,失败返回-1并置位errno
bind
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
功能:设定地址信息
sockfd要被绑定的套接字文件描述符
struct sockaddr *addr:通用地址信息结构体,主要用于强制转换,防止警告,原因是每个通信域的地址信息结构体不一定相同
用法:(struct sockaddr *)&服务端地址信息
AF_INET(ipV4): struct sockaddr_in { sa_family_t sin_family; /* 协议族: AF_INET */ in_port_t sin_port; /* port端口字节序 */ struct in_addr sin_addr; /* 网络地址(结构体) */ }; /* Internet address. */ struct in_addr { uint32_t s_addr; /* ip字节序 */ }; AF_UNIX(域套接字): struct sockaddr_un { sa_family_t sun_family; /* AF_UNIX */ char sun_path[108]; /* pathname */ };
socklen_t addrlen:就是参数2的大小
返回值:成功返回0,失败返回-1并置位错误码
listen
int listen(int sockfd, int backlog)
(sfd,超时缓冲区)
功能:将给定的套接字设置成被动监听状态,主要负责接收客户端的连接请求使用
sockfd要被设置的套接字文件描述符
backlog一次性能接收的最大客户端数量,等待队列的最大长度,一般为128
返回值:成功返回0,失败返回-1并置位错误码
accept
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
功能:阻塞等待接收客户端连接请求后,创建新的用于通信的套接字文件描述符
sockfd用于监听的套接字文件描述符
struct sockaddr *addr用于接收客户端地址信息结构体的指针
socklen_t *addrlen用于接收客户端地址信息的长度
注意:如果不需要客户端的信息,参数2和参数3都可以填NULL
返回值:成功返回新创建的用于通信的套接字文件描述符,失败返回-1并置位错误码
connect
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
功能:将套接字文件描述符连接到addr指向的地址空间中
sockfd客户端套接字文件描述符
const struct sockaddr *addr对端(服务端)地址信息结构体
socklen_t addrlen参数2的大小
返回值:成功返回0,失败返回-1并置位错误码
recv &send
ssize_t recv(int sockfd, void *buf, size_t len, int flags)
ssize_t send(int sockfd, const void *buf, size_t len, int flags)
功能:从套接字中接收(发送)数据到buf中
参数1:用于通信的套接字文件描述符
参数2:接收(发送)数据的容器地址
参数3:接收(发送)的数据的大小
参数4:是否阻塞接收
0:表示阻塞接收消息
MSG_DONTWAIT:表示非阻塞接收数据
recv返回值:
>0:表示成功读取的字符个数
=0:表示通信对端已经下线
=-1:表示出错,置位错误码
send返回值:成功返回发送字符的个数,失败返回-1并置位错误码
inet_addr & inet_ntoa
in_addr_t inet_addr(const char *cp)
功能:将点分十进制字符串IP地址转成32位字节序
const char *cp点分十进制字符串IP
返回值:成功返回字节序,失败返回-1
char *inet_ntoa(struct in_addr in)
功能:将32位字节序点转成点分十进制字符串IP地址
struct in_addr in点分十进制字符串IP
返回值:成功返回字节序,失败返回-1
完整代码
服务端
#include <stdio.h>
#include <25061head.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */
#define SER_PORT 8888
#define SER_IP "192.168.109.12"
int main(int argc, const char *argv[])
{
//0.创建服务器套接字文件描述符
int sfd=socket(AF_INET,SOCK_STREAM,0); //ipV4网络 TCP通信方式
if(sfd==-1) ERR_MSG("socket error");
printf("创建成功%d\n",sfd);
//端口号快速重用
int reuse=-1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)
ERR_MSG("setsockopt error");
printf("设置快速重用成功\n");
//1.绑定
//1.1封装地址信息
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(SER_PORT); //端口号转字节序
sin.sin_addr.s_addr=inet_addr(SER_IP);//ip地址转字节序
//1.2绑定信息
if(bind(sfd,(struct sockaddr *)&sin,sizeof(sin))==-1)
//参数2:需要强转成通用信息结构体
ERR_MSG("bind error");
printf("绑定成功%d\n",sfd);
//2.监听
if(listen(sfd,128)==-1)
ERR_MSG("listen error");
printf("监听成功%d\n",sfd);
//3.接收链接请求
struct sockaddr_in cin; //接收客户端的地址信息结构体
socklen_t socklen=sizeof(cin); //接收对方地址信息的长度 参数2写参数3必须写
int new_fd=accept(sfd,(struct sockaddr*)&cin,&socklen);
if(new_fd==-1)
ERR_MSG("accept error");
//将ip和端口分别转成点分十进制和无符号整型
printf("%s:%d\n",inet_ntoa(cin.sin_addr)\
,ntohs(cin.sin_port));
while(1)
{
//4.接收数据
char buf[128]="";
ssize_t recv_num=recv(new_fd,buf,sizeof(buf)-1,0);
//发生错误
if(recv_num==-1)
{
perror("recv error");
close(sfd);
close(new_fd);
return -1;
}
//对方下线
else if(recv_num==0)
{
printf("对方下线\n");
close(new_fd);
break;
}
//接收到数据
printf("num=%ld recv_text=%s\n",recv_num,buf);
//5.发送数据
char send_buf[128]="hello hello";
ssize_t send_num=send(new_fd,send_buf,strlen(send_buf),0);
if(send_num==-1) ERR_MSG("send error");
printf("num=%ld send_text=%s\n",send_num,send_buf);
}
close(sfd);
return 0;
}
客户端
#include <stdio.h>
#include <25061head.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#define SER_PORT 8888
#define SER_IP "192.168.109.12"
#define CER_PORT 9999
#define CER_IP "192.168.109.12"
int main(int argc, const char *argv[])
{
//创建
int cfd=socket(AF_INET,SOCK_STREAM,0);
if(cfd==-1) ERR_MSG("scoket error");
//绑定(可选)
//服务器地址信息
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin.sin_port=htons(SER_PORT);
sin.sin_addr.s_addr=inet_addr(SER_IP);
//连接
if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
ERR_MSG("connect error");
while(1)
{
//发送
char buf[128]="";
fgets(buf,sizeof(buf),stdin); //终端获取
buf[strlen(buf)-1]=0;
if(send(cfd,buf,strlen(buf),0)==-1) //发送
ERR_MSG("send error");
//接收
recv(cfd,buf,sizeof(buf),0);
printf("%s\n",buf);
}
close(cfd);
return 0;
}
思维导图

今日刷题
