赞
踩
1.1 创建基本流程
创建一个TCP服务端的程序需要调用的函数流程:
1.2 代码实现
(1) 初始化函数库
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2),&wsaData);
说明:WSAStartup的参数1->初始化winsock库的版本号;参数2-> 指向WSADATA的指针,而这个结构体用来存储WSAStartup函数调用后返回WindowsSockets数据。在程序的开始处调用该初始化函数,在程序中就可以使用Winsock相关的所有API函数。
(2) 创建套接字
SOCKET sListen = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
说明:socket()函数参数1-> 指定协议族,在windows下使用的有两个,AF_INET,PF_INET,这两个宏在Winsock2.h的定义是相同的。一般在调用socket()函数时应该使用PF_INET,而在设置地址时使用AF_INET。
参数2 -> 指定新套接字描述符的类型,一般使用的有3个,分别是SOCK_STREAM,SOCK_DGRAM和SOCK_RAW,分别是流套接字,数据包套接字和原始协议接口。
参数3 -> 这里用来指定应用程序所使用的通信协议,可以使用的IPPROTO_TCP、IPPROTO_UDP,IPPROTO_ICMP等。此参数是根据参数2值进行选择。这里参数2使用的是SOCK_STREAM,所以参数3使用IPPROTO_TCP。
(3) 绑定套接字与地址信息
struct sockaddr_in ServerAddr;
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.S_un.S_addr = inet_addr("192.168.41.1"); //将点分十进制IP转换为长整数型数
ServerAddr.sin_port = htons(1234); //htons将整形变量从主机字节序转换为网络字节序
bind(sListen,(SOCKADDR *)&ServerAddr,sizeof(ServerAddr));
说明:socket()函数可以创建新的套接字描述符,但它只是一个描述符,为网络资源做准备。要真正在网络中通信,需要本地的地址与本地的端口号信息。 通过bind函数进行信息的绑定,而bind函数的3个参数当中最重要当属参数2。
它是sockaddr的结构体,该结构体有16个字节,在该结构体之前使用sockaddr_in,为bind函数指定地址和端口时,向sockaddr_in结构体填充相应的内容。
Struct sockaddr_in{
Short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
而这里我们要此结构体的sin_family成员,它的取值有三种:
AF_UNIX(本机通信)
AF_INET(TCP/IP - IPv4)
AF_INET6(TCP/IP – IPv6)
(4) 监听端口
listen(sListen,SOMAXCONN);
说明:参数2 -> 定义了系统中每一个端口最大的监听队列的长度
(5) 获取连接请求
sockaddr_in ClientAddr;
int nSize = sizeof(ClientAddr);
SOCKET sClient = accept(sLisent,(SOCKADDR *)&ClientAddr,&nSize);
说明:accept从请求队列中获取连接信息,创建新的套接字描述符,获取客户端地址。
参数1-> 处于监听端套接字描述符
参数2-> 指向sockaddr结构体的指针,用来返回客户端的地址信息。
参数3-> 客户端sockaddr结构体的大小
(6) 到这里服务端的操作基本完成,只需等待客户端的连接,然后就可以进行数据传输了。
1.3 完整代码
#include <stdio.h> #include <winsock2.h> #pragma comment(lib,"ws2_32") int main() { WSADATA wsaData; WSAStartup(MAKEWORD(2,2),&wsaData); //创建套接字 SOCKET sListen = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); //对sockaddr_in结构体填充地址,端口等信息 struct sockaddr_in ServerAddr; ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.S_un.S_addr = inet_addr("192.168.41.1"); //将点分十进制IP转换为长整数型数 ServerAddr.sin_port = htons(1234); //htons将整形变量从主机字节序转换为网络字节序 //绑定套接字与地址信息 bind(sListen,(SOCKADDR *)&ServerAddr,sizeof(ServerAddr)); //端口监听 listen(sListen,SOMAXCONN); //获取连接请求 sockaddr_in ClientAddr; int nSize = sizeof(ClientAddr); SOCKET sClient = accept(sListen,(SOCKADDR *)&ClientAddr,&nSize); //输出客户端使用的IP地址和端口号 printf("ClientIP=%s:%d\r\n",inet_ntoa(ClientAddr.sin_addr),ntohs(ClientAddr.sin_port)); //ntohs()是一个函数名,作用是将一个16位数由网络字节顺序转换为主机字节顺序 //发送消息 char szMsg[MAXBYTE] = {0}; lstrcpy(szMsg,"Hello Client!\r\n"); send(sClient,szMsg,strlen(szMsg)+sizeof(char),0); //strlen测长和sizeof测长 //接受消息 recv(sClient,szMsg,MAXBYTE,0); printf("Client Msg :%s \r\n",szMsg); WSACleanup(); return 0; }
2.1 基本流程
客户端和服务端调用的API基本相同:
WSAStartup() -> socket() -> connect() -> send()/recv() -> closesocket() -> WSACleanup()
2.2 代码实现
客户端只需要创建套接字然后填充sockaddr_in结构体的地址和端口等信息即可。
struct sockaddr_in ServerAddr;
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.S_un.S_addr = inet_addr("192.168.41.1");
ServerAddr.sin_port = htons(1234);
此处填充的信息为需要连接的服务端的信息。
2.3 完整代码
#include <stdio.h> #include <winsock2.h> #pragma comment (lib,"ws2_32") int main() { WSADATA wsaData; WSAStartup(MAKEWORD(2,2),&wsaData); //创建套接字 SOCKET sServer = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); //对sockaddr_in结构体填充地址、端口等信息 struct sockaddr_in ServerAddr; ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.S_un.S_addr = inet_addr("192.168.41.1"); ServerAddr.sin_port = htons(1234); //连接服务器 connect(sServer,(SOCKADDR *)&ServerAddr,sizeof(ServerAddr)); char szMsg[MAXBYTE] = {0}; //接收消息 recv(sServer,szMsg,MAXBYTE,0); printf("Server Msg: %s \r\n",szMsg); //发送消息 lstrcpy(szMsg,"Hello Server!!!\r\n"); send(sServer,szMsg,sizeof(szMsg)+sizeof(char),0); WSACleanup(); return 0; }
基于UDP协议的服务端程序不会去监听端口和等待请求连接,因此UDP协议的服务端程序会较短。
#include <stdio.h> #include <winsock2.h> #pragma comment (lib,"ws2_32") int main() { WSADATA wsaData; WSAStartup(MAKEWORD(2,2),&wsaData); //创建套接字 SOCKET sServer = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP); //对sockaddr_in结构体填充地址、端口等信息 struct sockaddr_in ServerAddr; ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.S_un.S_addr = inet_addr("192.168.41.1"); ServerAddr.sin_port = htons(1245); //htons将整形变量从主机字节序转变为网络字节序 //绑定套接字与地址信息 bind(sServer,(SOCKADDR *)&ServerAddr,sizeof(ServerAddr)); //接受消息 char szMsg[MAXBYTE] = {0}; struct sockaddr_in ClientAddr; int nSize = sizeof(ClientAddr); recvfrom(sServer,szMsg,MAXBYTE,0,(SOCKADDR *)&ClientAddr,&nSize); printf("Client Msg:%s \r\n",szMsg); printf("ClientIP = %s:%d\r\n",inet_ntoa(ClientAddr.sin_addr),ntohs(ClientAddr.sin_port)); //发送消息 lstrcpy(szMsg,"Hello Client,this is Server!\r\n"); nSize = sizeof(ClientAddr); sendto(sServer,szMsg,strlen(szMsg)+sizeof(char),0,(SOCKADDR *)&ClientAddr,nSize); WSACleanup(); return 0; }
基于UDP客户端的代码相对于TCP协议的客户端代码来讲,不需要调用connect()函数进行连接,省去了TCP协议的"三次握手"的过程,可以直接发送数据给服务器。
#include <stdio.h> #include <winsock2.h> #pragma comment(lib,"ws2_32") int main() { WSADATA wsaData; WSAStartup(MAKEWORD(2,2),&wsaData); //创建套接字 SOCKET sClient = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP); //对sockaddr_in结构体填充地址、端口等信息 struct sockaddr_in = ServerAddr; ServerAddr.sin_family = AF_INET; ServerAddr.sin_addr.S_un.S_addr = inet_addr("10.10.30.12"); ServerAddr.sin_port = htons(1234); //发送消息 char szMsg[MAXBYTE] = {0}; lstrcpy(szMsg,"Hello Server This is Client !"); int nSize = sizeof(ServerAddr); sendto(sClient,szMsg,strlen(szMsg)+sizeof(char),0,(SOCKADDR *)&ServerAddr,nSize); //接收消息 nSize = sizeof(ServerAddr); recvfrom(sClient,szMsg,strlen(szMsg)+sizeof(char),0,(SOCKADDR*)&ServerAddr,nSize); printf("Server Msg:%s \r\n",szMsg); WSACleanup(); return 0; }
学长的博客:https://blog.roachs.cn/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。