Ping 命令的使用
Ping ip地址 查看ip的主机和自己是否互通
一、TCP三次握手:
a)三次握手
第一次:客户端向服务器端发送syn段,请求连接服务器的端口号
第二次:服务器端发送ack回应给客户端,并且发送一个syn段请求连接客户端的端口号
第三次:客户端给服务器端发送一个ack回应
以上过程使用了三个tcp段,称为三次握手
b)四次分手
1、客户端发送fin段给服务器
2、服务器端发送ack回应
3、服务器端发送给客户端一个fin段
4、客户端回应服务器端一个ack段
结束连接使用了四个tcp段,所以称为四次分手
二、编写TCP服务器端模型
步骤:
1) 创建socket端口描述符(等价于文件描述符)
Socket ( 2 )
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:创建一个通讯的端点,并返回一个描述符
参数:
domain:
AF_INET IPv4 版本 AF_INET6 IPv6版本
type:
SOCK_STREAM 用于流式文件传输(TCP)
SOCK_DGRAM 用于文件包传输模式(UDP)
protocol:一般取0
返回值:
-1 失败,errno被设置
成功返回一个新的文件描述符
int sock_fd=socket(AF_INET,SOCK_STREAM,0 );
2) 将这个描述符和socket pair(ip地址和端口号)绑定
bind(2)
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_taddrlen);
功能:将addr绑定给sockfd
参数:
sockfd:socket (2)的返回值
addrlen:addr指向的内容的长度
addr:地址。Structsockaddr遵循的访问方式。
在网络中有很多地址的结构,struct sockaddr地址结构中的通用方式。ipv4 ipv6
通用:
struct sockaddr{
unsigned shortsa_family; /*地址类型*/
char sa_data[14]; /* 14字节的协议地址 */
}
ipv4的地址结构:
struct sockaddr_in{
in_port_t sin_port; /*端口号*/
struct in_addr sin_addr; /* ip地址 */
};
struct in_addr{
int _addr_t s_addr;
}
返回值:成功返回0,错误返回-1,errno被设置
注意的两个问题:
第一, ip地址:我们使用的ip地址是点分十进制的ip地址(192.168.1.3),这种格式不被计算机系统识别,系统使用unsigned long格式的in_addr。 需要将字符串和in_addr相互转换。
字符串转in_addr的函数
inet_aton(3)
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
intinet_aton(const char *cp, struct in_addr *inp);
inet_addr(3)
in_addr_tinet_addr(const char *cp);
inet_pton(3)
#include<arpa/inet.h>
intinet_pton(int af, const char *src, void *dst);
参数:af AF_INET IPV4 AF_INET6 IPV6
in_addr转换为字符串的函数
inet_ntoa(3)
char*inet_ntoa(struct in_addr in);
inet_ntop (3)
#include<arpa/inet.h>
const char*inet_ntop(int af, const void *src, char*dst, socklen_t size);
第二, 端口号
大端和小端的问题
一般情况下,主机采用的是小端,也称做主机字节序
网络采用的是大端,也称做网络字节序。
转换函数:
htonl(3)
htons(3)
ntohl(3)
ntohs(3)
n:network
h:host
l:long
s:short
#include<arpa/inet.h>
uint32_thtonl(uint32_t hostlong);
uint16_t htons(uint16_thostshort);
uint32_tntohl(uint32_t netlong);
uint16_tntohs(uint16_t netshort);
The htonl()function converts the unsigned integer hostlong from hostbyte order to networkbyte order.
The htons()function converts the unsigned short integer hostshort fromhost byte order tonetwork byte order.
The ntohl()function converts the unsigned integer netlong from networkbyte order to hostbyte order.
The ntohs() function converts the unsigned shortinteger netshort from network byte order to host byte order.
3) 监听这个描述符
listen(2)
#include<sys/types.h>
#include<sys/socket.h>
int listen(intsockfd, int backlog);
功能:监听文件描述符
参数:
Sockfd:由socket(2)产生的
Backlog:未决最大数(排队阻塞等待人数)
4) 阻塞等待客户端发起连接。收到客户端的连接请求的时候,返回一个连接描述符(connfd)
accept(2)
#include<sys/types.h>
#include<sys/socket.h>
int accept(intsockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:基于sockfd连接上的客户的请求创建一个连接fd返回(connfd),这个connfd用于跟客户端的通讯。
参数:
sockfd:监听的对象就是这个文件描述符
addr:用来保存客户端的ip地址和端口号。若不需要知道客户端的ip地址和端口号,则设置为NULL
addrlen:用于指定第二个参数的长
度
返回值:是一个新的文件描述符
5) 通过connfd读取客户端的请求内容
Read(2)
int read(int connfd,void *buff , int count);
6) 处理客户端的请求
write(2)
int write(int connfd,const void *buf, int count);
7) 回应给客户端,回到第5步。直到客户端请求完毕为止,进入第八步
8) 关闭和客户端的连接
close(2)
int close(int connfd);
举例:服务器代码:server.c 代码模型
服务器端提供的服务:将客户端传过来的字符串转换成大写
1#include<stdio.h>
2#include<strings.h>
3#include<sys/types.h>
4#include<unistd.h>
5#include<sys/socket.h>
6#include<netinet/in.h>
7#include<linux/socket.h>
8#include<arpa/inet.h>
9#include<ctype.h>
10int main(void){
11 char buff[128];
12 struct sockaddr_in seraddr,cliaddr;
13 /* 创建socket描述符 */
14 int sock_fd=socket(AF_INET,SOCK_STREAM,0);
15 // 初始化seraddr的内容
16 bzero(&seraddr,sizeof(seraddr));//清零
17 seraddr.sin_family=AF_INET;
18 seraddr.sin_port=htons(4666/*设置端口号*/);
19 //INADDR_ANY代表这台服务器上的所有IP
20 //通过IP找到这台机器,通过端口号找到提供服务的进程
21 seraddr.sin_addr.s_addr=htonl(INADDR_ANY);
22 /* 绑定描述符和ip地址及端口号 */
23 bind(sock_fd,(struct sockaddr *)&seraddr,sizeof(seraddr));
24 /* 监听socketfd描述符 */
25 listen(sock_fd,5);//最大未决数是5
26 printf("listening....\n");
27 while(1){
28 int cliaddr_len=sizeof(cliaddr);
29 /* 等待客户请求的到达 */
30 int conn_fd=accept(sock_fd,NULL,&cliaddr_len);
31 int n=read(conn_fd,buff,128);
32 for(int i=0;i<n;i++){ /*除了数据处理这个模块,其它都是框架*/
33 //数据处理,将字符转换为大写
34 buff[i]=toupper(buff[i]);
35 }
36 write(conn_fd,buff,n);
37 close(conn_fd);
38 }
39 return 0;
40 }
三、编写TCP客户端模型
1)创建sock_fd
2)connect到服务器
Connect(2)
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const structsockaddr *addr , socklen_t addrlen);
功能:在socket(2)上初始化一个连接
参数:
sockfd:socket(2)的返回值
addr:服务器的ip地址和端口号
addrlen:addr的长度
3)数据的输入/输出
4)关闭和服务器的连接
代码模型:
1#include<stdio.h>
2#include<strings.h>
3#include<sys/socket.h>
4#include<sys/types.h>
5#include<netinet/in.h>
6#include<unistd.h>
7int main(void){
8 struct sockaddr_in servaddr;
9 int n;
10 char buff[32];
11 /* 创建socket描述符 */
12 int s_fd=socket(AF_INET,SOCK_STREAM,0);
13 /* connect 将s_fd和服务器端建立连接 */
14 bzero(&servaddr,sizeof(servaddr));
15 servaddr.sin_family=AF_INET;
16 servaddr.sin_port=htons(4666/*本客户端提供的端口号*/);
17 /* 点分十进制的字符串127.0.0.1 */
18 //服务器的ip地址,在此使用本机进行模拟
19 inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr);//地址转换
20 /* 连接服务器 */
21 connect(s_fd,(struct sockaddr *)&servaddr,sizeof(servaddr));
22 write(s_fd,"one dream one world",20);
23 n=read(s_fd,buff,22);
24 write(1,buff,n);
25 printf("\n");
26 /* 关闭和服务器端的连接 */
27 close(s_fd);
28 return 0;
29 }
在一个终端开启服务器
tarena@tarena-virtual-machine:~/day33$./server
listening....
(等待客户端发起请求)
tarena@tarena-virtual-machine:~/day33$./client
ONE DREAM ONE WORLD
修改程序:在server.txt里获取服务器地址
作业:服务器上有一个文件 test.txt 文件内容随便写。从客户端连接服务器端,然后将服务器端的test.txt文件下载到客户端