4月「掘金·日新计划」第11天
一、网络编程
socket编程,相关函数套接字
二、实践使用
2.1、套接字api,socket,bind,listen,accept,connect
int socket(int domain,int type,int protocol)
功能:创建套接字
参数:
-
所使用的协议族,通常使用AF_TNET,表示IPv4因特网域
- AF_INET,IPv4因特网域
- AF_INET6,IPv6因特网域
- AF_UNIX,域
- AF_ROUTE,路由套接字
- AF_KEY,密钥套接字
- AF_UNSPEC,未指定
-
指定socket类型
- SOCK_STREAM:TCP
- SOCK_DGRAM:UDP
- SOCK_RAW:直接访问IP或ICMP,通常用于协议开发
-
通常0,选择tpye类型对应的默认协议(结合参数12)
- IPPROTO_TCP
- IPPROTO_UDP
- IPPROTO_SCTP
- IPPROTO_TIPC
返回值:网络描述符,错误-1
int bind(int sockfd,const struct sockaddr* addr,socklen_t addr len)
功能:绑定IP地址和端口号
参数:
-
网络描述符
-
包含IP和端口号的结构体指针struct sockaddr
-
协议族
-
端口号(要转换网络字节序)
-
IP地址结构体sockaddr_in(需要强转为struct sockaddr)
- sin_family,AF_INET,IP v4
- sin_port,端口号(转网络字节序htons(8888))
- sin_addr.s_addr,ip地址(转网络字节序inet_aton("192.",struct in_addr地址))
-
没有实际意义,只为跟sockaddr内存对齐,这样才能转换,不需要配置
//ipv4对应 struct sockaddr_in { __kernel_sa_family_t sin_family; /* Address family */ __be16 sin_port; /* Port number */ struct in_addr sin_addr; /* Internet address */ /* Pad to size of `struct sockaddr'. */ unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)]; }; struct in_addr { __be32 s_addr; };
-
-
参数2占用字节个数sizeof
int listen(int sockfd,int backlog)
功能:监听,用于服务器端,设置处理最大连接数
参数:
- 网络描述符
- 最大连接数
int accept(int sockfd,struct sockaddr* addr,socklen_t* addrlen)
功能:连接,服务器端,阻塞等待
参数:
- 网络描述符
- 客户端的地址结构体,不关心NULL
- 参数结构体的长度地址变量(变量int),不关心NULL
返回值:新的套接字描述符,用于读写操作
int connect(int sockfd,const struct sockaddr* addr,socklen_t addrlen)
功能:连接,客户端
参数:
- 服务器端的网络描述符(socket)
- 服务端的IP和端口号结构体指针
- 参数2结构体的内存大小sizeof
返回值:失败返回-1,errno看错误代码
2.2、其他inet_aton,inet_ntoa,send,recv,htons,htonl,ntohs,ntohl
int inet_aton(const char* straddr,struct in_addr* addrp)
char* inet_ntoa(struct in_addr inaddr)
inet_aton("192.168.1.1",&s_addr.sin_addr);
功能:转换字节序
- 把字符串形式192.168.1.111转换为网络格式
- 把网络形式的IP转换为字符串形式
参数:
- ip地址
- struct in_addr
read()
write()
ssize_t send(int s,const void* msg,ssize_t len,int flags)
ssize_t recv(int s,void* buf,ssize_t len,int flags)
功能:数据收发(UDP用secvmsg,sendmsg,recvfrom,sendto)
最后一个参数设置阻塞,一般0
uint16_t htons(uint16_t host16bitvalue)//本地转网络h to n
uint32_t htonl(uint32_t host32bitvalue)
uint16_t ntohs(uint16_t net16bitvalue)
uint32_t ntohl(uint32_t net32bitvalue)
功能:
- 返回网络字节序(大端字节序)
- 返回主机字节序
h代表host(主机),n代表net(网络)s代表short(2字节),l代表long(4字节),INADDR_ANY指定地址让操作系统自己获取
例子:双方收发消息
-
服务器等待连接
-
客户端连接服务器
-
双方互发消息
-
注:可以多个客户端连接,但会出错,因为进程资源竞争(竞争的输入光标get();)
可以优化为服务器作为中转来实现。例如:a发消息给b,服务器收到a消息转发给b
//客户端
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
int main(int argc,char **argv)
{
if(argc != 3)
{
printf("parameter error\n");
exit(-1);
}
int s_fd;
int r_read;
char msg[128];
char read_buf[128];
struct sockaddr_in s_addr;
memset(&s_addr,0,sizeof(struct sockaddr_in));
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1)
{
perror("socket");
exit(-1);
}
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&s_addr.sin_addr);
/*bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));*/
//客户端不需要bind
if(connect(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in)) == -1)
{
perror("connect");
exit(-1);
}
while(1)
{
if(fork() == 0)
{
while(1)
{
memset(&read_buf,0,sizeof(read_buf));
r_read = read(s_fd,read_buf,128);
printf("read:%d,buf:%s\n",r_read,read_buf);
}
}
while(1)
{
memset(&msg,0,sizeof(msg));
printf("input: ");
gets(msg);
write(s_fd,msg,strlen(msg));
}
}
close(s_fd);
return 0;
}
//服务端
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
int main(int argc,char **argv)
{
if(argc != 3)
{
printf("parameter error\n");
exit(-1);
}
int s_fd;
int x_fd;
int r_read;
char msg[128];
char read_buf[128];
struct sockaddr_in s_addr;
struct sockaddr_in x_addr;
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&x_addr,0,sizeof(struct sockaddr_in));
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1)
{
perror("socket");
exit(-1);
}
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&s_addr.sin_addr);
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
listen(s_fd,10);
int changdu = sizeof(struct sockaddr_in);
while(1)
{
x_fd = accept(s_fd,(struct sockaddr *)&x_addr,&changdu);//这里必须要变量
printf("client IP:%s\n",inet_ntoa(x_addr.sin_addr));
if(fork() == 0)
{
if(fork() == 0)
{
while(1)
{
memset(&msg,0,sizeof(msg));
printf("input: ");
gets(msg);
write(x_fd,msg,strlen(msg));
}
}
while(1)
{
memset(&read_buf,0,sizeof(read_buf));
r_read = read(x_fd,read_buf,128);
printf("read:%d,buf:%s\n",r_read,read_buf);
}
}
}
close(x_fd);
return 0;
}