目录
socket
这是为了实现以上的通信过程而建立成来的通信管道,其真实的代表是客户端和服务器端的一个通信进程,双方进程通过socket进行通信,而通信的规则采用指定的协议。
socket只是一种连接模式,不是协议,socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。tcp、udp,简单的说(虽然不准确)是两个最基本的协议,很多其它协议都是基于这两个协议如,http就是基于tcp的,.用socket可以创建tcp连接,也可以创建udp连接,
这意味着,用socket可以创建任何协议的连接,因为其它协议都是基于此的。
一、基础知识
1、网络、互联网的目的:资源共享,信息交互
2、IP地址:用来唯一标识一台主机,是一个32位的二进制数,通常用“点分十进制”来表示。
inet_addr()可以将点分十进制转换为无符号整型
3、MAC地址:是网卡决定的,固定的,一般不可改
4、端口号:应用程序的代号
5、socket:IP地址+端口号 可以确定一个进程(唯一得表示了使用TCP通信的一端)
6、socket 套接字
头文件 #include<sys/socket.h>
表示socket地址的是 结构体struct sockaddr
Ipv4专用socket地址 struct sockaddr_in
7、主机字节序(小端)和网络字节序(大端)
由于网络中不同主机,字节序列可能不同,所以在传输端口号的时候,规定统一将转为大端,也就是把大端作为网络字节序列。
有四个方法可以使用:
htons 主机字节序列转网络字节序列 短整形
htonl 主机字节序列转网络字节序列 长整形
ntohs 网络字节序列转主机字节序列 短整形
ntohl 网络字节序列转主机字节序列 长整形
大端:将低地址放高位
小端:将高地址放高位
8、TCP/UDP
TCP传输控制协议(面向连接的,可靠的,流式服务)
UDP用户数据报协议(无连接,不可靠的,数据报服务)
9、服务端程序和客户端程序
网络程序是先由服务器程序启动,等待客户端的程序运行并建立连接。一般来说是服务端的程序 在一个端口上监听,直到有一个客户端的程序发来了请求。
10、netstat是用来显示网络的连接,路由表和接口统计等网络的信息.
netstat命令用于显示与IP、TCP、UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况。netstat是在内核中访问网络及相关信息的程序,它能提供TCP连接,TCP和UDP监听,进程内存管理的相关报告。
11、ifconfig(查看ip地址)
获取网络接口配置信息
12、端口值
0-1024知名端口,http协议使用80端口,root管理员可以使用
1024-4096 保留端口
4096以上 临时端口,应用程序使用
13、iptables -F 关闭防火墙
14、ping用来测试与目标主机的连通性
15、查看tcp连接状态
(1)ss -t -a
ss 可以用来获取 socket统计信息,此命令输出的结果类似于 netstat输出的内容,但它能显示更多更详细的 TCP连接状态的信息,且比 netstat 更快速高效。
-a, --all 显示所有套接字(sockets)
-t, --tcp 仅显示 TCP套接字(sockets)
-u, --udp 仅显示 UCP套接字(sockets)
(2)netstat :执行结果中 State 一列表示的就是 tcp 连接状态
netstat命令的功能是显示网络连接、路由表和网络接口信息,可以让用户得知目前都有哪些网络连接正在运作。
-a 显示所有socket,包括正在监听的。
-c 每隔1秒就重新显示一遍,直到用户中断它。
-i 显示所有网络接口的信息,格式同“ifconfig -e”。
-n 以网络IP地址代替名称,显示出网络连接情形。
-r 显示核心路由表,格式同“route -e”。
-t 显示TCP协议的连接情况。
-u 显示UDP协议的连接情况。
-v 显示正在进行的工作。
netstat -ant 查看当前的所有tcp连接
二、步骤详解
1、通用socket地址 struct sockaddr
协议族也称domain
2、专用socket地址
![](https://i-blog.csdnimg.cn/blog_migrate/9864b09f314bed19e1b4d92e15fc924d.png)
3、IP地址转换函数
4、创建socket--socket()
socket系统调用成功时返回一个socket文件描述符,失败则返回-1并设置errno
5、命名socket--bind()
创建时,我们给她指定了地址族,但是并未指定使用该地址族中的哪个具体socket地址。将一个socket与socket地址绑定称为socket命名。
服务器端通常要命名socket,因为只有命名后客户端才知道如何连接;客户端通常不需要命名socket,而是采用匿名的方式(使用操作系统自动分配的socket地址)
6、监听socket--listen()
监听队列的长度如果超过backlog,服务器将不受理新的客户连接,客户端也将受到错误信息。
内核版本2.2之后,backlog只表示处于完全连接状态的socket上限。backlog参数典型值时5。
listen成功时返回0,失败则返回-1并设置errno。
7、接受连接--accept()
8、发起连接--connect()
connect成功后则三次握手完成,建立好连接了。
9、TCP 数据接收与发送recv();send();
10、UDP数据读写
11、关闭连接--close()
三、代码(基于TCP)
服务器端
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);//创建套接字
assert(sockfd != -1);
struct sockaddr_in saddr,caddr;//Ipv4专用socket地址 struct sockaddr_in
memset(&saddr,0,sizeof(saddr)); //memset是因为有占位的,所以先清空
saddr.sin_family = AF_INET; //地址族 ipv4
saddr.sin_port = htons(7000);//端口号
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");//ip地址 inet_addr()可以将点分十进制转换为无符号整型
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//绑定套接字
assert( res != -1);
listen(sockfd,5); //监听套接字
while(1)
{
int len = sizeof(caddr);
int c = accept(sockfd,(struct sockaddr*)&caddr,&len);//接收连接
if( c < 0 )
{
continue;
}
printf("accept c = %d\n",c);
while(1)
{
char buff[128] = {0};
int n = recv(c,buff,127,0);//接收数据
if( n <= 0)
{
break;
}
printf("buff=%s\n",buff);
send(c,"ok",2,0); //发送数据
}
close(c); //关闭连接
}
}
客户端
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0); //创建套接字
assert( sockfd != -1 );
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(7000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr)); //发送连接
assert( res != -1);
while(1)
{
char buff[128] = {0};
printf("input:\n");
fgets(buff,128,stdin);
if( strncmp(buff,"end",3 ) == 0 );
{
break;
}
send(sockfd,buff,strlen(buff),0); //发送数据
memset(buff,0,128);
recv(sockfd,buff,127,0); //接收数据
printf("buff=%s\n",buff);
}
close(sockfd); //关闭连接
exit(0);
}
四、代码(基于UDP)
服务器端
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);//SOCK_DGRAM -udp 创建
assert( sockfd != -1 );
struct sockaddr_in saddr,caddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr)); 绑定
assert( res != -1 );
while(1)
{
int len = sizeof(caddr);
char buff[128] = {0};
recvfrom(sockfd,buff,127,0,(struct sockaddr*)&caddr,&len); //接收
printf("ip=%s,port=%d,buff=%s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),buff);
sendto(sockfd,"ok",2,0,(struct sockaddr*)&caddr,sizeof(caddr)); //发送
}
}
客户端
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);//udp
assert( sockfd != -1 );
struct sockaddr_in saddr,caddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
while(1)
{
char buff[128] = {0};
printf("input:\n");
fgets(buff,128,stdin);
if( strncmp(buff,"end",3) == 0 )
{
break;
}
sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&saddr,sizeof(saddr));
memset(buff,0,128);
int len = sizeof(caddr);
recvfrom(sockfd,buff,127,0,(struct sockaddr*)&saddr,&len);//每次读取数据都要获取发送端socket的地址
}
close(sockfd);
}
问题:Socket 编程中 listen 第 2 个参数是什么?
Listen()方法是创建监听队列,一般有两个,一个是未完成三次握手的,一个是已完成三次握手的。
Linux 中第二个参数代表已完成三次握手的队列长度。在一些 unix 系统中第二个参数表示未完成和已完成两个队列长度之和。