1 Winsock网络编程
1.1 TCP/IP网络的概念
应用层----我们的程序、数据
传输层----TCP/UDP 端口号 运沙子UDP 运机器TCP
网络层----IP 计算机---计算机 可以通信了
物理层----网卡 电压
1.2 TCP服务器端编程步骤
1 准备工作
确定使用Winsock的什么版本-----1.1
2 加载套接字
实现使用1.1版本-----同时还会,校验本机是否支持1.1版本
3 建立 侦听套接字(插座)-----在墙上装了1个电话插座
4 绑定-----就是宣布自己的 IP和端口号、自己的套接字
5 侦听----等待客户的连接请求
6 Accept响应客户的连接请求-------通信链路 通了;而且会返回1个 连接套接字
7 send/receive----都是通过 连接套接字
8 关闭 连接套接字
9 关闭 侦听套接字
10 卸载套接字
1.3 TCP服务器端编程步骤---翻译为C语言
1 准备工作
确定使用Winsock的什么版本-----1.1
增加头文件
#include
增加lib库
ws2_32.lib
2 加载套接字
实现使用1.1版本-----同时还会,校验本机是否支持1.1版本
int WSAStartup (
WORD wVersionRequested, //申请的版本号,如1.1,但要用2个字节的整型数来表示,所以填写257。 ---输入参数
LPWSADATA lpWSAData //这是地址类型,指向了1个结构体空间。该函数执行后,会修改该结构体数据。 ---输出参数
);
代码:
WSADATA wsadata;
LPWSADATA lpWSAData=&wsadata;
WSAStartup(257,lpWSAData);
3 建立 侦听套接字(插座)-----在墙上装了1个电话插座
SOCKET socket_listen=socket(AF_INET,SOCK_STREAM,0);
4 绑定-----就是宣布自己的 IP和端口号、自己的套接字
struct sockaddr_in addr_b;
addr_b.sin_addr.S_un.S_addr=inet_addr("172.18.23.200");
addr_b.sin_port=6588;
addr_b.sin_family=AF_INET;
struct sockaddr * addr=(struct sockaddr *)&addr_b;
bind(socket_listen,addr,sizeof(SOCKADDR));
5 侦听----等待客户的连接请求
listen(socket_listen,4);
注意:4代表在线用户不能超过4个。
6 Accept响应客户的连接请求-------通信链路 通了;而且会返回1个 连接套接字
sockaddr_in addr_bb;
sockaddr * addr_client=(sockaddr *)&addr_bb;
int len=sizeof(sockaddr);
SOCKET socket_connect=accept(socket_listen,addr_client,&len);
注意:
是阻塞函数,如果没有收到 任何客户的连接请求,程序会阻塞在这里。
阻塞不是绝对的,有前提----该阻塞的时候就阻塞,不该阻塞就不阻塞(如套接字库加载失败了,就不阻塞了;IP地址如果设错了,也不阻塞;网线断了,也不阻塞)。
如果,IP设置为127.0.0.1时,网线通时阻塞,不通时也阻塞。但为了使我们的服务器程序具有通用性,应该设置为127.0.0.1.
但不能设置为127.0.0.1,因为如果这样设置,别的的PC永远也连不到服务器了。所以,它只是用来进行本机测试用的。
所以,应该这样设置:
addr_b.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
但也有问题,即网线通或不通,都会阻塞。
7 send/receive----都是通过 连接套接字
char buf_recv[1000]={'\0'};
recv(socket_connect,buf_recv,sizeof(buf_recv)-1,0);
//strlen(buf_recv);
buf_recv[999]='\0';
printf("%s\n",buf_recv);
注意:
recv是阻塞函数-----网络通,而且收不大任何数据时阻塞
所以不阻塞时有2种情况:
1 收到数据了,就不阻塞了,函数将运行结束,而且有返回值。
2 如果收到数据了,返回值是数据的字节数。
3 但此时,如果客户端的网线断了,服务器是无法知道的(因为服务器的网口是通的),一直阻塞下去
4 如果客户端执行了关闭套接字,那么服务器端的recv会返回0(但不表示收到了0字节,而表示侦听套接字链路收到了客户端的关闭套接字请求)),此时,就又不阻塞了。
5 如果recv返回-1,则服务器端,肯定收到了客户端的关闭套接字(请求)---通过侦听套接字的链路.
6 如果客户端没有关闭套接字,而客户端的网线断了,则服务器是不知道的,所以recv会一直阻塞。
7 如果客户端没有关闭套接字,但客户端程序退出了,则服务器的recv会返回-1(相当于收到了客户端的关闭套接字请求).
解决办法:增加心跳检测代码
8 关闭 连接套接字
closesocket(socket_connect);
9 关闭 侦听套接字
closesocket(socket_listen);
10 卸载套接字库
WSACleanup();
代码:
//服务器端 #include#include <string.h> #include int main() { WSADATA wsadata; LPWSADATA lpWSAData=&wsadata; WSAStartup(257,lpWSAData); SOCKET socket_listen=socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in addr_b; addr_b.sin_addr.S_un.S_addr=inet_addr("172.18.23.200"); addr_b.sin_port=6588; addr_b.sin_family=AF_INET; struct sockaddr * addr=(struct sockaddr *)&addr_b; bind(socket_listen,addr,sizeof(SOCKADDR)); listen(socket_listen,4); sockaddr_in addr_bb; sockaddr * addr_client=(sockaddr *)&addr_bb; int len=sizeof(sockaddr); SOCKET socket_connect=accept(socket_listen,addr_client,&len); char buf_recv[1000]={'\0'}; recv(socket_connect,buf_recv,sizeof(buf_recv)-1,0); //strlen(buf_recv); buf_recv[999]='\0'; printf("%s\n",buf_recv); closesocket(socket_connect); closesocket(socket_listen); WSACleanup(); return 0; }
1.4 TCP客户端编程步骤
1 准备工作
头文件、lib文件
2 加载套接字库
一样
WSADATA wsadata;
LPWSADATA lpWSAData=&wsadata;
WSAStartup(257,lpWSAData);
3 建立 客户端套接字
一样
SOCKET socket_client=socket(AF_INET,SOCK_STREAM,0);
4 发生连接请求给服务器
connect()
不一样
struct sockaddr_in addr_b;
addr_b.sin_addr.S_un.S_addr=inet_addr("172.18.23.200");
addr_b.sin_port=6588;
addr_b.sin_family=AF_INET;
struct sockaddr * addr=(struct sockaddr *)&addr_b;
connect(socket_client,addr,sizeof(sockaddr));
5 发生数据
send()
一样
char buf_send[]="hello,我是客户端!";
send(socket_client,buf_send,strlen(buf_send)+1,0);
6 关闭套接字
一样
closesocket(socket_client);
7 卸载套接字库
一样
WSACleanup();
1.5 代码示例
//客户端:
#include#include <string.h> #include int main() { WSADATA wsadata; LPWSADATA lpWSAData=&wsadata; WSAStartup(257,lpWSAData); SOCKET socket_client=socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in addr_b; addr_b.sin_addr.S_un.S_addr=inet_addr("172.18.23.200"); addr_b.sin_port=6588; addr_b.sin_family=AF_INET; struct sockaddr * addr=(struct sockaddr *)&addr_b; connect(socket_client,addr,sizeof(sockaddr)); char buf_send[]="hello,我是客户端!"; send(socket_client,buf_send,strlen(buf_send)+1,0); char buf_recv[1000]={'\0'}; recv(socket_client,buf_recv,sizeof(buf_recv)-1,0); printf("%s\n",buf_recv); closesocket(socket_client); WSACleanup(); return 0; }
服务器端:
#include#include <string.h> #include int main() { WSADATA wsadata; LPWSADATA lpWSAData=&wsadata; WSAStartup(257,lpWSAData); SOCKET socket_listen=socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in addr_b; addr_b.sin_addr.S_un.S_addr=inet_addr("172.18.23.200"); addr_b.sin_port=6588; addr_b.sin_family=AF_INET; struct sockaddr * addr=(struct sockaddr *)&addr_b; bind(socket_listen,addr,sizeof(SOCKADDR)); listen(socket_listen,4); sockaddr_in addr_bb; sockaddr * addr_client=(sockaddr *)&addr_bb; int len=sizeof(sockaddr); SOCKET socket_connect=accept(socket_listen,addr_client,&len); char buf_recv[1000]={'\0'}; recv(socket_connect,buf_recv,sizeof(buf_recv)-1,0); //strlen(buf_recv); //buf_recv[999]='\0'; printf("%s\n",buf_recv); char buf_send[]="我是服务器!"; send(socket_connect,buf_send,strlen(buf_send)+1,0); closesocket(socket_connect); closesocket(socket_listen); WSACleanup(); //system("pause"); return 0; }
1.6 改进后的代码
服务器端:
#include#include <string.h> #include int main() { WSADATA wsadata; LPWSADATA lpWSAData=&wsadata; WSAStartup(257,lpWSAData); SOCKET socket_listen=socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in addr_b; //addr_b.sin_addr.S_un.S_addr=inet_addr("127.0.0.1"); addr_b.sin_addr.S_un.S_addr=htonl(INADDR_ANY); addr_b.sin_port=htons(6588); addr_b.sin_family=AF_INET; struct sockaddr * addr=(struct sockaddr *)&addr_b; bind(socket_listen,addr,sizeof(SOCKADDR)); int flag=listen(socket_listen,4); sockaddr_in addr_bb; sockaddr * addr_client=(sockaddr *)&addr_bb; int len=sizeof(sockaddr); SOCKET socket_connect=4576; socket_connect=accept(socket_listen,addr_client,&len); char buf_recv[1000]={'\0'}; int count=recv(socket_connect,buf_recv,sizeof(buf_recv)-1,0); //strlen(buf_recv); //buf_recv[999]='\0'; printf("%s\n",buf_recv); char buf_send[]="我是服务器!"; send(socket_connect,buf_send,strlen(buf_send)+1,0); closesocket(socket_connect); closesocket(socket_listen); WSACleanup(); //system("pause"); return 0; }
客户端:
#include#include <string.h> #include int main() { WSADATA wsadata; LPWSADATA lpWSAData=&wsadata; WSAStartup(257,lpWSAData); SOCKET socket_client=socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in addr_b; addr_b.sin_addr.S_un.S_addr=inet_addr("172.18.23.200"); addr_b.sin_port=htons(6588); addr_b.sin_family=AF_INET; struct sockaddr * addr=(struct sockaddr *)&addr_b; connect(socket_client,addr,sizeof(sockaddr)); char buf_send[]="hello,我是客户端!"; send(socket_client,buf_send,strlen(buf_send)+1,0); char buf_recv[1000]={'\0'}; recv(socket_client,buf_recv,sizeof(buf_recv)-1,0); printf("%s\n",buf_recv); closesocket(socket_client); WSACleanup(); //system("pause"); return 0; }