作者:小小鸟_--旺旺 | 来源:互联网 | 2023-09-15 17:15
1.什么是socketsocket(套接字),简单来说是IP地址与端口(port)的组合,可以与远程主机的应用程序进行通信。通过IP地址可以确定一台主机,而通过端口则可以确定某一个
1.什么是socket
socket(套接字),简单来说是ip地址与端口(port)的组合,可以与远程主机的应用程序进行通信。通过ip地址可以确定一台主机,而通过端口则可以确定某一个应用程序。ip+端口则可以完全确定某台主机的某个应用。socket起源于unix,类似一种特殊文件,可以进行打开,关闭,读写操作。总而言之,有了socket就可以与网络上的主机进行通信。
2.tcp/udp 协议
要进行网络通信,就要进行一定规则约束,tcp/udp就是这样的协议,规定了通信的规则。
tcp是可靠的,面向连接的双向数据传输协议。可靠是指数据不会重复,也不会丢失。每当发送方发送一个数据给接收方时,如果接收方接收到了该数据,则会发送确认信息给发送方表示”我已经收到该数据了,你可以发送下一条数据了“,收到确认信息后,发送方才会发送下一条数据。这样就可以确定信息的无误。双向传输指双方都可以作为发送方或接收方。
udp是不可靠的,无连接的双向传输协议。udp协议只管发送数据,不会确认你有没有收到,只负责发,不负责确认,所以是不可靠的。udp适用于传输视频之类的,视频就算丢失一两帧也不会有太大影响。
socket既可以是基于tcp,也可以是基于udp的,根据需求选择即可。
3.一个简单的通信程序
用一个简单的例子来说明socket的用法。用socket写的程序一般分为,两部分,一个是服务器端,一个是客户端.
下面说明服务器端创建过程
1).首先要有套接字才能进行通信,创建套接字的函数是
1 int socket(int af, int type, int protocol);
af:表示地址族,常用的有af_inet表示使用ipv4地址,af_inet6表示使用ipv6地址
type:传输类型常用有sock_stream ,sock_dgram,流式传输,报文传输
protocol:要使用的协议常用有 ipproto_tcp 和 ipptoto_udp,分别表示tcp,udp协议
返回一个套接字描述符,也就是一个整型。
2).用bind()函数确定socket各种属性
1 int bind(int sock, struct sockaddr *addr, socklen_t addrlen);
sock:要绑定的套接字
addr:sockaddr地址结构体,里面包含使用的协议,ip地址,端口等。要自己设定
addrlen:sockaddr的大小,可以用sizeof()获取
下面的代码展示创建一个套接字与绑定的过程
1 //使用ipv4地址,tcp协议 2 serversocket = socket(af_inet, sock_stream, ipproto_tcp); 3 sockaddr_in addr; 4 addr.sin_addr.s_un.s_addr = htonl(addr_any);//表示任何的ip过来连接都接受 5 addr.sin_family = af_inet;//使用ipv4地址 6 addr.sin_port = htons(6666);//使用6666号端口 7 bind(serversocket, &addr, sizeof(sockaddr));//将套接字与端口6666,设定接收的ip绑定
3).listen函数监听
设定属性后,服务器端就可以开始监听了,监控是否有客户端请求连接。
函数原型
1 int listen(int sock, int backlog);
sock:套接字
backlog:允许多少个客服端连接
4).accept函数等待连接
accept是一个阻塞函数,如果没有客户端清求连接会一直等待在这里
1 int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
sock:套接字,
addr:sockaddr 结构体
addrlen:addr的长度,可以用sizeof求到
要注意该函数的返回值,它会返回一个新的套接字,这个新的套接字是用来与客户端通信的套接字,之前那个套接字是监听的套接字,要分清楚。
5).send/recv发送/接收信息
与客户端连接成功后就可以进行通信了。可以通信的函数有write/read,send/recv等,这里介绍send/recv
1 int send(int sockfd, const char *buf, size_t len, int flags); 2 3 int recv(int sockfd, char*buf, size_t len, int flags);
sockfd:套接字
buf:发送数据的缓冲区
len:发送数据的长度
flags:标志,一般为零
6).closesocket函数关闭套接字
closesocket()关闭套接字
下面是一个完整的服务器端的代码
1 #include 2 #include 3 #include 4 #pragma comment (lib,"ws2_32.lib") 5 int main() 6 { 7 socket serversocket;//监视的套接字 8 socket newsocket;//用来通信的套接字 9 sockaddr_in newaddr;//保存客户端的socket地址信息 10 sockaddr_in addr;//地址结构体,包括ip port(端口) 11 12 wsadata data; 13 word version;//socket版本 14 int info; 15 char buf[32];//数据缓冲区 16 /* 17 在使用socket之前要进行版本的设定和初始化 18 看不懂不用管 19 */ 20 version = makeword(2, 2);//设定版本 21 info = wsastartup(version, &data); 22 /*应用程序或dll只能在一次成功的wsastartup()调用之后 23 才能调用进一步的windows sockets api函数。 24 根据版本初始化 windows socket,返回0表示成功 25 */ 26 27 if (info != 0) 28 { 29 printf("初始化失败n"); 30 return -1; 31 } 32 if (lobyte(data.wversion) != 2 || hibyte(data.wversion) != 2) 33 { 34 printf("加载失败n"); 35 wsacleanup(); 36 return 0; 37 } 38 //创建套接字,使用tcp协议 39 serversocket = socket(af_inet, sock_stream, ipproto_tcp); 40 addr.sin_addr.s_un.s_addr = htonl(addr_any);//表示任何的ip过来连接都接受 41 addr.sin_family = af_inet;//使用ipv4的地址 42 addr.sin_port = htons(6666);//设定应用占用的端口 43 bind(serversocket, &addr, sizeof(sockaddr));//将套接字与端口6666,接收的ip绑定 44 listen(serversocket, 3);//开始监听,是否有客服端请求连接 45 printf("开始监听,等待连接..........n"); 46 int len = sizeof(sockaddr); 47 newsocket=accept(serversocket, (sockaddr*)&newaddr,&len); 48 sprintf(buf, "欢迎:%s 的用户连接", inet_ntoa(newaddr.sin_addr)); 49 send(newsocket, buf, 32, 0);//发送信息 50 printf("连接成功,开始发送信息..........n"); 51 recv(newsocket, buf, 32, 0);//接收信息 52 printf("接收到的信息为:%sn", buf); 53 closesocket(newsocket);//关闭套接字 54 }
运行结果
客户端例子
客户端与服务器端不同,服务器端是等待连接的,而客户端是主动连接的,所以客户端没有listen函数监听,也没有accept函数等待连接。
客户端有一个connect函数用于主动连接服务器端。其余差不多
1 int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen);
sock:套接字
serv_addr:sockaddr结构体
addrlen:serv_addr长度,可以用sizeof得到
客户端代码
1 #include 2 #include 3 #include 4 #pragma comment(lib,"ws2_32.lib") 5 6 int main() 7 { 8 socket clientsocket; 9 sockaddr_in addr; 10 int len; 11 char buf[32]; 12 int info; 13 wsadata data; 14 word version; 15 //设定版本,与初始化 16 version = makeword(2, 2); 17 info = wsastartup(version, &data); 18 if (info != 0) 19 { 20 printf("初始化失败n"); 21 return -1; 22 } 23 if (lobyte(data.wversion) != 2 || hibyte(data.wversion) != 2) 24 { 25 printf("加载失败n"); 26 wsacleanup(); 27 return 0; 28 } 29 30 clientsocket = socket(af_inet, sock_stream, 0);//创建套接字 31 //要连接的服务器的ip,因为现在服务器端就是本机,所以写本机ip 32 //127.0.0.1一个特殊的ip地址,表示是本机的ip地址 33 addr.sin_addr.s_un.s_addr = inet_addr("127.0.0.1"); 34 //端口要与服务器相同,不然找不到 35 addr.sin_port = htons(6666); 36 //用ipv4地址 37 addr.sin_family = af_inet; 38 //主动连接服务器 39 connect(clientsocket,(sockaddr*)&addr,sizeof(sockaddr)); 40 //接收服务发送的数据 41 recv(clientsocket, buf, 32, 0);//接收数据 42 printf("服务器发送的信息是:%sn", buf); 43 sprintf(buf, "%s","你好,服务器"); 44 //发送数据 45 send(clientsocket, buf, 32, 0); 46 //关闭套接字 47 closesocket(clientsocket); 48 return 0; 49 50 }
先启动服务器,再启动客户端。一次简单的通信就完成了
把这个简单的例子做出来,对于socket应该会有初步的认识,最起码应该学会怎么用。
下次利用socket写个简单的聊天程序,进一步加深对socket的认识。