一、日常八股
1.键入网址到页面显示,中间发生了哪些内容?
二、Socket网络编程
在VS上复习下Socket网络编程
1.大体流程
首先做服务端,与在Linux上不同,如果要在VS上使用Socket进行网络通信,需要引入网络库如下:
#include <WinSock2.h>
#pragma comment(lib, "Ws2_32.lib")
WSADATA wsaData; //初始化Winsock库
int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0) {
std::cerr << "WSAStartup failed: " << result << std::endl;
return 1;
}
初始化完成之后,就可以建立套接字了,由于在服务端,需要一个socket绑定服务端的端口,还需要一个socket来绑定客户端的端口,用来对客户端进行收发数据,所以定义两个:
int server_fd, new_socket; //声明服务器socket和新的客户端Socket
此外,还需要定义一个结构体用来存储地址信息,和地址结构体大小等信息
struct sockaddr_in address; //声明用于存储信息的结构体地址
int addrlen = sizeof(address); //地址结构体大小
const int port = 8080;
2.建立socket:
socket参数(domain
(协议域),type
(套接字类型),protocol(协议))
server_fd = socket(AF_INET,SOCK_STREAM,0);
if (server_fd == 0) {std::cerr << "socket creation err?" << std::endl;return 1;
}
此外还有setsockopt()函数
setsockopt()函数用于设置一个套接字选项。通过这个函数,可以对套接字的各种属性进行配置,如设置套接字的接收缓冲区大小、启用地址重用、设置发送超时等。
设置基本信息到结构体,然后进行绑定,是将本地的网络地址与套接字相绑定。
address.sin_addr.s_addr = INADDR_ANY;address.sin_family = AF_INET;address.sin_port = htons(port);//绑定 if (bind(server_fd,(struct sockaddr*)&address,addrlen)<0) {std::cerr << "bind err!!!" << std::endl;return 1;}
//监听
int szbuf=3;
if (listen(server_fd,szbuf)<0) {std::cerr << "listen err!!!" << std::endl;return 1;
}
服务端设置循环监听客户端的链接:由此可见 我设置两个socket的目的在于一个用来绑定本机地址,另一个socket用来接收客户端的链接,并进行网络通信。
while (true) {new_socket = accept(server_fd,(struct sockaddr*)&address,&addrlen);//阻塞的 if (new_socket < 0) {std::cerr << "accept err!!!" << std::endl;continue;}char szbuf[1024] = { 0 };SSIZE_T bytes_read = recv(new_socket,szbuf,sizeof(szbuf),0);if (bytes_read > 0) {std::cout <<"Client say:" << szbuf << std::endl;const char* message = "Hello stupid peter";send(new_socket,message,strlen(message),0);}
}
accept函数
accept函数主要用于服务器端,它从已完成连接队列(由listen函数创建的队列)中取出一个已完成的连接请求,创建一个新的套接字来与客户端进行通信,并返回这个新套接字的描述符。原有的监听套接字(用于监听客户端连接请求的套接字)继续用于监听新的连接请求。
send函数:(sockfd
(套接字描述符),buf
(数据缓冲区指针),len
(数据长度),flags
(标志位))
len表示要发送的数据长度,以字节为单位。对于字符串数据,如果只想发送字符串的一部分,可以指定该部分的长度。
3.listen()函数的第二个参数
代表全连接队列长度,即已完成队列和未完成队列的总长度,这个数字表示同一时间可以同时等待处理的客户端连接请求数量。
4.listen和TCP三次握手的关系
当服务器调用listen后,他开始等待客户端的连接请求,当客户端发起连接请求时,会进行TCP的三次握手。
三次握手完成之后,会将该链接请求移到已完成的队列。
服务器可以通过accept函数来获取已完成连接队列中的客户端的连接。
这样一个简单的DEMO就最好了,现在的功能就是客户端发一句,服务端发一句,二者直接相互通信,但是注意如果你发的字符有空格,那么send函数将会自动拆分成了两个包发送。
服务端:
#include<iostream>
#include<cstring>
#include <WinSock2.h>
#pragma comment(lib, "Ws2_32.lib")
int main() {WSADATA wsaData;int result = WSAStartup(MAKEWORD(2, 2), &wsaData);if (result != 0) {std::cerr << "WSAStartup failed: " << result << std::endl;return 1;}int server_fd, new_socket; //声明服务器socket和新的客户端Socketstruct sockaddr_in address; //声明用于存储信息的结构体地址int opt = 1; //设置选项int addrlen = sizeof(address); //地址结构体大小const int port = 8080;//建立socketserver_fd = socket(AF_INET,SOCK_STREAM,0);if (server_fd == 0) {std::cerr << "socket creation err?" << std::endl;return 1;}//设置socket选项//setsockopt(server_fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//用于socket的行为address.sin_addr.s_addr = INADDR_ANY;address.sin_family = AF_INET;address.sin_port = htons(port);//绑定 if (bind(server_fd,(struct sockaddr*)&address,addrlen)<0) {std::cerr << "bind err!!!" << std::endl;return 1;}//监听int szbuf=3;if (listen(server_fd,szbuf)<0) {std::cerr << "listen err!!!" << std::endl;return 1;}std::cout << "server is listening on port:" << port << "..." << std::endl;new_socket = accept(server_fd,(struct sockaddr*)&address,&addrlen);//阻塞的 if (new_socket < 0) {std::cerr << "accept err!!!" << std::endl;return 1;}//循环监听客户端的连接while (true) {char szbuf[1024] = { 0 };SSIZE_T bytes_read = recv(new_socket,szbuf,sizeof(szbuf),0);if (bytes_read > 0) {std::cout <<"Client say:" << szbuf << std::endl;//const char* message = "Hello stupid peter";char message[1024] = { 0 };std::cin >> message;send(new_socket,message,strlen(message),0);}}closesocket(server_fd);closesocket(new_socket);WSACleanup();return 0;
}
客户端代码:
#include<iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
int main() {WSADATA wsaData;int result = WSAStartup(MAKEWORD(2, 2), &wsaData);if (result != 0) {std::cerr << "WSAStartup failed: " << result << std::endl;return 1;}int sockClien;sockClien = socket(AF_INET,SOCK_STREAM,0);if (sockClien<0) {std::cerr << "socket creation err?" << std::endl;return 1;}struct sockaddr_in address;inet_pton(AF_INET, "127.0.0.1", &address.sin_addr);address.sin_family = AF_INET;address.sin_port = htons(8080);if (connect(sockClien, (struct sockaddr*)&address, sizeof(address))<0) {std::cerr << "connect creation err?" << std::endl;return 1;}while (true) {//std::string message = "hello gus you win";std::string message;std::cin >> message;if (send(sockClien,message.c_str(), message.size(), 0)<0) {std::cerr << "send creation err?" << std::endl;return 1;}char szbuf[1024] = { 0 };int bytes_read = recv(sockClien,szbuf,sizeof(szbuf)-1,0);if (bytes_read > 0) {std::cout << "Server say:" << szbuf << std::endl;}else if(bytes_read==0) {std::cout << "连接关闭:" << szbuf << std::endl;}}closesocket(sockClien);WSACleanup();return 0;
}
1985.找出数组第k个大的整数
给你一个字符串数组 nums 和一个整数 k 。nums 中的每个字符串都表示一个不含前导零的整数。 返回 nums 中表示第 k 大整数的字符串。 注意:重复的数字在统计时会视为不同元素考虑。例如,如果 nums 是 ["1","2","2"],那么 "2" 是最大的整数,"2" 是第二大的整数,"1" 是第三大的整数。
核心在于比较函数,很巧妙,由于是字符串数组,如果在比较中字符串长度较大的一方肯定更大,如果相等的话,那么就按照字典序比较(s1 > s2),字典序中字符编码较大的字符串更大。
最后用到STL容器中的nth_element()函数,nums.begin()和nums.end()确定了要操作的范围是整个nums向量。而nums.begin()+k-1则确定了位置,经过nth_element()操作之后,这个位置的元素将被放置到它按照cmp
规则排序后的最终位置。而cmp的规则是,在特定位置的元素要比左面的元素大,比右面的元素小。
class Solution {
public:bool static cmp(string s1,string s2){return s1.size()!=s2.size()?s1.size()>s2.size():s1>s2;}string kthLargestNumber(vector<string>& nums, int k) {nth_element(nums.begin(),nums.begin()+k-1,nums.end(),cmp);return nums[k-1];}
};