我对套接字编程完全不熟悉。现在我只是尝试我发现的随机内容。所以我有这个程序(下面的代码)使用csapp.h
和csapp.c
,特别是open_clientfd(hostname, port)
函数,以及RIO IO打印出你输入的内容。
https://github.com/tlozoot/cs-262-bank-protocol/blob/master/csapp.c
好吧,当我尝试运行程序./prog www.google.com 80(我甚至不确定主机名和端口)时,它会打印出奇怪的内容然后停止。
预期产出:
Enter message:abc
Echo:abc
实际输出:
Enter message:abc
Echo:HTTP/1.0 400 Bad Request
以下是我的代码:
int main(int argc, char **argv)
{
int clientfd, port;
char *host, buf[MAXLINE];
rio_t rio;
host = argv[1]; port = atoi(argv[2]);
clientfd = Open_clientfd(host, port);
Rio_readinitb(&rio, clientfd);
printf("Enter message:"); fflush(stdout);
while (Fgets(buf, MAXLINE, stdin) != NULL) {
Rio_writen(clientfd, buf, strlen(buf));
Rio_readlineb(&rio, buf, MAXLINE);
printf("Echo:");
Fputs(buf, stdout);
printf("Enter message:");
fflush(stdout);
}
Close(clientfd);
exit(0);
}
我有另一个问题。如何将这样的内容发送到套接字?
GET /index.html HTTP/1.1\r\n
Host: www.google.com\r\n
\r\n
答案 0 :(得分:2)
看起来你正在做这件事:
telnet www.google.com 80
Trying 173.194.123.19...
Connected to www.google.com.
Escape character is '^]'.
message abc
HTTP/1.0 400 Bad Request
答案 1 :(得分:1)
如果我正确理解您的代码和您的意图,那么您的代码就可以了......但不适用于google.com服务器。你知道,google.com服务器是一个HTTP服务器,因此会响应你在第二个问题中提出的HTTP请求。发送该请求会产生响应 - index.html内容和响应头。
如果您要使用您正在使用的库对一个简单的服务器应用程序进行编码,它会向客户端回送它发送的任何内容,那么您将看到所需的输出。
以下是简单服务器的完整代码,它可以接受并回送您发送给它的内容(由Beej网络编程指南提供,由我稍加修改)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#define PORT "3490" // the port users will be connecting to
#define BACKLOG 10 // how many pending connections queue will hold
void sigchld_handler(int s)
{
while(waitpid(-1, NULL, WNOHANG) > 0);
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=1;
char s[INET6_ADDRSTRLEN];
char buf[1024];
int rv;
memset(&buf, 0, sizeof(buf));
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
return 2;
}
freeaddrinfo(servinfo); // all done with this structure
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
printf("server: waiting for connections...\n");
while(1) { // main accept() loop
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
printf("server: got connection from %s\n", s);
if (!fork()) { // this is the child process
close(sockfd); // child doesn't need the listener
if (recv(new_fd, buf, sizeof(buf), 0) == -1){
perror("recv");
}
if (send(new_fd, buf, sizeof(buf), 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
close(new_fd); // parent doesn't need this
}
return 0;
}
应该完成它。希望我帮忙。顺便问一下,看看这个链接:[Beej的网络编程指南1。开始时非常好。
编辑: 至于第二个问题,你可以创建:
char* index_get_request=
"GET /index.html HTTP/1.1\r\n"
"Host: www.google.com\r\n"
"\r\n";
然后使用:
send(sock_fd, index_get_request, strlen(index_get_request), 0);
答案 2 :(得分:1)
HTTP协议在实践中相当复杂(核心协议很简单,但有很多极端情况)。我建议使用一些HTTP客户端库(例如libcurl ...)或一些HTTP服务器库(如libonion)
如果您不想使用HTTP或某些定义良好的协议,例如一些更简单的协议(您可能会发现许多其他协议的服务器和客户端库,如SMTP,JSON-RPC等等),您必须明确地设计和定义您需要对服务器端和客户端进行编码(并测试在你的机器上,两个不同的,或者如果使用localhost
,即在IPV4中使用127.0.0.1,则相同;
BTW,套接字不在标准C99(或C11)中。您需要经常通过操作系统提供它们。 POSIX的套接字受BSD socket API的启发。
请注意,tcp(7)套接字只是一个字节流。你需要缓冲发送和放大接收方。无法保证发射器端的单个send(2)(或write
)对应于相同数据大小的单个recv(2)(或read
)。通常,您需要poll(2)和缓冲区(对于部分应用程序“消息”)。
答案 3 :(得分:1)
结果看起来是正确的,它看起来好像你没有正确地说出端口的HTTP ......
jason@io ~ $ nc -v www.google.com 80
nc: sea09s15-in-f20.1e100.net (173.194.33.84) 80 [http] open
nc: using stream socket
message:abc
HTTP/1.0 400 Bad Request
Content-Type: text/html; charset=UTF-8
Content-Length: 1419
Date: Wed, 05 Aug 2015 23:00:52 GMT
Server: GFE/2.0
<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
<title>Error 400 (Bad Request)!!1</title>
<style>
*{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/errors/logo_sm_2.png) no-repeat}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/errors/logo_sm_2_hr.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/errors/logo_sm_2_hr.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/errors/logo_sm_2_hr.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:55px;width:150px}
</style>
<a href=//www.google.com/><span id=logo aria-label=Google></span></a>
<p><b>400.</b> <ins>That’s an error.</ins>
<p>Your client has issued a malformed or illegal request. <ins>That’s all we know.</ins>
如果您想了解有关HTTP文本协议的更多信息,请参阅RFC。 FTP协议可能更有趣,因为它是文本和二进制的混合。我写的第一批网络软件之一是FTP服务器。
如果我没记错的话,RIO是微软用于低延迟I / O的API。您可能希望避免这种情况,除非您真正需要它。如果您与使用Windows相关联,则可能需要使用类似boost::asio
的内容,因为它的级别更高一些。另一种选择可能是libuv。使用库的性能通常与使用原始套接字相当,具体取决于您的软件设计。否则,如果你可以使用OSX / Linux / FreeBSD / NetBSD / Solaris / etc ......我建议使用标准POSIX API。