基于TCP的字符串传输程序
---恢复内容开始---
LINUX中的网络编程是通过SOCKET接口来进行的。
Socket(套接字)
Socket相当于进行网络通信两端的插座,只要对方的Socket和自己的Socket有通信联接,双方就可以发送和接收数据了。Socket的定义类似于文件句柄的定义。下面的流程图大概描述了基于TCP协议的网络编程过程。同学们只需要对大概流程有一个初步认识即可,暂时不必深究每个函数的意义。因为在后面的实训子任务中,每一个函数的具体内容和使用方法都会讲到。
TCP协议socket流程图
基本套接字调用顺序如下:
创建套接字--socket()-->绑定本机端口--bind();-->建立连接--connect(),accept();-->侦听端口--listen();-->数据传输--send(),recv();-->输入/输出多路复用--select();-->关闭套接字--closesocket();
其中,服务器端基于TCP的编程步骤大致如下:
① 创建套接字;
建立服务器端的socket套接字:int socket(int domain,int type,int protocol)
②绑定套接字到一个IP地址和一个端口上
int bind(int sockfd,struct sockaddr * myaddr,int addrlen);
③将套接字设置为监听模式,以等待连接请求
int listen(int sockfd,int backlog)
④请求到来后,接受连接请求,并返回一个与此次连接对应的套接字
int accept(int sockfd,struct sockaddr * addr,int * addrlen)
此时新建连接,并创建新的Socket套接字,此时addr为客户端的addr信息。
⑤用返回的套接字和客户端进行通信
使用recv()和send()
⑥关闭服务器端的套接字描述符
int close(int sockfd);
客户端调用顺序如下:
①创建客户端套接字
int socket(int domain,int type,int protocol)
②向服务器发出连接请求
int connect(int sockfd,struct sockaddr * servaddr,int addrlen)
其中参数servaddr指定远程服务器的套接字地址,包括服务器的IP地址和端口号
③和服务器端进行网络通信
使用recv()和send()
int read(int fd,char * buf,int len)
int write(int fd,char * buf,int len)
④关闭套接字
int close(int sockfd);
1、网络字节序和本地字节序相互转换
任务要求:
- 打印本地字节序
- 用htonl函数把本地字节序转换成网络字节序,并打印
相关知识:
小端法(Little-Endian)就是低位字节排放在内存的低地址端即该值的起始地址,高位字节排放在内存的高地址端。
大端法(Big-Endian)就是高位字节排放在内存的低地址端即该值的起始地址,低位字节排放在内存的高地址端。
举个简单的例子,对于整形0x12345678。它在大端法和小端法的系统内中,分别如图1所示的方式存放。
- 网络字节序
我们知道网络上的数据流是字节流,对于一个多字节数值,在进行网络传输的时候,先传递哪个字节?也就是说,当接收端收到第一个字节的时候,它是将这个字节作为高位还是低位来处理呢?
网络字节序定义:收到的第一个字节被当作高位看待,这就要求发送端发送的第一个字节应当是高位。而在发送端发送数据时,发送的第一个字节是该数字在内存中起始地址对应的字节。可见多字节数值在发送前,在内存中数值应该以大端法存放。
uint32_t htonl(uint32_t hostlong):htonl 表示 host to network long ,用于将主机 unsigned int 型数据转换成网络字节顺序;
uint16_t htons(uint16_t hostshort):htons 表示 host to network short ,用于将主机 unsigned short 型数据转换成网络字节顺序;
ntohl、ntohs 的功能分别与 htonl、htons 相反。
main.c:
#include<stdio.h>
#include<arpa/inet.h> //网络字节序和本地字节序相互转换
int main(void)
{
int num=0x12345678,i;
printf("before convert :%x\n",num);
for(i=;i<;i++)
printf("[%d]:0x%x\n",i,*((char *)&num+i));//将num转化成char型指针,+i表示向后偏移8位,16进制是4个4位,那么8位对应了2个字符。
num=htonl(num);
printf("after convert :%x\n",num);
for(i=;i<;i++)
printf("[%d]:0x%x\n",i,*((char *)&num+i));
return ;
}
2、利用socket函数创建一个网络套接字
任务描述:
- c语言编写服务端程序,利用socket函数创建一个套接字(TCP协议)
- 返回函数套接字状态,若成功,打印创建成功信息,失败,返回失败原因
相关知识:
- socket相当于进行网络通信两端的插座,只要对方的Socket和自己的Socket有通信联接,双方就可以发送和接收数据了。Socket的定义类似于文件句柄的定义。
int socket(int family, int type, int protocol );
为了在网络上进行I/O通信,第一件事就是要调用socket函数,创建套接字,并指定相应的通信协议类型。第一个参数family一般设置为AF_INET,对于IPv6网络,则需设置成AF_INET6。对于Unix域协议套接字,该参数的值是AF_LOCAL,对于路由套接字,该参数的值是AF_ROUTE,对于秘钥套接字,该参数的值是AF_KEY。第二个参数type制定了套接字接口的类型,其值分别为SOCK_STREAM或SOCK_DGRAM。这两个参数分别表示该套接字是TCP协议类型或者是UDP协议类型。该参数还有两个选项,分别为SOCK_SEQPACKET和SOCK_RAW,前一个表示有序分组的套接字,后一个表示原始套接字。第三个参数一般设置为0。
socket函数在成功时返回一个小的非负整数值,它与文件描述符类似,我们把它们称之为套接字描述符,简称sockfd。为了得到这个套接字描述符,我们只是制定了协议族(IPv4、IPv6或Unix)和套接字类型(字节流、数据报或原始套接字)。我们并没有指定本地协议地址或者远程协议地址。
main.c:
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h> int main(void)
{
int sockfd; sockfd=socket(AF_INET,SOCK_STREAM,);
if(sockfd==-){
perror("create socket fail\n");
exit();
}
else
printf("create success\n");
close(sockfd); return ;
}
3、利用bind函数将创建好的套接字绑定到本地计算机的某一端口上
任务描述:
- 调用bind函数,并将创建好的套接字绑定到指定端口上,如2500端口
相关知识:
对于服务器端来说,一旦通过socket函数创建了套接字以后,下一步就需要把该套接字绑定到本地计算机的某一个端口上。bind函数的作用就是完成该功能。
int bind(int sockfd, const struct sockaddr* myaddr, socklen_t addrlen);
第一个参数sockfd是由socket()调用返回的套接口文件描述符。第二个参数myaddr是指向数据结构sockaddr的指针。数据结构sockaddr中包括了关于服务器的地址、端口和IP地址的信息。第三个参数addrlen表示sockaddr结构体的长度,可以设置成sizeof(struct sockaddr)。bind函数可以指定一个端口号,或者指定一个IP地址,也可以两者都指定或者都不指定。如果指定端口号为0,那么内核就在bind被调用的时候选择一个临时接口。如果让内核来为套接字选择一个临时端口号,那么必须注意,函数bind并不返回选择的端口号值。因为bind函数的第二个参数是const类型的,它无法返回内核所分配的端口号。为了得到内核默认分配的端口号,必须调用函数getsockname函数来获取。值得一提的是,当不指定本地IP地址时,系统会为该socket分配一个默认IP地址。分配方式:myaddr.sin_addr.s_addr = htonl(INADDR_ANY);其中INADDR_ANY表示IPv4的通配地址。这个值一般为0,在这种选择下,内核将等到套接字已经连接(TCP)或者在套接字上发出数据报(UDP)时,才会选择一个本地IP地址。当server有多个网卡时,可以指定绑定到特定的网卡IP上,分配方式:myaddr.sin_addr.s_addr=inet_addr("192.168.0.123");绑定到服务器特定端口的指定方式为:myaddr.sin_port=htons(12345);
main.c:
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<string.h> //利用bind函数将创建好的套接字绑定到本地计算机的某一端口上 int main(void)
{
int sockfd,bind_fd;
struct sockaddr_in servaddr; sockfd=socket(AF_INET,SOCK_STREAM,);
if(sockfd==-){
perror("create socket fail\n");
exit();
}
else
printf("create success\n"); //初始化
bzero(&servaddr,sizeof(servaddr));//结构体初始化
servaddr.sin_family=AF_INET;//确定ip协议
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);//接收任意ip
servaddr.sin_port=htons();//设定端口号2500 //绑定端口
bind_fd=bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
if(bind_fd==-){
perror("bind fail\n");
exit();
}
else
printf("bind success\n"); close(sockfd);
return ;
}
4、利用listen函数将已经绑定的套接字设置为被动连接监听状态
任务描述:
- 在task3中创建好并绑定到本地特定端口的套接字上,增加listen函数,使得套接字处于监听状态
- 打印监听函数调用成功与否
相关知识:
listen函数使用主动连接套接口变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程。在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接。
int listen(int sockfd, int backlog)
参数sockfd被listen函数作用的套接字,sockfd之前由socket函数返回。在被socket函数返回的套接字fd之时,它是一个主动连接的套接字,也就是此时系统假设用户会对这个套接字调用connect函数,期待它主动与其它进程连接,然后在服务器编程中,用户希望这个套接字可以接受外来的连接请求,也就是被动等待用户来连接。由于系统默认时认为一个套接字是主动连接的,所以需要通过某种方式来告诉系统,用户进程通过系统调用listen来完成这件事。
参数backlog这个参数涉及到一些网络的细节。在进程正理一个一个连接请求的时候,可能还存在其它的连接请求。因为TCP连接是一个过程,所以可能存在一种半连接的状态,有时由于同时尝试连接的用户过多,使得服务器进程无法快速地完成连接请求。如果这个情况出现了,服务器进程希望内核如何处理呢?内核会在自己的进程空间里维护一个队列以跟踪这些完成的连接但服务器进程还没有接手处理或正在进行的连接,这样的一个队列内核不可能让其任意大,所以必须有一个大小的上限。这个backlog告诉内核使用这个数值作为上限。毫无疑问,服务器进程不能随便指定一个数值,内核有一个许可的范围。这个范围是实现相关的。很难有某种统一,一般这个值会小30以内。
当调用listen之后,服务器进程就可以调用accept来接受一个外来的请求。
main.c:
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<string.h> //利用listen函数将已经绑定的套接字设置为被动连接监听状态 int main(void)
{
int sockfd,bind_fd,listen_fd;
struct sockaddr_in servaddr; sockfd=socket(AF_INET,SOCK_STREAM,);
if(sockfd==-){
perror("create socket fail\n");
exit();
}
else
printf("create success\n"); //初始化
bzero(&servaddr,sizeof(servaddr));//结构体初始化
servaddr.sin_family=AF_INET;//确定ip协议
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);//接收任意ip
servaddr.sin_port=htons();//设定端口号2500 //绑定端口
bind_fd=bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
if(bind_fd==-){
perror("bind fail\n");
exit();
}
else
printf("bind success\n"); //将已绑定的套接口设为被动连接监听
listen_fd=listen(sockfd,);
if(listen_fd==-){
perror("listen fail\n");
exit();
}
else
printf("listen success\n");
close(sockfd); return ;
}
5、利用accept函数来处理客户端请求的连接
任务描述:
- 在task4的程序中,添加对客户端连接的处理,调用accept函数,为客户端连接分配一个新的socket,并打印客户端IP地址和端口号
相关知识:
- 对于服务器编程中最重要的一步等待并接受客户的连接,那么这一步在编程中如何完成,accept函数就是完成这一步的。它从内核中取出已经建立的客户连接,然后把这个已经建立的连接返回给用户程序,此时用户程序就可以与自己的客户进行点到点的通信了。
- int accept(int sockfd, struct sockaddr* addr, socklen_t* len)
accept默认会阻塞进程,直到有一个客户连接建立后返回,它返回的是一个新可用的套接字,这个套接字是连接套接字。此时我们需要区分两种套接字,一种套接字正如accept的参数sockfd,它是监听套接字,在调用listen函数之后,一个套接字会从主动连接的套接字变身为一个监听套接字;而accept返回是一个连接套接字,它代表着一个网络已经存在的点点连接。自然要问的是:为什么要有两种套接字?原因很简单,如果使用一个描述字的话,那么它的功能太多,使得使用很不直观,同时在内核确实产生了一个这样的新的描述字。
参数sockfd就是上面解释中的监听套接字,这个套接字用来监听一个端口,当有一个客户与服务器连接时,它使用这个一个端口号,而此时这个端口号正与这个套接字关联。当然客户不知道套接字这些细节,它只知道一个地址和一个端口号。
参数addr是一个结果参数,它用来接受一个返回值,这返回值指定客户端的地址,当然这个地址是通过某个地址结构来描述的,用户应该知道这一个什么样的地址结构。如果对客户的地址不感兴趣,那么可以把这个值设置为NULL。
参数len也是结果的参数,用来接受上述addr的结构的大小的,它指明addr结构所占有的字节个数。同样的,它也可以被设置为NULL。
main.c:
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
#define MYPORT 3490 //利用accept函数来处理客户端请求的连接 int main(void)
{
int sockfd,newfd,i,n;
char buf[];
socklen_t len;
struct sockaddr_in servaddr;
struct sockaddr_in clientaddr; sockfd=socket(AF_INET,SOCK_STREAM,);
if(sockfd==-){
perror("socket error\n");
exit();
}
else
printf("create socket successful\n"); servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(MYPORT);
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
bzero(&(servaddr.sin_zero),); n=bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
if(n==-){
perror("bind error\n");
exit();
}
else
printf("bind successful\n"); i=listen(sockfd,);
if(i==-){
perror("listen error\n");
exit();
}
else
printf("listen successful\n"); while(){
len=sizeof(clientaddr);
newfd=accept(sockfd,(struct sockaddr*)&clientaddr,&len);
if(newfd==-){
perror("accept error\n");
exit();
}
else
printf("accept successful\n"); if(inet_ntop(AF_INET,&clientaddr.sin_addr,buf,sizeof(buf))==NULL){//转化为本地可识别ip
perror("inet_ntop error\n");
exit();
}
printf("conneting from %s ,",buf);
printf("port is %d\n",ntohs(clientaddr.sin_port));
close(newfd);
}
close(sockfd); return ;
}
6、利用connect函数创建客户端程序,连接前文编好的服务器程序
任务描述:
- 创建一个客户端,利用命令行模式输入IP地址,客户端程序调用connect函数与服务端取得连接。
- argv[1]参数为目的IP地址,要求必须对连接不可达的各种状态进行解析,并打印提示信息
- 调用格式:./client IP地址。
eg:./client 10.42.0.250(不可达IP)。提示:can not connect to 10.42.0.250,exit!
No route to host
./client 10.42.0.123(一个没开启server端服务的IP地址)。提示:can not connect to ×××,exit!
Connection refused
调用成功:./client 10.42.0.1.提示:connect to 10.42.0.1 successfully
相关知识:
与Server端程序不同的是,在Client端,创建了一个新的套接字之后,不需要调用bind函数进行与本地的绑定,只需要预先初始化好服务器端连接信息,也就是sockaddr_in server_addr即可。该结构体中必须包含网络协议的网络类型(对于IPv4,就是AF_INET),目的服务器的IP地址,目的服务器的端口号。
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen):参数sockfd 的套接字连至参数serv_addr 指定的网络地址
sockfd 是系统调用 socket() 返回的套接口文件描述符 ;serv_addr 是保存着目的地端口和 IP 地址的数据结构 struct sockaddr ;addrlen 设置为 sizeof(struct sockaddr) ;connect 激发 TCP的三路握手过程
这通过调用socket,bind和1isten函数来完成,称为被动打开(Passive open)
- 三次握手
客户通过调用connect进行主动打开(active opn)。 这引起客户TCP发送一个SYN分节(表示同步),它告诉服务器客户将在(待建立的)连接中发送的数据的初始序列号。服务器必须确认客户的SYN,同时自己也得发送一个SYN分节,它含有服务器将在同一连接中发送的数据的韧始序列号。服务器以单个分节向客户发送SYN和对客户 SYN的ACK。客户必须确认服务器的SYN。
main.c:
#include<stdio.h>
#include <netinet/in.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<errno.h>
#include<string.h>
#define SERVER_PORT 3490 //利用connect函数创建客户端程序,连接前文编好的服务器程序 int main(int argc,char *argv[])
{
int sockfd,i,n;
socklen_t len;
struct sockaddr_in servaddr; sockfd=socket(AF_INET,SOCK_STREAM,);
if(sockfd==-){
perror("socket error\n");
exit();
}
else
printf("create successful\n"); len=sizeof(servaddr);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(SERVER_PORT);
n=inet_pton(AF_INET,argv[],&servaddr.sin_addr);//转化为网络可识别的ip
if(n==-){
printf("%s\n",strerror(errno));
exit();
}
if(n==){
printf("ip error\n");
exit();
} i=connect(sockfd,(struct sockaddr*)&servaddr,len);
if(i<){
printf("can't connect to %s,exit\n",argv[]);
printf("%s\n",strerror(errno));
exit();
}
close(sockfd); return ;
}
7、获取套接字的地址族
任务描述:
- c语言创建一个套接字,调用bind函数,其中端口设置为0,将被套接字绑定到本地某个端口上。
- 调用getsockname,显示内核为该bind函数分配的端口号
相关知识:
- int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen)
int getpeername(int sockfd, struct sockaddr * peeraddr, socken_t * addrlen);
getsockname函数返回与套接口关联的本地协议地址。使用场合:在不调用bind的TCP客户,当connect成功返回后,getsockname返回分配给此连接的本地IP地址和本地端口号;
在以端口号为0调用bind后,使用getsockname返回内核分配的本地端口号;getsockname可用来获取某套接口的地址族;在捆绑了通配IP地址的TCP服务器上,当连接建立后,可以使用getsockname获得分配给此连接的本地IP地址;
getpeername返回远程协议地址描述结构。当一个服务器是由调用过accept的,并且fork出一个子进程进行业务逻辑处理时,必须调用getpeername来获取对端地址和端口号。
main.c:
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<string.h> int main(void)
{
int sockfd,bind_fd,listen_fd,newfd,n;
char buf[];
struct sockaddr_in servaddr;
socklen_t len=sizeof(servaddr); sockfd=socket(AF_INET,SOCK_STREAM,);
if(sockfd==-){
perror("create socket fail\n");
exit();
}
else
printf("create success\n"); //初始化
bzero(&servaddr,sizeof(servaddr));//结构体初始化
servaddr.sin_family=AF_INET;//确定ip协议
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);//接收任意ip
servaddr.sin_port=htons();//设定端口号2500 //绑定端口
bind_fd=bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
if(bind_fd==-){
perror("bind fail\n");
exit();
}
else
printf("bind success\n"); listen_fd=listen(sockfd,);
if(listen_fd==-){
perror("listen fail\n");
exit();
}
else
printf("listen success\n"); if(getsockname(sockfd,(struct sockaddr*)&servaddr,&len)==-){
perror("getsockname error\n");
exit();
}
n=inet_ntop(AF_INET,&servaddr,buf,sizeof(buf));
printf("port :%d\n",ntohs(servaddr.sin_port));
close(sockfd); return ;
}
基于TCP的字符串传输程序的更多相关文章
- 模拟一个简单的基于tcp的远程关机程序(转)
最近在学习unix网络编程,现在正在学习tcp的通信.其实,只要建立起了tcp通信,操作远端的计算机就不是什么问题了.正向telnet一样,也是基于tcp/IP协议的.所以这个实验,也算是对telne ...
- 用scala实现一个基于TCP Socket的快速文件传输程序
这是用scala实现的一个简单的文件传输程序. 服务端 package jpush import java.io.{DataInputStream, File, FileOutputStream} i ...
- 利用TCP协议,实现基于Socket的小聊天程序(初级版)
TCP TCP (Transmission Control Protocol)属于传输层协议.其中TCP提供IP环境下的数据可靠传输,它提供的服务包括数据流传送.可靠性.有效流控.全双工操作和多路复用 ...
- 10.17小作业 基于TCP开发一款远程CMD程序
基于TCP开发一款远程CMD程序 客户端连接服务器后,可以向服务器发送命令 服务器收到命令后执行,无论执行是否成功,无论执行几遍,都将执行结果返回给客户端 注意: 执行系统指令使用subprocess ...
- 基于TCP协议的项目架构之Socket流传输的实现
项目背景 某银行的影像平台由于使用时间长,服务器等配置原因,老影像系统满足不了现在日益增长的数据量的需求,所以急需要升级改造.传统的影像平台使用的是Oracle数据库和简单的架构来存储数据(视频.图 ...
- Linux网络编程:基于TCP的程序开发回顾篇《转》
面向连接的TCP程序设计 基于TCP的程序开发分为服务器端和客户端两部分,常见的核心步骤和流程: 其实按照上面这个流程调用系统API确实可以完全实现应用层程序的开发,一点问题没有.可随着时间的推移,你 ...
- 编写基于TCP的应用程序
编写基于TCP的应用程序 这似乎是一个非常简单的话题, 就跟"是个人就能做网站"一样, 你可能也认为"是个人就能写使用TCP socket的网络程序". 不 ...
- Python网络编程02 /基于TCP、UDP协议的socket简单的通信、字符串转bytes类型
Python网络编程02 /基于TCP.UDP协议的socket简单的通信.字符串转bytes类型 目录 Python网络编程02 /基于TCP.UDP协议的socket简单的通信.字符串转bytes ...
- 标准C实现基于TCP/IP协议的文件传输
上学期集成程序设计的课堂作业,对于理解TCP/IP实现还是挺有帮助的. TCP/IP编程实现远程文件传输在LUNIX中一般都采用套接字(socket)系统调用. 采用客户/服务器模式,其程序编写步骤如 ...
随机推荐
- STM32F407 STLINK 在线调试 个人笔记
配置的部分请看本博客STM32分类下的环境配置篇目 开始调试 一些按键 查看寄存器 查看变量值 选中变量,右键,add to watch
- php 正则匹配包含字母、数字以及下划线,且至少包含2种
新系统注册功能需对用户名和密码做以下要求:包含字母.数字以及下划线,且至少包含2种: 在网上没有搜到符合要求的代码,于是自己对他人代码做了一点修改,经测试满足要求.代码如下: if (!preg_ma ...
- 准确率(Precision),召回率(Recall)以及综合评价指标(F1-Measure)
准确率和召回率是数据挖掘中预测,互联网中得搜索引擎等经常涉及的两个概念和指标. 准确率:又称“精度”,“正确率” 召回率:又称“查全率” 以检索为例,可以把搜索情况用下图表示: 相关 不相关 检索 ...
- POJ 3177 边双连通求连通量度的问题
这道题的总体思路就是找到连通量让它能够看作一个集合,然后找这个集合的度,度数为1的连通量为k,那么需要添加(k+1)/2条边才可以保证边双连通 这里因为一个连通量中low[]大小是相同的,所以我们用a ...
- vue.js基础知识总结
初始化一个项目 npm init -y 安装一些依赖 npm install 名称 --save 例如 npm install vue axios bootstrap --save --save 表示 ...
- 骑士精神 (codevs 2449)
题目描述 Description 在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位.在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标 ...
- 1597: [Usaco2008 Mar]土地购买 [ dp+斜率优化 ] 未完
传送门 1597: [Usaco2008 Mar]土地购买 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1979 Solved: 705[Subm ...
- windows 下安装Apache httpd 只需三步
1.下载 Apache 官网地址:http://httpd.apache.org/docs/current/platform/windows.html#down 找到这个, 看到这几个选项: Apac ...
- 进程&进程池
进程 服务器中, s.listen(n) n不能无限大,以为内存不可能无限大,n表示内存同一时间接纳的等待连接数,可以看成一个(队列),取出一个拿去建立连接,然后再放进一个,队列中一直保持n个连接 请 ...
- CodeForces 592A PawnChess
简单暴力模拟. #include<cstdio> #include<cstring> #include<cmath> #include<algorithm&g ...