今天介绍的是基于ipv4的socket网络编程,我们知道socket API是一层的抽象的网络编程接口,但各网络协议的地址却是各不相同的。
下图是sockaddr数据结构图:
ipv4和ipv6的地址格式定义在netinet/in.h中,ipv4地址用sockaddr_in结构体表示,包括16位的端口号和32位的ip地址,ipv6地址用sockaddr_in6表示,包括16位的端口号和128位的ip地址和一些控制字段。UNIX Domain Socket的地址格式定义在sys/un.h中,⽤用sockaddr_un结构体表 ⽰示。
IPv4、IPv6和UNIX Domain Socket的地 址类型分别定义为常数AF_INET、AF_INET6、AF_UNIX。所以,我们根据结构体首地址类型字段就可以确定结构体中的内容。
sockaddr_in在/usr/include/usr/include/linux/in.h里面
socket API是一套通用的接口,可以接受各种类型的sockaddr结构体指针,但是参数需为void*类型的,由于socket API早于ANSIC所以那时没有void*类型,所以我们要将函数参数转为struct sockaddr *类型。
TCP连接所用到的一些函数
1、socket函数
作用 :socket函数是一种可用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源的函数
参数:
domain: 一个地址描述。目前仅支持AF_xxxx格式:AF_INET代表IPv4地址
type: 新套接字的类型描述:SOCK_STREAM : 提供面向连接的稳定数据传输,即TCP协议。 SOCK_dGRAM:表示面向数据报的传输协议,即UDP协议
protocol : 套接字所用的协议。如调用者不想指定,可用0指定,表示缺省。
返回值:成功返回文件描述符,失败返回-1
2、bind
函数作用:声明sockfd所处的状态为监听状态,将套接字地址与所创建字号联系起来
参数:
af: 通信发生的区域
type: 要建立的套接字类型
procotol: 使用的特定协议
3、accept和connect
这两个是为了完成连接,参数和上面的相同。accept是服务器端,connect是客户端
4、listen
功能:面向连接服务器,表明它愿意接收连接
返回值:成功返回0,失败返回-1
代码展示:
tcp_server.c:
#include
#include
#include
#include
#include
#include
#include
#include
#include
static usage(const char* proc)
{
printf("Usage: %s[local_ip] [local_port]\n",proc);
}
int startup(const char* ip,int port)
{
int sock = socket(AF_INET,SOCK_STREAM,0); //创建socket
if(sock < 0)
{
perror("socket");
exit(2);
}
printf("sock:%d\n",sock);
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = inet_addr(ip);
if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0) //进行绑定
{
perror("bind");
exit(3);
}
if(listen(sock,10) < 0) //将资源设置为监听状态
{
perror("listen");
exit(4);
}
return sock;
}
int main(int argc,char* argv[])
{
if(argc != 3)
{
usage(argv[0]);
return 1;
}
int listen_sock = startup(argv[1],atoi(argv[2]));//上面返回的是listen的sockt
while(1)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len);//监听套接字
if(new_sock < 0)
{
perror("accept");
continue;
}
//printf("get a new client [%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
printf("get a new client\n");
//客户端和服务器未能建立连接 继续监听
//先read后write
while(1)
{
char buf[1024];
ssize_t s = read(new_sock,buf,sizeof(buf)-1);
if(s > 0)
{
buf[s] = 0;
printf("client# %s\n",buf);
write(new_sock,buf,strlen(buf));
}
else if(s == 0 )
{
printf("client close!!!\n");
break;
}
else
{
perror("read");
break;
}
}
}
return 0;
}
tcp_client.c:
#include
#include
#include
#include
#include
#include
#include
#include
static void usage(const char* proc)
{
printf("%s [local_ip] [local_port]\n",proc);
}
int main(int argc,const char* argv[])
{
if(argc != 3)
{
usage(argv[0]);
return 1;
}
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0)
{
perror("socket");
return 2;
}
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[2]));
server.sin_addr.s_addr = inet_addr(argv[1]);
if(connect(sock,(struct sockaddr*)&server,sizeof(server)) < 0)
{
perror("connect");
return 3;
}
//客户端不需要绑定 也不需要监听
//客户端是先write后read
char buf[1024];
while(1)
{
printf("please Enter# ");
fflush(stdout);
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s > 0)
{
buf[s-1] = 0;
write(sock,buf,strlen(buf));
ssize_t _s = read(sock,buf,sizeof(buf)-1);
if(_s > 0 )
{
buf[_s] = 0;
printf("server echo# %s\n",buf);
}
}
}
close(sock);
return 0;
}