#socket Socket本身有“插座”的意思,在Linux环境下,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。
在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口号”就对应一个socket。欲建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接。因此可以用Socket来描述网络连接的一对一关系。
在网络通信中,套接字一定是成对出现的。一端的发送缓冲区对应对端的接收缓冲区。我们使用同一个文件描述符索发送缓冲区和接收缓冲区。
#socket API ##为TCP/IP协议设计的应用层编程接口
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节 为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。 h表示host,n表示network,l表示32位长整数,s表示16位短整数。 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回,如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。
1 2 3 4 5 6 #include <arpa/inet.h> uint32_t htonl (uint32_t hostlong) ;uint16_t htons (uint16_t hostshort) ;uint32_t ntohl (uint32_t netlong) ;uint16_t ntohs (uint16_t netshort) ;
#IP地址转换函数
1 2 3 4 5 6 7 8 #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int inet_aton (const char *cp, struct in_addr *inp) ;in_addr_t inet_addr (const char *cp) ;char *inet_ntoa (struct in_addr in) ;
1 2 3 4 5 6 #include <arpa/inet.h> int inet_pton (int af, const char *src, void *dst) ;const char *inet_ntop (int af, const void *src, char *dst, socklen_t size) ;
#sockaddr数据结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 struct sockaddr { sa_family_t sa_family; char sa_data[14 ]; }; struct sockaddr_in { __kernel_sa_family_t sin_family; 地址结构类型 __be16 sin_port; 端口号 struct in_addr sin_addr ; IP地址 unsigned char __pad[__SOCK_SIZE__ - sizeof (short int ) - sizeof (unsigned short int ) - sizeof (struct in_addr)]; }; struct in_addr { __be32 s_addr; }; struct sockaddr_in6 { unsigned short int sin6_family; __be16 sin6_port; __be32 sin6_flowinfo; struct in6_addr sin6_addr ; __u32 sin6_scope_id; }; struct in6_addr { union { __u8 u6_addr8[16 ]; __be16 u6_addr16[8 ]; __be32 u6_addr32[4 ]; } in6_u; #define s6_addr in6_u.u6_addr8 #define s6_addr16 in6_u.u6_addr16 #define s6_addr32 in6_u.u6_addr32 }; #define UNIX_PATH_MAX 108 struct sockaddr_un { __kernel_sa_family_t sun_family; char sun_path[UNIX_PATH_MAX]; };
Pv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位端口号和32位IP地址,IPv6地址用sockaddr_in6结构体表示,包括16位端口号、128位IP地址和一些控制字段
因此,socket API可以接受各种类型的sockaddr结构体指针做参数,例如bind、accept、connect等函数,这些函数的参数应该设计成void *类型以便接受各种类型的指针,但是sock API的实现早于ANSI C标准化,那时还没有void *类型,因此这些函数的参数都用struct sockaddr *类型表示,在传递参数之前要强制类型转换一下,例如:
1 2 struct sockaddr_in servaddr ;bind(listen_fd, (struct sockaddr *)&servaddr, sizeof (servaddr));
TCP客户端:socket() -> connect() -> write() <-> read() -> close() TCP服务端:socket() -> bind() -> listen() -> accept() -> 阻塞直到有客户端连接 -> read() <-> write() -> read() -> close()
##socket函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int socket(int domain, int type , int protocol); domain: AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址 AF_INET6 与上面类似,不过是来用IPv6的地址 AF_UNIX 本地协议,使用在Unix和Linux系统上,一般都是当客户端和服务器在同一台及其上的时候使用 type : SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。 SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。 SOCK_SEQPACKET该协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须把这个包完整的接受才能进行读取。 SOCK_RAW socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议) SOCK_RDM 这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数据包的顺序 protocol: 传0 表示使用默认协议。 返回值: 成功:返回指向新创建的socket的文件描述符,失败:返回-1,设置errno
socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket()调用出错则返回-1。对于IPv4,domain参数指定为AF_INET。对于TCP协议,type参数指定为SOCK_STREAM,表示面向流的传输协议。如果是UDP协议,则type参数指定为SOCK_DGRAM,表示面向数据报的传输协议。protocol参数的介绍从略,指定为0即可。
##bind函数
1 2 3 4 5 6 7 8 9 10 11 12 int bind (int sockfd, const struct sockaddr *addr, socklen_t addrlen); sockfd: socket文件描述符 addr: 构造出IP地址加端口号 addrlen: sizeof(addr)长度 返回值: 成功返回0,失败返回-1, 设置errno
服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接,因此服务器需要调用bind绑定一个固定的网络地址和端口号。
bind()的作用是将参数sockfd和addr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听addr所描述的地址和端口号。前面讲过,struct sockaddr *是一个通用指针类型,addr参数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需要第三个参数addrlen指定结构体的长度。如:
1 2 3 4 5 struct sockaddr_in servaddr;bzero (&servaddr, sizeof (servaddr));servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl (INADDR_ANY); servaddr.sin_port = htons (6666 );
##listen函数
1 2 3 4 5 6 7 #include <sys/types.h> #include <sys/socket.h> int listen (int sockfd, int backlog) ;sockfd: socket文件描述符 backlog: 排队建立3 次握手队列和刚刚建立3 次握手队列的链接数和
典型的服务器程序可以同时服务于多个客户端,当有客户端发起连接时,服务器调用的accept()返回并接受这个连接,如果有大量的客户端发起连接而服务器来不及处理,尚未accept的客户端就处于连接等待状态,listen()声明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接待状态,如果接收到更多的连接请求就忽略。listen()成功返回0,失败返回-1。
##accept函数
1 2 3 4 5 6 7 8 9 10 11 12 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); sockdf: socket文件描述符 addr: 传出参数,返回链接客户端地址信息,含IP地址和端口号 addrlen: 传入传出参数(值-结果),传入sizeof(addr)大小,函数返回时返回真正接收到地址结构体的大小 返回值: 成功返回一个新的socket文件描述符,用于和客户端通信,失败返回-1,设置errno
三次握手完成后,服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来。addr是一个传出参数,accept()返回时传出客户端的地址和端口号。addrlen参数是一个传入传出参数(value-result argument),传入的是调用者提供的缓冲区addr的长度以避免缓冲区溢出问题,传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区)。如果给addr参数传NULL,表示不关心客户端的地址。
1 2 3 4 5 6 7 while (1 ) { cliaddr_len = sizeof (cliaddr); connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); n = read(connfd, buf, MAXLINE); ...... close(connfd); }
我们的服务器程序结构整个是一个while死循环,每次循环处理一个客户端连接。由于cliaddr_len是传入传出参数,每次调用accept()之前应该重新赋初值。accept()的参数listenfd是先前的监听文件描述符,而accept()的返回值是另外一个文件描述符connfd,之后与客户端之间就通过这个connfd通讯,最后关闭connfd断开连接,而不关闭listenfd,再次回到循环开头listenfd仍然用作accept的参数。accept()成功返回一个文件描述符,出错返回-1
##connect函数
1 2 3 4 5 6 7 8 9 10 11 12 #include <sys/types.h> #include <sys/socket.h> int connect (int sockfd, const struct sockaddr *addr, socklen_t addrlen) ;sockdf: socket文件描述符 addr: 传入参数,指定服务器端地址信息,含IP地址和端口号 addrlen: 传入参数,传入sizeof (addr)大小 返回值: 成功返回0 ,失败返回-1 ,设置errno
客户端需要调用connect()连接服务器,connect和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址。connect()成功返回0,出错返回-1。
服务器调用socket()、bind()、listen()完成初始化后,调用accept()阻塞等待,处于监听端口的状态,客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答,服务器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK段,服务器收到后从accept()返回。 数据传输的过程: 建立连接后,TCP协议提供全双工的通信服务,但是一般的客户端/服务器程序的流程是由客户端主动发起请求,服务器被动处理请求,一问一答的方式。因此,服务器从accept()返回后立刻调用read(),读socket就像读管道一样,如果没有数据到达就阻塞等待,这时客户端调用write()发送请求给服务器,服务器收到后从read()返回,对客户端的请求进行处理,在此期间客户端调用read()阻塞等待服务器的应答,服务器调用write()将处理结果发回给客户端,再次调用read()阻塞等待下一条请求,客户端收到后从read()返回,发送下一条请求,如此循环下去。 如果客户端没有更多的请求了,就调用close()关闭连接,就像写端关闭的管道一样,服务器的read()返回0,这样服务器就知道客户端关闭了连接,也调用close()关闭连接。注意,任何一方调用close()后,连接的两个传输方向都关闭,不能再发送数据了。如果一方调用shutdown()则连接处于半关闭状态,仍可接收对方发来的数据。 在学习socket API时要注意应用程序和TCP协议层是如何交互的: 应用程序调用某个socket函数时TCP协议层完成什么动作,比如调用connect()会发出SYN段 应用程序如何知道TCP协议层的状态变化,比如从某个阻塞的socket函数返回就表明TCP协议收到了某些段,再比如read()返回0就表明收到了FIN段
##最简单的客户端/服务器程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define MAXLINE 80 #define SERV_PORT 6666 int main (void ) { struct sockaddr_in servaddr , cliaddr ; socklen_t cliaddr_len; int listenfd, connfd; char buf[MAXLINE]; char str[INET_ADDRSTRLEN]; int i, n; listenfd = socket(AF_INET, SOCK_STREAM, 0 ); bzero(&servaddr, sizeof (servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); bind(listenfd, (struct sockaddr *)&servaddr, sizeof (servaddr)); listen(listenfd, 20 ); printf ("Accepting connections ...\n" ); while (1 ) { cliaddr_len = sizeof (cliaddr); connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); n = read(connfd, buf, MAXLINE); printf ("received from %s at PORT %d\n" , inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof (str)), ntohs(cliaddr.sin_port)); for (i = 0 ; i < n; i++) buf[i] = toupper (buf[i]); write(connfd, buf, n); close(connfd); } return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #define MAXLINE 80 #define SERV_PORT 6666 int main (int argc, char *argv[]) { struct sockaddr_in servaddr ; char buf[MAXLINE]; int sockfd, n; char *str; if (argc != 2 ) { fputs ("usage: ./client message\n" , stderr ); exit (1 ); } str = argv[1 ]; sockfd = socket(AF_INET, SOCK_STREAM, 0 ); bzero(&servaddr, sizeof (servaddr)); servaddr.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1" , &servaddr.sin_addr); servaddr.sin_port = htons(SERV_PORT); connect(sockfd, (struct sockaddr *)&servaddr, sizeof (servaddr)); write(sockfd, str, strlen (str)); n = read(sockfd, buf, MAXLINE); printf ("Response from server:\n" ); write(STDOUT_FILENO, buf, n); close(sockfd); return 0 ; }
##出错封装函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 //wrap.c void perr_exit(const char *s) { perror(s); exit (1); } int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr) { int n; again: if ( (n = accept(fd, sa, salenptr)) < 0) { if ((errno == ECONNABORTED) || (errno == EINTR)) goto again; else perr_exit("accept error" ); } return n; } int Bind(int fd, const struct sockaddr *sa, socklen_t salen) { int n; if ((n = bind(fd, sa, salen)) < 0) perr_exit("bind error" ); return n; } int Connect(int fd, const struct sockaddr *sa, socklen_t salen) { int n; if ((n = connect(fd, sa, salen)) < 0) perr_exit("connect error" ); return n; } int Listen(int fd, int backlog) { int n; if ((n = listen(fd, backlog)) < 0) perr_exit("listen error" ); return n; } int Socket(int family, int type , int protocol) { int n; if ( (n = socket(family, type , protocol)) < 0) perr_exit("socket error" ); return n; } ssize_t Read(int fd, void *ptr, size_t nbytes) { ssize_t n; again: if ( (n = read (fd, ptr, nbytes)) == -1) { if (errno == EINTR) goto again; else return -1; } return n; } ssize_t Write(int fd, const void *ptr, size_t nbytes) { ssize_t n; again: if ( (n = write(fd, ptr, nbytes)) == -1) { if (errno == EINTR) goto again; else return -1; } return n; } int Close(int fd) { int n; if ((n = close(fd)) == -1) perr_exit("close error" ); return n; } ssize_t Readn(int fd, void *vptr, size_t n) { size_t nleft; ssize_t nread; char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nread = read (fd, ptr, nleft)) < 0) { if (errno == EINTR) nread = 0; else return -1; } else if (nread == 0) break ; nleft -= nread; ptr += nread; } return n - nleft; } ssize_t Writen(int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nwritten = write(fd, ptr, nleft)) <= 0) { if (nwritten < 0 && errno == EINTR) nwritten = 0; else return -1; } nleft -= nwritten; ptr += nwritten; } return n; } static ssize_t my_read(int fd, char *ptr) { static int read_cnt; static char *read_ptr; static char read_buf[100]; if (read_cnt <= 0) { again: if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) { if (errno == EINTR) goto again; return -1; } else if (read_cnt == 0) return 0; read_ptr = read_buf; } read_cnt--; *ptr = *read_ptr++; return 1; } ssize_t Readline(int fd, void *vptr, size_t maxlen) { ssize_t n, rc; char c, *ptr; ptr = vptr; for (n = 1; n < maxlen; n++) { if ( (rc = my_read(fd, &c)) == 1) { *ptr++ = c; if (c == '\n' ) break ; } else if (rc == 0) { *ptr = 0; return n - 1; } else return -1; } *ptr = 0; return n; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #ifndef __WRAP_H_ #define __WRAP_H_ void perr_exit (const char *s) ;int Accept (int fd, struct sockaddr *sa, socklen_t *salenptr) ;int Bind (int fd, const struct sockaddr *sa, socklen_t salen) ;int Connect (int fd, const struct sockaddr *sa, socklen_t salen) ;int Listen (int fd, int backlog) ;int Socket (int family, int type, int protocol) ;ssize_t Read (int fd, void *ptr, size_t nbytes) ;ssize_t Write (int fd, const void *ptr, size_t nbytes) ;int Close (int fd) ;ssize_t Readn (int fd, void *vptr, size_t n) ;ssize_t Writen (int fd, const void *vptr, size_t n) ;ssize_t my_read (int fd, char *ptr) ;ssize_t Readline (int fd, void *vptr, size_t maxlen) ;#endif
##多进程并发服务器
使用多进程并发服务器时要考虑以下几点: 1.父进程最大文件描述个数(父进程中需要close关闭accept返回的新文件描述符) 2.系统内创建进程个数(与内存大小相关) 3.进程创建过多是否降低整体服务性能(进程调度)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include <stdio.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <signal.h> #include <sys/wait.h> #include <sys/types.h> #include "wrap.h" #define MAXLINE 80 #define SERV_PORT 800 void do_sigchild (int num) { while (waitpid(0 , NULL , WNOHANG) > 0 ) ; } int main (void ) { struct sockaddr_in servaddr , cliaddr ; socklen_t cliaddr_len; int listenfd, connfd; char buf[MAXLINE]; char str[INET_ADDRSTRLEN]; int i, n; pid_t pid; struct sigaction newact ; newact.sa_handler = do_sigchild; sigemptyset(&newact.sa_mask); newact.sa_flags = 0 ; sigaction(SIGCHLD, &newact, NULL ); listenfd = Socket(AF_INET, SOCK_STREAM, 0 ); bzero(&servaddr, sizeof (servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); Bind(listenfd, (struct sockaddr *)&servaddr, sizeof (servaddr)); Listen(listenfd, 20 ); printf ("Accepting connections ...\n" ); while (1 ) { cliaddr_len = sizeof (cliaddr); connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); pid = fork(); if (pid == 0 ) { Close(listenfd); while (1 ) { n = Read(connfd, buf, MAXLINE); if (n == 0 ) { printf ("the other side has been closed.\n" ); break ; } printf ("received from %s at PORT %d\n" , inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof (str)), ntohs(cliaddr.sin_port)); for (i = 0 ; i < n; i++) buf[i] = toupper (buf[i]); Write(connfd, buf, n); } Close(connfd); return 0 ; } else if (pid > 0 ) { Close(connfd); } else perr_exit("fork" ); } Close(listenfd); return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <stdio.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #include "wrap.h" #define MAXLINE 80 #define SERV_PORT 6666 int main (int argc, char *argv[]) { struct sockaddr_in servaddr ; char buf[MAXLINE]; int sockfd, n; sockfd = Socket(AF_INET, SOCK_STREAM, 0 ); bzero(&servaddr, sizeof (servaddr)); servaddr.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1" , &servaddr.sin_addr); servaddr.sin_port = htons(SERV_PORT); Connect(sockfd, (struct sockaddr *)&servaddr, sizeof (servaddr)); while (fgets(buf, MAXLINE, stdin ) != NULL ) { Write(sockfd, buf, strlen (buf)); n = Read(sockfd, buf, MAXLINE); if (n == 0 ) { printf ("the other side has been closed.\n" ); break ; } else Write(STDOUT_FILENO, buf, n); } Close(sockfd); return 0 ; }
在使用线程模型开发服务器时需考虑以下问题: 1.调整进程内最大文件描述符上限 2.线程如有共享数据,考虑线程同步 3.服务于客户端线程退出时,退出处理。(退出值,分离态) 4.系统负载,随着链接客户端增加,导致其它线程不能及时得到CPU
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <stdio.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <pthread.h> #include "wrap.h" #define MAXLINE 80 #define SERV_PORT 6666 struct s_info { struct sockaddr_in cliaddr ; int connfd; }; void *do_work (void *arg) { int n,i; struct s_info *ts = (struct s_info*)arg; char buf[MAXLINE]; char str[INET_ADDRSTRLEN]; pthread_detach(pthread_self()); while (1 ) { n = Read(ts->connfd, buf, MAXLINE); if (n == 0 ) { printf ("the other side has been closed.\n" ); break ; } printf ("received from %s at PORT %d\n" , inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof (str)), ntohs((*ts).cliaddr.sin_port)); for (i = 0 ; i < n; i++) buf[i] = toupper (buf[i]); Write(ts->connfd, buf, n); } Close(ts->connfd); } int main (void ) { struct sockaddr_in servaddr , cliaddr ; socklen_t cliaddr_len; int listenfd, connfd; int i = 0 ; pthread_t tid; struct s_info ts [256]; listenfd = Socket(AF_INET, SOCK_STREAM, 0 ); bzero(&servaddr, sizeof (servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); Bind(listenfd, (struct sockaddr *)&servaddr, sizeof (servaddr)); Listen(listenfd, 20 ); printf ("Accepting connections ...\n" ); while (1 ) { cliaddr_len = sizeof (cliaddr); connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); ts[i].cliaddr = cliaddr; ts[i].connfd = connfd; pthread_create(&tid, NULL , do_work, (void *)&ts[i]); i++; } return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #include <stdio.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> #include "wrap.h" #define MAXLINE 80 #define SERV_PORT 6666 int main (int argc, char *argv[]) { struct sockaddr_in servaddr ; char buf[MAXLINE]; int sockfd, n; sockfd = Socket(AF_INET, SOCK_STREAM, 0 ); bzero(&servaddr, sizeof (servaddr)); servaddr.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1" , &servaddr.sin_addr); servaddr.sin_port = htons(SERV_PORT); Connect(sockfd, (struct sockaddr *)&servaddr, sizeof (servaddr)); while (fgets(buf, MAXLINE, stdin ) != NULL ) { Write(sockfd, buf, strlen (buf)); n = Read(sockfd, buf, MAXLINE); if (n == 0 ) printf ("the other side has been closed.\n" ); else Write(STDOUT_FILENO, buf, n); } Close(sockfd); return 0 ; }
##多路I/O转接服务器
原文链接: https://kettycode.github.io/2024/02/06/cpp/linux网络编程/socket编程1/
版权声明: 转载请注明出处.