2023年7月15日发(作者:)
内部公开▲
关于Socket编程的几个问题
一 TCP Socket client端
TCP为上层应用程序提供一个面向连接、可靠、有序、流量控制的全双工服务,使用TCP需要一个 建立连接,交换数据,断开连接的过程。
TCP客户端不需要绑定,它调用Socket函数建立一个套结字后,指定一个服务器的Socket地址和端对,就可以在该套结字上调用Connect去连接服务器。然后通过TCP套结字读数据。因为TCP数据是没有边界的字节流形式,必须循环去读直到该套结字上数据读完或出错为止。比如服务器发送来的数据是26个字节的字符串,| “Mon May 26 20 : 58 : 40
2003rn” 这26个字节可能是通过一个TCP片段该片段上有完整的26字节;也可能是通过26个TCP片段,每个片段1字节;还可能是任何字节数的片段组合(片断总字节数是26)
1 #include "unp.h"
2 int
3 main(int argc, char **argv)
4 {
5 int sockfd, n;
6 char recvline[MAXLINE + 1];
7 struct sockaddr_in servaddr;
8 if (argc != 2)
9 err_quit("usage:
10 if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
11 err_sys("socket error");
12 bzero(&servaddr, sizeof(servaddr));
13 _family = AF_INET;
14 _port = htons(13); /* daytime server */
15 if (inet_pton(AF_INET, argv[1], &_addr) <= 0)
16 err_quit("inet_pton error for %s", argv[1]);
17 if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
18 err_sys("connect error");
19 while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
20 recvline[n] = 0; /* null terminate */
21 if (fputs(recvline, stdout) == EOF)
22 err_sys("fputs error");
23 }
24 if (n < 0)
25 err_sys("read error");
26 exit(0);
27 }
本文所有信息为中兴通讯股份有限公司内部信息,未经允许,不得外传 第1页,共7页 内部公开▲
该客户端程序是以服务器端断开连接(客户端读不到数据)作为读数据结束,进而终止客户程序的,HTTP/1.0的客户端也是这种机制。但有些TCP的应用程序,比如 SMTP程序 ,当已读到两个字节ASCII形式的回车换行时就结束 ,还有的 应用程序会在数据前添加数据的字节数……总之 TCP本身并不标志字节流的结束和边界。
二 TCP Socket Server端程序示例
一个Server的TCP程序需要有bind listen等准备程序(三步握手完成后该函数才能返回),server一般是一个无限循环,在新建的连接上读写数据
1 #include "unp.h".
2 #include
3 int
4 main(int argc, char **argv)
5 {
6 int listenfd, connfd;
7 struct sockaddr_in servaddr;
8 char buff[MAXLINE];
9 time_t ticks;
10 listenfd = Socket(AF_INET, SOCK_STREAM, 0);
11 bzeros(&servaddr, sizeof(servaddr));
12 _family = AF_INET;
13 _addr.s_addr = htonl(INADDR_ANY);
14 _port = htons(13); /* daytime server */
15 Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
16 Listen(listenfd, LISTENQ);
17 for ( ; ; ) {
18 connfd = Accept(listenfd, (SA *) NULL, NULL);
19 ticks = time(NULL);
20 snprintf(buff, sizeof(buff), "%.24srn", ctime(&ticks));
21 Write(connfd, buff, strlen(buff));
22 Close(connfd);
23 }
24 }
三 几个有用的函数介绍
1
#include
本文所有信息为中兴通讯股份有限公司内部信息,未经允许,不得外传 第2页,共7页 内部公开▲
int inet_pton(int family, const char *strptr, void *addrptr);
Returns: 1 if OK, 0 if input not a valid presentation format, -1 on error
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
Returns: pointer to result if OK, NULL on error
inet_pton :该函数与IPV6兼容,将ASCII command-line argument(如206.62.226.35)转化为合适的格式。即使你现在的系统还未支持IPV6,你也可以用新函数:
inet_pton(AF_INET, cp, &_addr);
来代替以前的
_addr.s_addr = inet_addr(cp); 其中cp 是字符串如“206.62.226.35”;
使用 char str[INET_ADDRSTRLEN];
ptr = inet_ntop(AF_INET, &_addr, str, sizeof(str));
来代替以前的
ptr = inet_ntoa(_addr);
2
struct sockaddr是网络地址的通用标识,当需要一个地址的指针时(作为参数),必须将struct
sockaddr_in 转换为该通用表示
3
库函数time 返回当前的时间和日期(以UTC时间为坐标,相对于Unix Epoch: 00:00:00
January 1, 1970流逝的秒数)而库函数 ctime, 将time返回的整数值转化为可读的字符串形式如Mon May 26 20:58:40 2003
4
虽然功能一致,snprintf比sprintf要安全,
snprintf(buff, sizeof(buff), "%.24srn", ctime(&ticks));
如果要拷贝的数据"%.24srn", ctime(&ticks)程度超过sizeof(buff),则会自动截断。
其它我们要注意的函数还有 gets, strcat, and strcpy, 一般应该调用fgets, strncat, and strncpy
来代替. 最好使用现在已可用的函数strlcat和strlcpy, which ensure the result is a properly
terminated string.写一个安全可靠的网络应用程序应注意的一些点可以参考 Chapter 23 of
[Garfinkel, Schwartz, and Spafford 2003].
5
还可以改变listen可以监听的套结字最大个数
137 void
138 Listen (int fd, int backlog)
139 {
140 char *ptr;
141 /* can override 2nd argument with environment variable */
142 if ( (ptr = getenv("LISTENQ")) != NULL)
143 backlog = atoi (ptr);
144 if (listen (fd, backlog) < 0)
145 err_sys ("listen error");
本文所有信息为中兴通讯股份有限公司内部信息,未经允许,不得外传 第3页,共7页 内部公开▲
146 }
四 TCP建立连接的三步握手及accept内幕
为了加深对 Connect和Accept的理解 ,介绍 三步握手。当一个TCP连接建立时,有哪些内幕呢 ?为了建立一个连接
1 首先服务器必须准备好接受客户端的连接,通过调用Socket,Bind,Listen (passive open 状)
2 客户端调用Connect使服务器active open: 该函数会使ClientTCP向Server发送一个同步片段“SYN”作用就是告诉Server,Client在该连接上将要发送数据的初始序列号。
3收到后Server必须相应该SYN,即其TCP发送一个ACK。同时Server TCP必须发送一个SYN来告诉Client,Server将在这个连接上发送的数据的初始序列号。
4 Client必须以 ACK来相应Server的SYN
下图即为三步握手过程,假设Client初始序列号为j,Server的是k
ClientsocketConnect (blocks)(Active open)Connect returnsServerSYNSsocket ,bind,listen(passive open)
j
j+1accept(blocks),ACKk
NYACK k+1accept returns read (blocks)
如果没有收到对段ACK,将自动重复发送SYN直到超时。
SYN可以只是TCP头+IP头而无实际内容,也可携带TCP选项如MSS,告诉对方自己能一次接收片段的最大字节数。TCP_MAXSEG 。。在长链路(卫星)或 高速带宽链路上常用的TCP选项是Window scale option(滑动窗口的大小)和time stamp option
断开连接需要四步,如下图
先调用close 的client,其TCP会向server发送一个FIN来表示发送数据已结束。Server的TCP收到该FIN后,一方面向Client的TCP回复一个ACK,一面给自己的application层一个end of file,表示在该连接上再也读不到数据了(read return 0)。然后收到EOF的server也会调用close,使其TCP发送一个FIN。Client的TCP收到FIN后回复ACK。
大多数应用中,client充当active close角色,但在HTTP中 Server是 active close
本文所有信息为中兴通讯股份有限公司内部信息,未经允许,不得外传 第4页,共7页 内部公开▲
ClientFIN
MACK M+1FIN NServerclose(active close)(passive close)read returns 0closeACK
N+1
五 UDP程序步骤
UDP serversocket()bind()UDP clientrecvfrom()Socket()Blocks until datagram received
from clientsendto()data(request)Process request)reply(
atdasendto()recvfrom()close()
UDP不需要建立连接,client只需要调用sendto向server发送数据(请求),当然以 server地址为参数。Server的receivefrom函数一直block,直到它接收到来自client的数据,recvfrom返回client的地址以及来自client的数据,server在此地址上调用sendto给client回复。
#include
ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags, struct
sockaddr *from, socklen_t *addrlen);
本文所有信息为中兴通讯股份有限公司内部信息,未经允许,不得外传 第5页,共7页 内部公开▲
ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags,
const struct sockaddr *to, socklen_t addrlen);
六 形成一个daemon进程
1 #include "unp.h"
2 #include
3 #define MAXFD 64
4 extern int daemon_proc; /* defined in error.c */
5 int
6 daemon_init(const char *pname, int facility)
7 {
8 int i;
9 pid_t pid;
10 if ( (pid = Fork()) < 0)
11 return (-1);
12 else if (pid)
13 _exit(0); /* parent terminates */
14 /* child */
15 if (setsid() < 0) /* become session leader */
16 return (-1);
17 Signal(SIGHUP, SIG_IGN);
18 if ( (pid = Fork()) < 0)
19 return (-1);
20 else if (pid)
21 _exit(0); /* child 1 terminates */
22 /* child */
23 daemon_proc = 1; /* for err_XXX() functions */
24 chdir("/"); /* change working directory */
25 /* close off file descriptors */
26 for (i = 0; i < MAXFD; i++)
27 close(i);
本文所有信息为中兴通讯股份有限公司内部信息,未经允许,不得外传 第6页,共7页 内部公开▲
28 /* redirect stdin, stdout, and stderr to /dev/null */
29 open("/dev/null", O_RDONLY);
30 open("/dev/null", O_RDWR);
31 open("/dev/null", O_RDWR);
32 openlog(pname, LOG_PID, facility);
33 return (0); /* success */
34 }
关于socket I/O的复用,和对select、poll等函数的使用。参考第二部分第6章,一些名字和地址转换的函数,参考第11章
本文所有信息为中兴通讯股份有限公司内部信息,未经允许,不得外传 第7页,共7页
发布者:admin,转转请注明出处:http://www.yc00.com/web/1689407537a243117.html
评论列表(0条)