Unix Socket

Unix Socket

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条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信