Go的Tcp服务器应该设置多少个accepttcpclientasync合适

用Go语言做一个可用的TCP服务器_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
用Go语言做一个可用的TCP服务器
上传于||文档简介
&&我​看​了​几​个​g​o​的​示​例​代​码​,​发​现​有​很​多​错​误​,​最​常​见​的​是​:​
,​居​然​不​判​断​数​据​有​没​有​接​收​完​整​就​开​始​解​析​了​.​.​.​
,​不​考​虑​t​c​p​的​粘​包​/​分​包​等​问​题​.​.​.​
​假​设​,​我​们​使​用​二​进​制​协​议​,​数​据​头​是​下​面​这​样​,2​字​节​
​s​t​r​u​c​t​ ​A​C​C​_​C​M​D​H​E​A​D​
​{​
​ ​u​i​n​t6​_​t​ ​m​_​w​C​m​d​T​y​p​e​;​ ​ ​/​/​请​求​类​型​
​ ​u​i​n​t6​_​t​ ​m​_​w​A​t​t​r​;​ ​ ​/​/​属​性​字​段​
​ ​u​i​n​t2​_​t​ ​m​_​n​L​e​n​;​ ​ ​ ​/​/​数​据​包​的​长​度​,​不​包​括​该​数​据​头​
​ ​u​i​n​t2​_​t​ ​m​_​n​E​x​p​a​n​d​I​n​f​o​;​ ​/​/​扩​展​信​息​
​}​;​
​请​注​意​数​据​的​大​小​端​问​题​.
阅读已结束,如果下载本文需要使用2下载券
想免费下载本文?
定制HR最喜欢的简历
你可能喜欢1. TCP回射示例
服务器代码
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
#include &sys/socket.h&
#include &arpa/inet.h&
#include &netinet/in.h&
#define SRV_PORT 8888
#define MAXLINE 4096
void str_echo(int fd);
int main(int argc, char **argv)
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(listenfd & 0)
perror("create socket error.");
struct sockaddr_
bzero(&srvaddr, sizeof(srvaddr));
srvaddr.sin_family = AF_INET;
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
srvaddr.sin_port = htons(SRV_PORT);
if(bind(listenfd, (struct sockaddr*)&srvaddr, sizeof(srvaddr)) & 0)
perror("bind error.");
if(listen(listenfd, 1023) & 0)
perror("listen error.");
struct sockaddr_
socklen_t clilen = sizeof(cliaddr);
int connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen);
if(connfd & 0)
perror("accept error.");
if( (childpid = fork()) == 0 )
close(listenfd);
str_echo(connfd);
close(connfd);
void str_echo(int sockfd)
char line[MAXLINE];
while(read(sockfd, line, MAXLINE) != 0)
if(write(sockfd, line, strlen(line)) != strlen(line))
perror("write error");
客户端代码
#include &stdio.h&
#include &stdlib.h&
#include &string.h&
#include &sys/socket.h&
#include &arpa/inet.h&
#include &netinet/in.h&
#define SRV_PORT 8888
#define MAXLINE 4096
void str_cli(FILE *fp, int sockfd);
int main(int argc, char **argv)
if(argc != 2)
printf("usage:tcpcli &ip address&\n");
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd & 0)
perror("create socket error.");
struct sockaddr_
bzero(&srvaddr, sizeof(srvaddr));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(SRV_PORT);
if(inet_pton(AF_INET, argv[1], &srvaddr.sin_addr) &= 0)
printf("address error %s\n", argv[1]);
if(connect(sockfd, (struct sockaddr *)&srvaddr, sizeof(srvaddr)) & 0 )
perror("connect error");
str_cli(stdin, sockfd);
void str_cli(FILE *fp, int sockfd)
char sendline[MAXLINE];
char readline[MAXLINE];
while(fgets(sendline, MAXLINE, fp))
if( write(sockfd, sendline, strlen(sendline)) != strlen(sendline) )
perror("send data error");
if( read(sockfd, readline, MAXLINE) == 0)
perror("recv data error");
fputs(readline,stdout);
2. 示例启动过程
客户端服务器建立连接后发生的动作:
客户端调用str_cli函数,阻塞于fgets,等待输入;
服务器中的accept返回时,服务器调用fork,再由子进程调用str_echo,子进程阻塞于read等待客户端发送的数据;
另一方面服务器的父进程再次调用accept等待下一个客户连接。
所以至此有三个阻塞进程:客户进程,服务器父进程,服务器子进程。
3. 示例正常终止过程
客户端键入EOF,str_cli函数返回main函数, main函数调用exit终止进程;
由于客户端程序没有关闭其描述符,所以其描述符由内核关闭,此时开始发送FIN与服务器进行TCP四次握手关闭连接;
服务器子进程收到FIN,子进程从str_echo返回子进程的main函数,通过调用exit终止子进程,子进程所有描述符关闭;
最终服务器发FIN给客户端,客户端返回ACK,进入TIME_WAIT状态。连接完全终止。
服务器子进程终止时会给父进程发送一个SIGCHLD信号,我们没有捕捉此信号,信号默认被忽略,这导致子进程进入僵死状态,僵死进程占用系统资源,所以我们还需要处理僵死的进程。
4. POSIX信号处理(处理3产生的僵死进程)
& & 信号由一个进程发给另一个进程(或自身),也可由内核发给某个进程。
1)调用函数sigaction设定一个信号的处理有以下三种选择:
提供一个信号处理函数(signal handler),捕获到特定信号(SIGKILL与SIGSTOP不能被捕获)发生它就被调用,其原型是 void handler(int signo);
可以设置信号为SIG_IGN来忽略它(SIGKILL与SIGSTOP不能被忽略);
可以设置信号为SIG_DFL来启用信号的默认处理。
2)signal函数(使用系统提供的)
#include &signal.h&
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
3)处理SIGCHLD信号(处理僵死进程)
设置僵死进程的目的是为了维护子进程的信息,以便父进程在某个时候获取;
为了防止僵死进程产生无论何时调用fork创建子进程,父进程都得wait它们,为此我们可以建立一个捕获SIGCHLD的信号处理函数
在创建子进程之前调用如下函数:
& & signal(SIGCHLD, sig_child);
创建信号处理函数sig_child& &&
void sig_chld(int signo)
pid_t pid = wait(&stat);
printf("child %d terminated\n", pid);
5)处理被中断的系统调用
慢系统调用:用于描述永远阻塞的系统调用如accept
适用慢系统调用的规则:当阻塞于某个慢系统调用的一个进程捕获某个信号,且相应信号处理函数返回时该系统调用可能返回一个EINTR错误。编写捕获信号的程序时,应该对慢系统调用返回EINTR有所准备。
如:处理被中断的accept,以下是用于处理被中断的accept调用的代码
for( ; ; )
clilen = sizeof(cliaddr);
if((connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen)) & 0)
if(errno == EINTR)
perror("accept error");
5. wait和waitpid
#include &sys/wait.h&
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
status为进程的终止状态:正常终止,信号杀死或由作业控制停止。waitpid的pid参数允许我们指定想等待的进程,pid值为-1时表示等待第一个终止的进程。
wait不足以防止僵死进程的出现,在调用wait之前可能有几个SIGCHLD信号产生,但是wait的调用次数不确定,可能只调用一次。
防止僵死进程正确的解决方法是用waitpid,waitpid必须指定WNOHANG选项,以告知waitpid在有尚未终止的子进程在运行时不要阻塞。可以改造上面sig_chld函数如下:
void sig_chld(int signo)
while( (pid = waitpid(-1, &stat, WNOHANG)) & 0 )
printf("child %d terminated\n", pid);
小结:网络编程中可能会遇到的三种情况
fork子进程时,必须捕获SIGCHLD信号;
捕获信号时,必须处理被中断的系统调用(EINTR);
SIGCHLD的信号处理函数必须编写正确,用waitpid而不是wait,以免留下僵死进程。
6. accept返回前连接终止:服务器在调用accept之前收到RST(此种情况在16章再详细给出)
7.服务器进程终止
& & & &示例程序中,如果服务器终止,当服务器发出的FIN到达客户端时,客户端正阻塞在fgets上等待用户输入。客户端此时要应对两个描述符(套接字和用户输入),它不能单纯的阻塞在其中任何一个源的输入上,这个涉及到select和poll,第六章继续讨论。
8.SIGPIPE信号
场景:例子中服务器终止后,客户端在读数据之前,向服务器执行两次的写操作。
客户端第一次写操作引起服务器的RST回复,而当进程向收到RST的套接字就行写操作时,内核会向该进程发送一个SIGPIPE信号,该信号的默认行为是终止进程。因此进程应该捕获SIGPIPE信号以免它被动的被终止。不论进程忽略SIGPIPE信号还是捕获处理了,写操作都将返回EPIPE错误。
处理SIGPIPE的建议方法:取决于其发生时进程想要做什么,如果没有什么特殊的事情做,一般直接将此信号设置为SIG_IGN,并假设后续的输出操作将捕获EPIPE错误。注意,如果有多个套接字,该信号无法区分哪个套接字出错了,要知道哪个write出差,要么忽略该信号,要么信号处理完后再处理来自write的EPIPE信号。
9.服务器主机崩溃
& & &主机崩溃可能导致客户端长时间(9分钟左右)对服务器进行重连,这使得我们有时候需要在更短的时间发现服务器已经挂掉,此时可以把read设置一个超时(后面再详说),或者用SO_KEEPALIVE套接字选项和一些心跳技术实现(第七章)。
&10. 服务器主机崩溃后重启
& & 服务器崩溃后,不向客户端发送任何信息,客户端继续向服务器发送数据。服务器重启后由于失去了崩溃前所有连接信息,所有服务器TCP对所有来自客户的数据分节响应RST,客户收到RST后,阻塞的read调用返回ECONNRESET错误。
11. 服务器主机关机:会先发SIGTERM信号,再发SIGKILL信号。进程终止后与7描述的情况一样。
12. 关于数据格式
在客户和服务器之间传递文本:文本传输不论客户机与服务器的字节序如何,都可以对数据进行正常的传递,不发生错误。
在客户和服务器之间传递二进制结构:传递二进制结构如果客户机和服务器字节序不同将发生错误。
注意三个潜在的问题:
不同的实现以不同格式存储二进制数(如字节序大端小端);
不同实现在存储相同的C数据类型可能存在差异(对于short,int或long等整数类型大小在不同系统间可能不同);
不同的实现给结构打包的方式存在差异,取决于各种数据类型所用的位数及机器对齐限制。
解决以上问题的常用方法:
把所用数据用文本串传递;
显式定义所支持数据类型的二进制格式(位数、大端、小端),并以这样的格式在客户机与服务器之间传递。
阅读(...) 评论()转贴:用Go语言做一个可用的TCP服务器_致力于linux网络服务器程序..._golang吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0可签7级以上的吧50个
本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:1,348贴子:
转贴:用Go语言做一个可用的TCP服务器_致力于linux网络服务器程序...
安卓/android/苹果/iOS/App服务器程序定制开发
QQ: ,用Go语言做一个可用的TCP服务器转自:
贴吧热议榜
使用签名档&&
保存至快速回贴

我要回帖

更多关于 tcp listen accept 的文章

 

随机推荐