目录
一、IP主机和IP地址
二、IP数据包格式
三、TCP/IP在网络中的数据流
四、套接字编程
4.1 创建套接字
4.2绑定socket和端口号
4.3、UDP 套接字
4.4 TCP 套接字
五、 UDP回显 服务器-客户机程序
六、TCP 回显服务器-客户机
TCP/IP 是互联网的基础, TCP代表传输控制协议,IP代表互联网协议。目前有两个版本IP,一个是32位地址的IPv4 和一个是128位的 IPv6 。而IPv4 是现如今使用最多的IP版本,也是这次讨论的重点。
每一个注意由一个32位的IP地址来标识。为了方便起见,通常用32位的IP低质号用记点法标识例如:134.121.64.1 也可以用主机名标识如 dns1.eec.wsu.edu 。实际上应用程序通常使用主机名而不是IP地址。因为给定其中一个,我们都可以通过dns(域名系统)服务器找到另外一个,两者之间可以相互转换。
IP地址分为两部分 NeiworkID 和 HostID 字段。其中,IP 可以分为A~E类。例如B类IP分为一个16位NeiworkID,前两位是10 。发往UP地址的数据包首先被发送到具有相同networkID的路由器默认IP地址位127.0.0.1.
IP数据包由IP头、发送方IP地址、接收方IP地址和数据组成。每个IP数据包的大小最大为64K。IP头包含有关数据包的信息。内容如下:
IP主机可能距离很远通常不可能从一个主机直接向另一个主机发送数据包。路由器是转发数据包的特殊IP主机,它可以向普通IP主机和其他路由器发送数据包,
应用层的数据被传到传输层会添加TCP 或者UDP包头来标识使用的传输协议。合并后的数据被传到IP网络层,添加一个包含IP地址的IP报头来标识发送和接收主机。然后合并后的数据传递到网络链路层,再次将数据分成多个帧,添加发送和接收网络的地址,用于在物理网络之间的传输。
在 netdb.h 和 sys/socket.h中有套接字的地址结构定义
struct sockaddr_in {
sa_family_t sin_family; //TCP/IP网络的sin_family 始终设置为AF_INET
in_port_t sin_port; //包含网络字节顺序排列的端口号
struct in_addr sin_addr ; //按网络字节顺序排列的IP地址
}
struct in_addr{
unit32_t s_addr; //按网络字节顺序排列的IP地址
}
服务器套接字编程步骤如下
客户端套接字编程步骤如下
int socket(int domain, int type, int protocol);
domain参数 | 参数含义 |
---|---|
AF_INET | IPv4协议 |
AF_INET6 | Ipv6协议 |
AF_LOCAL | Unix协议域/只在本机内通信的套接字 |
AF_ROUTE | 路由套接字 |
AF_KEY | 秘钥套接字 |
type参数 | 参数含义 |
---|---|
SOCK_STREAM | 字节流套接字 (TCP) |
SOCK_DGRAM | 数据报套接字 (UDP) |
SOCK_SEQPACKET | 有序分组套接字 |
SOCK_RAW | 原始套接字 |
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
bind系统调用将addr指定的地址分配文件描述符 sockfd所引用的套接字,addrlen指定addr所指向地址结构的大小对于用于联系其他UDP服务器主机的UDP套接字,必须绑定到客户机地址,允许服务器发回应答。对于接收客户机的TCP套接字,必须先将其绑定到服务器主机地址。
将缓冲区中len字节数据发送到dest_addr标识的目标主机
ssize_t sendto(int fd, const void *buf,size_t len,int flags,const struct sockaddr *dest_addr,socklen_t tolen);
从缓冲区中len字节数接收数据
ssize_t recvfrom(int fd, void *buf, size_t len,int flags,struct sockaddr *src_addr, socklen_t *len);
在绑定socket 和端口号后 TCP服务器用 listen 和 accpet 来监听、接受客户机的连接
//backlog 定义了等待连接的最大长度
int listen(int sockfd, int backlog);
//提取等待连接队列上的第一个连接请求同于监听sockfd,执行时造成进程阻塞,直到客户机通过connect建立连接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); // 返回新的文件描述符
//如果sockfd 时SOCK_DGRAM (USD 套接字)类型时,addr时发送数据报的默认地址,也是接收数据报的唯一地址。如果时 SOCK_STREAM (TCP 套接字)类型时,connect连接到绑定到addr指定的套接字
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
发送/接收数据
可以使用 send/read 或者 recv/write 来接收和发送数据。
read 和 write 是对文件描述符的读取和写入
//对已经连接的fd 发送数据 ,flags 通常是0
ssize_t send(int fd , const void *buf , size_t len , int flags);
//对已经连接的fd 接收数据 ,flags 通常是0
ssize_t recv(int fd , void *buf , size_t len , int flags);
服务器端:
#include
#include
#include
#include
#include
#include
#define BUFLEN 256
#define PORT 1234
char line[BUFLEN];
struct sockaddr_in me ,client;
int sock ,rlen=sizeof(client);
socklen_t clen = sizeof (client);
int main (){
printf("1. create a UDP socket\n");
sock = socket (AF_INET,SOCK_DGRAM,IPPROTO_UDP);
printf("2. fill me with server address and port number \n");
memset((char *) & me ,0,sizeof(me));
me.sin_family = AF_INET;
me.sin_port = htons(PORT);
me.sin_addr.s_addr = htonl(INADDR_ANY);
printf("3. bind socket to server IP and port \n");
bind (sock ,(struct sockaddr *) & me ,sizeof (me));
printf("4. wait for datagram \n");
while (1){
memset(line ,0,BUFLEN);
printf("UDP server:waiting for datagram \n");
rlen =recvfrom (sock , line , BUFLEN,0,(struct sockaddr *)&client ,&clen);
printf("received a datagram from [host : prot] =[%s :%d] \n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
printf("rlen=%d:line%s \n",rlen,line);
printf("send reply \n");
sendto (sock ,line,rlen,0,(struct sockaddr*)&client,clen);
printf("------------------------\n");
}
}
UDP客户机端
#include
#include
#include
#include
#include
#include
#define BUFLEN 256
#define SERVER_PORT 1234
#define SERVER_HOST "127.0.0.1"
char line[BUFLEN];
struct sockaddr_in server;
int sock ,rlen,slen = sizeof (server);
int main (){
printf("1. create a UDP socket\n");
sock = socket (AF_INET,SOCK_DGRAM,IPPROTO_UDP);
printf("2. fill in server address and port number \n");
memset((char *) & server ,0,sizeif(server));
server.sin_family = AF_INET;
server.sin_port = htons(SERVER_PORT);
inet_aton(SERVER_PORT , &server.sin_addr);
while(1){
printf("Enter a line: \n");
fget(line,BUFLEN,stdin);
line[strlen(line) -1 ]=0;
printf("send a line to server \n");
sendto(sock,line,strlen(len),0,(struct sockaddr *)&server,slen);
memset(line ,0 ,BUFLEN);
printf("try to receive a line from server \n");
rlen =recvfrom (sock,line,BUFLEN,0,(struct sockaddr *)&server,slen);
printf("rlen =%d : line=%s \n" ,rlen ,line);
}
}
服务器端
#include
#include
#include
#include
#include
#define MAX 256
#define SERVER_IP "127.0.0.1"
#define SERVER_HOST "localhost"
#define SERVER_PORT 1234
struct sockaddr_in server_addr ,client_addr;
int mysock,csock; //sock的文件描述符
int r,n; //辅助变量
socklen_t len;
int server_init(){
printf("=============server init========\n");
printf("1. create a TCP socket\n");
mysock = socket (AF_INET,SOCK_STREAM,0);
if(mysock < 0){
printf("socket call failed\n"); exit(1);
}
printf("2. fill server_addr with host_ip and port number \n");
server_addr .sin_family = AF_INET;
server_addr .sin_port = htons(SERVER_PORT);
server_addr .sin_addr.s_addr = htonl(INADDR_ANY);
printf("3. bind socket to server address \n");
r =bind (mysock ,(struct sockaddr *) & server_addr ,sizeof (server_addr ));
if( r<0){
printf("bind call failed\n"); exit(3);
}
printf(" hostname =%s, port = %d \n", SERVER_HOST ,SERVER_PORT);
listen(mysock , 5 );
printf("============init done ===========\n");
return 0;
}
int main (){
char line [MAX];
server_init();
while (1){
printf("server accepting new connection... \n");
len = sizeof(client_addr);
csock = accept(mysock,(struct sockaddr *)&client_addr,&len);
if(csock <0){
printf("accept failed\n"); exit(1);
}
printf("Server: accept a client :IP=%s , port = %d \n",
inet_ntoa(client_addr.sin_addr) ,
ntohs(client_addr.sin_port));
while(1){
n=read(csock,line,MAX);
if(n=0) {
printf("clinet dead ,server loop\n");
close(csock); break;
}
printf("read n =%d byte;line = %s \n", n,line);
n=write(csock,line,MAX);
printf("write n =%d byte;ECHO = %s \n", n,line);
printf("ready for next request\n");
}
}
}
客户端:
#include
#include
#include
#include
#include
#include
#include
#define MAX 256
#define SERVER_HOST "localhost"
#define SERVER_PORT 1234
struct sockaddr_in server_addr ;
int sock,r;
int client_init(){
printf("=============client init========\n");
printf("1. create a TCP socket\n");
sock = socket (AF_INET,SOCK_STREAM,0);
if(sock < 0){
printf("socket call failed\n"); exit(1);
}
printf("2. fill server_addr with host_ip and port number \n");
server_addr .sin_family = AF_INET;
server_addr .sin_port = htons(SERVER_PORT);
server_addr .sin_addr.s_addr = htonl(INADDR_ANY);
printf("3. connecting to server \n");
r = connect (sock ,(struct sockaddr *) & server_addr ,sizeof (server_addr ));
if( r<0){
printf("bind call failed\n"); exit(3);
}
printf(" hostname =%s, port = %d \n", SERVER_HOST ,SERVER_PORT);
printf("============client init done ===========\n");
return 0;
}
int main (){
char line [MAX] , ans[MAX];
int n;
client_init();
printf(" ********processing loop *******************");
while (1){
printf("put a line... \n");
bzero(line ,MAX);
fgets(line,MAX,stdin);
line[strlen(line) - 1] = 0;
if(line[0] ==0) exit(0);
n=write(sock,line,MAX);
printf("client : wrote n =%d bytes ; line %s :\n", n , line);
n =read(sock ,ans,MAX);
printf("client : read n =%d bytes ; line %s :\n", n , line);
}
}