首先我们先来看一下套接字通讯的流程!
图片中的read()和write()等同于send()和recv()
socket通信流程
服务器端:
1、创建套接字socket
2、绑定套接字bind
3、监听listen
4、等待连接accept
5、接收发送recv、send
6、关闭套接字close
客户端:
1、创建套接字socket
2、连接服务器connect
3、接收发送recv、send
4、关闭套接字close
我们先看一下这样几个函数,稍后我们在继续
#include<arpa/inet.h>
unit32_t htonl(unit32_t hostlong);//32位的长整数从主机字节序转换为网络字节序
unit16_t htons(unit16_t hostshort);//16位的短整数从主机字节序转换为网络字节序
unit32_t ntohl(unit32_t netlong);//32位的长整数从网络字节序转换为主机字节序
unit16_t ntohs(unit16_t netshort);//16位的短整数从网络字节序转换为主机字节序
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char* cp, struct in_addr *inp);//将字符串cp的十进制转换为网络字节序的二进制形式后存储到inp中
char* inet_ntoa(struct in_addr *in);//将网络字节序的二进制形式转换为十进制的字符串形式,返回字符串的首地址
unsigned long inet_addr(const char* cp);//inet_addr()的功能是将一个点分十进制的IP转换成网络字节的32位二进制数值并返回(uint32_t)
好了这些函数,我们稍后再来讲解。
接下来我们来看一下套接字的地址结构体
//通用套接字地址结构体,不常用
struct sockaddr{
unit8_t sa_len;
sa_famliy sa_famlity;
char sa_data[14];
}
// IPV4套接字地址结构体:
struct sockaddr_in{
unit8_t sin_len;
sa_famliy_t sin_famliy;//协议家族
in_port_t sin_port;//端口号
struct in_addr sin_addr;//IP地址
char sin_zero[8];
}
struct in_addr
{
uint32_t s_addr;//IP
}
接下来我们根据套接字通信流程来看一下相应的一些函数
int socket(int domain,int type,int protocol);
头文件:
#include <sys/types.h>
#include <sys/socket.h>
第1个参数domain指明了协议族/域,通常AF_INET、AF_INET6、AF_LOCAL等;
第2个参数type是套接口类型,主要SOCK_STREAM、SOCK_DGRAM、SOCK_RAW;
第3个参数protocol一般取为0。成功时,返回一个小的非负整数值,与文件描述符类似
返回值:成功–非负描述符, 出错-1
#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
第1个参数sockfd是用socket()函数创建的文件描述符。
第2个参数my_addr是指向一个结构为sockaddr参数的指针,sockaddr中
包含了地址、端口和IP地址的信息。在进行地址绑定的时候,需要弦将
地址结构中的IP地址、端口、类型等结构struct sockaddr中的域进行设
置之后才能进行绑定,这样进行绑定后才能将套接字文件描述符与地址等接合在一起。
第3个参数addrlen是my_addr结构的长度,可以设置成sizeof(struct
sockaddr)。使用sizeof(struct sockaddr)来设置套接字的类型和其
对已ing的结构。
bind()函数的返回值为0时表示绑定成功,-1表示绑定失败,errno的错误值如表1所示。
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
第1个参数sockfd参数 是用socket()函数创建的文件描述符。
第2个参数backlog参数 此参数指定了sockfd句柄上pending的连接的队列可以达到的最大的长度。
返回值,成功返回0, 失败返回-1.
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr* addr, socklen_t* len)
第1个参数:sockfd 监听套接字,即服务器端创建的用于listen的socket描述符。
第2个参数:addr 这是一个结果参数,它用来接受一个返回值,这返回值指定客户端的地址
第3个参数:len 描述 addr 的长度
返回值,成功返回链接的描述符, 失败返回-1
#include <sys/types.h>
#include <sys/socket.h>
int send( SOCKET s, const char FAR *buf, int len, int flags );
第1个参数指定发送端套接字描述符;
第2个参数指明一个存放应用程序要发送数据的缓冲区;
第3个参数指明实际要发送的数据的字节数;
第4个参数一般置0。
返回值:成功返回发送的字符数,失败返回-1
int recv( SOCKET s, char FAR *buf, int len, int flags);
第1个参数指定接收端套接字描述符;
第2个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
第3个参数指明buf的长度;
第4个参数一般置0。
返回值:成功返回发送的字符数,失败返回-1
close函数就不说了。
说了这莫多开始上代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 6789//1024~49050
#define IP "192.168.43.130"
int main()
{
char r_buf[100]={0};
char w_buf[100]={0};
struct sockaddr_in addr,cli_addr;
int len=sizeof(addr);
int sockfd=socket(AF_INET,SOCK_STREAM,0);//IPV4,tcp
if(sockfd==-1)
{
perror("error socket");
return -1;
}
printf("连接套接字创建成功\n");
addr.sin_family=AF_INET;
addr.sin_port=htons(PORT);//转换
addr.sin_addr.s_addr=inet_addr(IP);//转换
int ret=bind(sockfd,(struct sockaddr *)&addr,sizeof(addr));
if(ret==-1)
{
perror("bind error");
return -1;
}
printf("绑定成功\n");
ret=listen(sockfd,3);
if(ret==0)
{
printf("监听成功\n");
}
else
{
return -1;
}
int comfd = accept(sockfd,(struct sockaddr *)&cli_addr,&len);
if(comfd==-1)
{
perror("accept error");
return -1;
}
printf("客户端%s已连接\n", inet_ntoa(cli_addr.sin_addr));
while(1)
{
memset(r_buf,0,sizeof(r_buf));
recv(comfd,r_buf,sizeof(r_buf),0);
printf("服务器接收%s\n", r_buf);
printf("服务器发送\n");
memset(w_buf,0,sizeof(w_buf));
scanf("%s", w_buf);
send(comfd,w_buf,sizeof(w_buf),0);
}
close(sockfd);
close(comfd);
return 0;
}
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 6789
#define IP "192.168.43.130"
int main()
{
char r_buf[100]={0};
char w_buf[100]={0};
struct sockaddr_in addr;
int len=sizeof(addr);
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1)
{
perror("error socket");
return -1;
}
printf("连接套接字创建成功\n");
addr.sin_family=AF_INET;
addr.sin_port=htons(PORT);
addr.sin_addr.s_addr=inet_addr(IP);
connect(sockfd,(struct sockaddr *)&addr,len);
while(1)
{
printf("客户端发送\n");
memset(w_buf,0,sizeof(w_buf));
scanf("%s", w_buf);
send(sockfd,w_buf,sizeof(w_buf),0);
memset(r_buf,0,sizeof(r_buf));
recv(sockfd,r_buf,sizeof(r_buf),0);
printf("客户端接收%s\n", r_buf);
}
close(sockfd);
return 0;
}
看一下测试运行结果:
这次实现的功能也比较简单,后续会继续跟一点socket编程部分的高阶!
感谢评阅,欢迎交流指正!
Q:918619587