套接字:应用进程与端到端传输协议(TCP或UDP)之间的门户
TCP服务:从一个进程向另一个进程可靠地传输字节流
服务器首先运行,等待连接建立
1:服务器进程必须先处于运行状态
- 创建欢迎socket
- 和本地端口捆绑
- 在欢迎socket上阻塞式等待接收用户的连接
客户端主动和服务器建立连接:
2:创建客户端本地套接字(隐式捆绑到本地port)
- 指定服务器进程的IP地址和端口号,与服务器进程连接
3 :当与客户端连接请求到来时
-
服务器接受来自用户端的请求,解除阻塞式等待,返回一个新的socket(与欢迎socket不一样),与客户端通信
允许服务器与多个客户端通信 使用源IP和源端口来区分不同的客户端
4:连接API调用有效时,客户端P与服务器建立了TCP连接
TCP在客户端和服务器进程之间提供了可靠的、字节流(管道)服务
TCP Socket编程:
Server:
#include<iostream>
#include<Windows.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
//void ErrorSocket{};
int main() {
cout << "开始" << endl;
WSADATA wsaData{};
SOCKET SerSock{}, CliSock{};
sockaddr_in SerAddr{}, CliAddr{};
//初始化网络库
WSAStartup(MAKEWORD(2, 1), &wsaData);
//创建socket
SerSock = socket(PF_INET, SOCK_STREAM, 0);
//服务端的信息,IP地址,端口
SerAddr.sin_family = AF_INET;
//SerAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //绑定地址
SerAddr.sin_addr.s_addr = htonl(INADDR_ANY); //宏就是定义0.0.0.0 监听所有IP
SerAddr.sin_port = htons(6969); //转成端口
//给服务端Socket绑定 地址信息结构体
//绑定地址
bind(SerSock, (SOCKADDR*)&SerAddr, sizeof(SerAddr);
//监听 等待连接请求 等待队列
//第一个参数为服务端的套接字 第二个等待队列的长度 5 (表示最多可以连接五个请求)
if (listen(SerSock, 5) == -1) {
cout << "错误" << WSAGetLastError() << endl;
closesocket(SerSock);
WSACleanup(); //关闭
return -1;
}
int len = sizeof(CliAddr);
//客户端地址信息 绑定一个新的套接字 连接后welcomeSocket会变成新的connectSocket
//接受连接 如果没连接上就返回-1 accept可能会堵塞
if ((CliSock = accept(SerSock, (SOCKADDR*)&CliAddr, &len)) == -1) {
cout << "错误" << WSAGetLastError() << endl;
closesocket(SerSock);
WSACleanup(); //关闭
return -1;
}
// Recv 接收它收到的数据 I/O操作
/*
第一个参数为 客户端套接字
第二个参数为 要发送的缓冲区(要接收的信息)
第三个参数为 缓冲区的大小(你要接收的信息的长度)
第四个参数为 标志 一般都为0
*/
char RecvBuf[BUFSIZ]{};
int ret=recv(CliSock,RecvBuf,sizeof(RecvBuf),0);
if (ret <= 0) {
cout << "错误" << WSAGetLastError() << endl;
closesocket(SerSock);
WSACleanup(); //关闭
return -1;
}
cout << "RecvBuf=" << " " << RecvBuf << endl;
// Send 发送网络数据 I/O操作
/*
第一个参数为 客户端套接字
第二个参数为 要发送的缓冲区(要发送的信息)
第三个参数为 缓冲区的大小(你要发送的信息的长度)
第四个参数为 标志
*/
char SendBuf[]{ "hello Client!" };
ret = send(CliSock, SendBuf, sizeof(SendBuf), 0);
if (ret <= 0) {
cout << "错误=" << WSAGetLastError() << endl;
closesocket(SerSock);
WSACleanup(); //关闭
return -1;
}
cout << "结束" << endl;
closesocket(SerSock);
closesocket(CliSock);
WSACleanup();
return 0;
}
Client:
#include<iostream>
#include<Windows.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
//void ErrorSocket{};
int main() {
cout << "开始" << endl;
WSADATA wsaData{};
SOCKET sock{};
sockaddr_in SerAddr{};
//初始化网络库
if (WSAStartup(MAKEWORD(2, 1), &wsaData) == -1) {
cout << "错误" << WSAGetLastError() << endl;
return -1;
}
//创建socket
if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { //正常有值
cout << "错误" << WSAGetLastError() << endl;
return -1;
}
SerAddr.sin_family = PF_INET;
SerAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //指定客户端地址?
SerAddr.sin_port = htons(6969);
//与服务端进行连接
connect(sock, (SOCKADDR*)&SerAddr, sizeof(SerAddr));
char SendBuf[]{ "Client 你好" };
send(sock, SendBuf, sizeof(SendBuf), 0); //发送
closesocket(sock);
WSACleanup();
return 0;
}
UDP Socket编程:
UDP Socket编程
UDP: 在客户端和服务器之间没有连接
- 发送端在每一个报文中明确地指定目标的IP地址和端口号
- 服务器必须从收到的分组中提取出发送端的IP地址和端口号
UDP: 传送的数据可能乱序,也可能丢失
UDP 为客户端和服务器提供不可靠的字节组的传送服务
Server:
#include<iostream>
#include<Windows.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main() {
WSADATA wsaData{};
SOCKET sock{};
sockaddr_in SerAddr{}, CliAddr{};
if (WSAStartup(MAKEWORD(2, 1), &wsaData) == -1) {
cout << "错误3: " << WSAGetLastError() << endl;
return -1;
}
sock = socket(AF_INET, SOCK_DGRAM,0); //UDP不使用SOCK_STREAM 而是使用SOCK_DGRAM
if (sock == -1) {
cout << "有错误4: " << WSAGetLastError() << endl;
closesocket(sock);
WSACleanup();
return -1;
}
//服务端名字,IP地址,端口
SerAddr.sin_family = PF_INET;
SerAddr.sin_addr.s_addr = inet_addr("0.0.0.0");//本机网络
SerAddr.sin_port = htons(6969);
//需要绑定地址 bind UDP的绑定与TCP不同
if (bind(sock, (SOCKADDR*)&SerAddr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
cout << "有错误5: " << WSAGetLastError() << endl;
closesocket(sock);
WSACleanup();
return -1;
}
char RecvBuf[1024]{};
int len = sizeof(CliAddr); //客户端传输的信息的长度
recvfrom(sock, RecvBuf, sizeof(RecvBuf), 0, (SOCKADDR*)&CliAddr, &len); //接收什么的数据
cout << "Client Message" <<RecvBuf<< endl;
//传输回去的信息
char SendBuf[1024]{ "Hello Client" };
sendto(sock, SendBuf, sizeof(SendBuf), 0, (SOCKADDR*)&CliAddr, len); //发送到什么位置
cout << "End" << endl;
//需要关闭套接字
closesocket(sock);
WSACleanup();
return 0;
}
Client:
#include<iostream>
#include<windows.h>
#pragma comment (lib,"ws2_32.lib")
using namespace std;
int main() {
WSADATA wsaData{ };
SOCKET sock{};
sockaddr_in SerAddr{};
if (WSAStartup(MAKEWORD(2, 1), &wsaData) == -1) {
cout << "有错误1: " << WSAGetLastError() << endl;
WSACleanup();
return -1;
}
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
cout << "有错误2: " << WSAGetLastError() << endl;
WSACleanup();
return -1;
}
//客户端名字,IP地址,端口
SerAddr.sin_family = PF_INET;
SerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
SerAddr.sin_port = htons(6969);
//连接 检测服务端是否在线
connect(sock, (SOCKADDR*)&SerAddr, sizeof(SerAddr));
char SendBuf[]{"Hello Server!-------------------"};
send(sock, SendBuf, sizeof(SendBuf), 0); //向服务端传输信息
char RecvBuf[1024]{};
recv(sock, RecvBuf, sizeof(RecvBuf), 0); //接收服务端传输给客户端的信息
cout << "Server Message: " << RecvBuf << endl;
cout << "结束" << endl;
closesocket(sock);
WSACleanup();
getchar();
}