通用socket地址
#include
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];//因为这是端口号和ip地址共用,不知道哪里是端口号,哪里是ip地址,所以一般不用这个结构体
};
一般使用专用结构体
struct sockaddr_in
{
sa_family sin_family;//地址族:AF_INET
u_int16_t sin_port;//端口号,要用网络字节序表示
struct in_addr sin_addr;//IPv4地址结构体
};
struct in_addr
{
u_int32_t s_addr;//IPv4地址,要用网络字节序表示
};
主机字节序和网络字节序之间的转换:
#include
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);
IP地址转换函数:
#include
in_addr_t inet_addr(const char *strptr);
//用于将点分十进制字符串表示的IPV4地址转换为用网络字节序整数表示的IPv4地址。失败返回INADDR_NONE;
int inet_aton(const char *cp,struct in_addr* inp);//功能和inet_addr一样,但是将转换的结果存储在参数inp指向的地址结构中。成功返回1,失败返回0;
char* inet_ntoa(struct in_addr in);//将用网络字节序整数表示的IPV4地址转换为用点分十进制表示的IPV4地址。(不可重入);
socket在linux上是一个文件描述符
#include
#include
int socket(int domain, int type, int protocol);
domain参数表示系统使用哪个底层协议族。对于TCP/IP协议族而言,参数应该设置为PF_INET(IPV4)或PF_INET6(IPV6);而对于linux本地域协议族而言,应该为PF_UNIX
type参数表示服务类型:主要有SOCK_STREAM(流服务)和SOCK_UGRAM(数据报)服务。对于TCP/IP协议而言流服务就是TCP协议,数据报服务就是UDP协议。
protocol参数表示在前两个参数构成的协议下,在选择具体的协议。一般都设置为0;
在创建socket时我们给定了它一个地址族,但是并没有指定使用那个具体的socket地址。就需要将一个socket矛socket地址绑定在一起,就是为socket命名。
在服务器端,通常需要命名,只有命名后客户端才能知道该如何连接它。客户端不需要命名socket,采用匿名的方式,计时用操作系统自动分配的socket地址。
#include
#include
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen );
bind将my_addr所指的socket地址分配给未命名的sockfd文件描述符,addrlen参数指出了socket地址的长度。
bind成功返回0,失败返回-1;
socket被命名之后,还不能马上接受客户连接,需要创建监听队列以存放待处理的客户连接
#include
init listen (int sockfd, int backlog);
sockfd参数指定被监听的socket。backlog参数提示内核监听队列的最大长度。
成功返回0。失败返回-1;
从监听队列中接受一个连接
#include
#include
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd为监听的sockfd,addr为用来获取被接受连接的远端socket地址,socket地址长度为addrlen.
函数成功返回一个新连接的socket,该socket唯一的标识了被接受的这个连接。失败返回-1。
服务器通过listen调用来被动接受连接,客户端需要通过系统调用来建立连接。
#include
#include
int connect (int sockfd, const struct scokaddr *serev_addr , socklen_t addrlen);
sockfd参数由socket系统调用返回一个socket,serv_addr参数是服务器监听的socket地址,addrlen参数指定这个地址长度。
函数成功返回0,一旦连接成功,这个socket就唯一标识这个连接。失败返回-1。
关闭一个连接实际上就是关闭了一个对应的socket,可以通过关闭普通的文件描述符系统调用完成;
#include
int close (int fd);
可以关闭一个连接,也可以关闭一个通信
#include
#include
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);
recv读取sockfd上的数据,buf和len参数分别指定度缓冲区的位置和大小,flags参数的含义为数据收发提供额外的控制,一般设置为0;
实现代码:
服务器端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int listenfd=socket(PF_INET,SOCK_STREAM,0);//创建一个socket文件描述符
assert(listenfd!=-1);
struct sockaddr_in ser;
memset(&ser,0,sizeof(ser));
ser.sin_family=AF_INET;//设置协议为tcp协议族
ser.sin_port=htons(6000);//将主机子节序转换为网络子节序
ser.sin_addr.s_addr=inet_addr("127.0.0.1");//将点分十进制字符串转换为网络字节序整数
int res=bind(listenfd, (struct sockaddr*)&ser,sizeof(ser));//命名socket
assert(res!=-1);
listen(listenfd,5);//监听socket
while(1)
{
struct sockaddr_in cli;
int len=sizeof(cli);
int c =accept(listenfd,(struct sockaddr*)&cli,&len);//接收连接,没有客户请求时阻塞
if(c==-1)
{
printf("link is error\n");
break;
}
while(1)
{
char data[128]={0};
int n=recv(c,data,127,0);//发送消息
if(n==-1)
{
printf("recv data error\n");
break;
}
if(n==0)//断开连接时
{
printf("%s:%d unlink\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
break;
}
printf("%s: %d ::",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
printf("%s \n",data);
send(c,"Ok",2,0);
}
close(c);//关闭连接
}
exit(0);
}
客户端:
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);//创建一个socket文件描述符
assert(sockfd!=-1);
struct sockaddr_in ser_addr;
memset(&ser_addr,0,sizeof(ser_addr));
ser_addr.sin_family=AF_INET;
ser_addr.sin_port=htons(6000);
ser_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
int res=connect(sockfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));//创建一个连接
while(1)
{
printf("please input\n");
char buff[128]={0};
fgets(buff,128,stdin);//从标准输入设备获取
if(strncmp(buff, "bye",3)==0)
{
break;
}
send(sockfd,buff,strlen(buff)-1,0);//发送消息
char data[128]={0};
int n=recv(sockfd,data,sizeof(data),0);//接受消息
}
close(res);
exit(0);
}