TCP服务端和客户端的编程流程是网络编程的重点。以下内容包括编程步骤、代码实现、以及代码解析四个方面。
目录
tcp服务器与客户端的编程流程如下所示:

左边为服务端,右边为客户端。
我们常见的服务器客户端大多都是遵循这个步骤的。
我们按照上述步骤来实现一个简易的服务端与客户端:
ser.c
# include<stdio.h>
# include<unistd.h>
# include<stdlib.h>
# include<string.h>
# include<assert.h>
# include<sys/socket.h>
# include<netinet/in.h>
# include<arpa/inet.h>
int main()
{
//创建套接子
//AF_INFT表示ipv4 SOCKET_STREAM表示传输层使用tcp协议
int sockfd = socket(AF_INET,SOCK_STREAM,0);
assert(sockfd!=-1);
//绑定
struct sockaddr_in saddr,caddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);//1024知名 4096保留 5000之上,临时端口
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
assert(res!=-1);
listen(sockfd,5);//开机
while(1)
{
int len = sizeof(caddr);
int c =accept(sockfd,(struct sockaddr*)&caddr,&len);
if(c<0)
{
continue;
}
printf("c=%d,ip=%s
",c,inet_ntoa(caddr.sin_addr));
char buf[128]={0};
int n = recv(c,buf,127,0);
printf("buf(%d)=%s
",n,buf);
send(c,"ok",2,0);
close(c);//待机
}
exit(0);
}
cli.c
# include<stdio.h>
# include<unistd.h>
# include<stdlib.h>
# include<string.h>
# include<assert.h>
# include<sys/socket.h>
# include<netinet/in.h>
# include<arpa/inet.h>
int main()
{
//创建套接子
//AF_INFT表示ipv4 SOCKET_STREAM表示传输层使用tcp协议
int sockfd = socket(AF_INET,SOCK_STREAM,0);
assert(sockfd!=-1);
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr =inet_addr("127.0.0.1");
int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
assert(res!=-1);
printf("input:
");
char buf[128]= {0};
fgets(buf,128,stdin);
send(sockfd,buf,strlen(buf),0);
memset(buf,0,128);
recv(sockfd,buf,127,0);
printf("buf=%s
",buf);
close(sockfd);
exit(0);
}
程序运行结果如下图所示:

从下面几个问题出发,对代码进行解析:
首先,listen在套接字函数中表示让一个套接字处于监听到来的连接请求的状态,那么第二个参数的含义为完成三次握手建立连接的队列的长度,5就代表在等待服务器的客户端
当客户端执行connect函数的时候,开始进行三次握手。当某一端close的时候,开始四次挥手。当两端同时close的时候,四次挥手会演变为3次。
sockaddr如下:
struct sockaddr {
unsigned short sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};
sa_family是地址家族,sa_data是一个14字节的协议地址。
sockaddr_in如下:
struct sockaddr_in {
short int sin_family; /* Address family */
unsigned short int sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */u
nsigned char sin_zero[8]; /* Same size as struct sockaddr */
};
在上述代码中我们可以发现,sockaddr和sockaddr_in可以相互转换,这是因为sockaddr是通用的套接字地址,而sockaddr_in是网络环境下的套接字地址,两个结构体的长度一样。sockaddr_in结构体中的元素sin_zero就是为了让sockaddr与sockaddr_in这两个结构体保持大小相同而预留的空字节。
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has
最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru
在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo
我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru
我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b