文章目录
- 1、socket()函数
- 2、bind()函数
- 3、listen()
- 4、accept()
- 5、connect()
- 6、send()/write()
- 7、recv()/read()
- 8、套接字的关闭
- 9、TCP循环服务器模型
- 10、TCP多线程服务器
- 11、TCP多进程并发服务器
网络编程常用函数
socket()
创建套接字bind()
绑定本机地址和端口connect()
建立连接listen()
设置监听端口accept()
接受TCP连接recv(), read(), recvfrom()
数据接收send(), write(), sendto()
数据发送close(), shutdown()
关闭套接字
1、socket()函数
int socket (int domain, int type, int protocol);
- domain 是地址族
- PF_INET IPv4 Internet protocols ip(7) // internet 协议
- PF_UNIX IPv6 Internet protocols ipv6(7) // unix internal协议
- PF_NS Local communication unix(7) // Xerox NS协议
- PF_IMPLINK Low level packet interface packet(7)// Interface Message协议
- type // 套接字类型
- SOCK_STREAM // 流式套接字
- SOCK_DGRAM // 数据报套接字
- SOCK_RAW //原始套接字
- protocol 参数通常置为0
地址相关的数据结构
-
通用地址结构
-
struct sockaddr { u_short sa_family; // 地址族, AF_xxx char sa_data[14]; // 14字节协议地址 };
-
-
Internet协议地址结构
-
struct sockaddr_in { u_short sin_family; // 地址族, AF_INET,2 bytes u_short sin_port; // 端口,2 bytes struct in_addr sin_addr; // IPV4地址,4 bytes char sin_zero[8]; // 8 bytes unused,作为填充 };
-
-
IPv4地址结构
-
// internet address struct in_addr { in_addr_t s_addr; // u32 network address };
-
返回值
-
RETURN VALUE On success, a file descriptor for the new socket is returned. On error, -1 is returned, and errno is set appropriately.
成功时返回文件描述符,出错时返回为-1
2、bind()函数
int bind (int sockfd, struct sockaddr* addr, int addrLen);
-
sockfd
由socket() 调用返回 -
addr
是指向sockaddr_in
结构的指针,包含本机IP 地址和端口号(struct sockaddr
的结构体变量的地址)-
struct sockaddr_in
-
u_short sin_family // protocol family u_short sin_port // port number struct in_addr sin_addr //IP address (32-bits)4字节
-
-
addrLen
:sizeof (struct sockaddr_in)
(地址长度)
地址结构的一般用法:
-
定义一个
struct sockaddr_in
类型的变量并清空-
struct sockaddr_in myaddr; memset(&myaddr, 0, sizeof(myaddr));
-
-
填充地址信息
-
myaddr.sin_family = PF_INET; myaddr.sin_port = htons(8888); myaddr.sin_addr.s_addr = inet_addr(“192.168.1.100”);
-
-
将该变量强制转换为struct sockaddr类型在函数中使用
-
bind(listenfd, (struct sockaddr*)(&myaddr), sizeof(myaddr));
-
地址转换函数:
-
unsigned long inet_addr(char *address);
-
address
是以’\0’结尾的点分IPv4字符串。该函数返回32位的地址。如果字符串包含的不是合法的IP地址,则函数返回-1。例如:struct in_addr addr; addr.s_addr = inet_addr(" 192.168.1.100 ");
-
-
char* inet_ntoa(struct in_addr address);
address
是IPv4地址结构,函数返回一指向包含点分IP地址的静态存储区字符指针。如果错误则函数返回NULL
代码举例:
#include "net.h"
int main(void)
{
int fd = -1;
struct sockaddr_in sin;
/* 1. 创建socket fd*/
if( (fd = socket(AF_INET,SOCK_STREAM, 0)) < 0) {
perror("socket");
exit(1);
}
/*2. 绑定 */
/*2.1 填充struct sockaddr_in结构体变量 */
bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT); //网络字节序的端口号
#if 0
sin.sin_addr.s_addr = inet_addr(SERV_IP_ADDR);
#else
if( inet_pton(AF_INET, SERV_IP_ADDR, (void *)&sin.sin_addr) != 1) {
perror("inet_pton");
exit(1);
}
#endif
/*2.2 绑定 */
if( bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0 ) {
perror("bind");
exit(1);
}
}
如果是IPV6的编程,要使用struct sockddr_in6
结构体(详细情况请参考man 7 ipv6),通常更通用的方法可以通过struct sockaddr_storage
来编程
3、listen()
int listen (int sockfd, int backlog);
把主动套接字变成被动套接字
sockfd
:监听连接的套接字(通过socket()函数拿到的fd)backlog
同时允许几路客户端和服务器进行正在连接的过程(正在三次握手)
一般填5
,测试得知,ARM最大为8
- 指定了正在等待连接的最大队列长度,它的作用在于处理可能同时出现的几个连接请求。
- DoS(拒绝服务)攻击即利用了这个原理,非法的连接占用了全部的连接数,造成正常的连接请求被拒绝。
- 返回值: 0 或 -1
- 完成
listen()
调用后,socket变成了监听socket(listening socket)
。
内核中服务器的套接字fd会维护2个链表:
- 正在三次握手的的客户端链表(数量=2*backlog+1)
- 已经建立好连接的客户端链表(已经完成3次握手分配好了newfd)
- 比如:
listen(fd, 5)
; //表示系统允许11(=2*5+1)个客户端同时进行三次握手
返回值:
-
RETURN VALUE On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
4、accept()
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) ;
(阻塞等待客户端连接请求)
-
返回值:已建立好连接的套接字或-1
-
头文件
-
#include <sys/types.h> #include <sys/socket.h>
-
-
sockfd
: 监听套接字(经过前面socket()创建并通过bind(),listen()设置过的fd) -
addr
: 对方地址(获取连接过来的客户的信息) -
addrlen
:地址长度(获取连接过来的客户的信息)
listen()
和accept()
是TCP服务器端使用的函数
返回值
-
RETURN VALUE On success, these system calls return a nonnegative integer that is a descriptor for the accepted socket. On error, -1 is returned, and errno is set appropriately.
-
成功时返回已经建立好连接的新的newfd
代码举例:
创建文件net.h
#ifndef __MAKEU_NET_H__
#define __MAKEU_NET_H__
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#define SERV_PORT 5001
#define SERV_IP_ADDR "192.168.7.246"
#define BACKLOG 5
#define QUIT_STR "quit"
#endif
创建服务器文件server.c
#include "net.h"
int main (void)
{
int fd = -1;
struct sockaddr_in sin;
/* 1. 创建socket fd */
if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
perror ("socket");
exit (1);
}
/*2. 绑定 */
/*2.1 填充struct sockaddr_in结构体变量 */
bzero (&sin, sizeof (sin));
sin.sin_family = AF_INET;
sin.sin_port = htons (SERV_PORT); //网络字节序的端口号
/*优化1: 让服务器程序能绑定在任意的IP上 */
#if 1
sin.sin_addr.s_addr = htonl (INADDY_ANY);
#else
if (inet_pton (AF_INET, SERV_IP_ADDR, (void *) &sin.sin_addr) != 1) {
perror ("inet_pton");
exit (1);
}
#endif
/*2.2 绑定 */
if (bind (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
perror ("bind");
exit (1);
}
/*3. 调用listen()把主动套接字变成被动套接字 */
if (listen (fd, BACKLOG) < 0) {
perror ("listen");
exit (1);
}
printf ("Server starting....OK!\n");
int newfd = -1;
/*4. 阻塞等待客户端连接请求 */
#if 0
newfd = accept (fd, NULL, NULL);
if (newfd < 0) {
perror ("accept");
exit (1);
}
#else
/*优化2:通过程序获取刚建立连接的socket的客户端的IP地址和端口号 */
struct sockaddr_in cin;
socklen_t addrlen = sizeof (cin);
if ((newfd = accept (fd, (struct sockaddr *) &cin, &addrlen)) < 0) {
perror ("accept");
exit (1);
}
char ipv4_addr[16];
if (!inet_ntop (AF_INET, (void *) &cin.sin_addr, ipv4_addr, sizeof (cin))) {
perror ("inet_ntop");
exit (1);
}
printf ("Clinet(%s:%d) is connected!\n", ipv4_addr, ntons (cin.sin_port));
#endif
/*5. 读写 */
//..和newfd进行数据读写
int ret = -1;
char buf[BUFSIZ];
while (1) {
bzero (buf, BUFSIZ);
do {
ret = read (newfd, buf, BUFSIZ - 1);
} while (ret < 0 && EINTR == errno);
if (ret < 0) {
perror ("read");
exit (1);
}
if (!ret) { //对方已经关闭
break;
}
printf ("Receive data: %s\n", buf);
if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) { //用户输入了quit字符
printf ("Client is exiting!\n");
break;
}
}
close (newfd);
close (fd);
return 0;
}
5、connect()
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
(客户端的连接函数)
-
返回值:0 或 -1
-
头文件:
-
#include <sys/types.h> #include <sys/socket.h>
-
-
sockfd
:socket
返回的文件描述符(通过socket()
函数拿到的fd) -
serv_addr
: 服务器端的地址信息(struct sockaddr
的结构体变量的地址) -
addrlen
:serv_addr
的长度(地址长度)
connect()
是客户端使用的系统调用。
返回值:
-
RETURN VALUE If the connection or binding succeeds, zero is returned. On error, -1 is returned, and errno is set appropriately.
代码举例:创建文件client1.c
#include "net.h"
int main (void)
{
int fd = -1;
struct sockaddr_in sin;
/* 1. 创建socket fd */
if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
perror ("socket");
exit (1);
}
/*2.连接服务器 */
/*2.1 填充struct sockaddr_in结构体变量 */
bzero (&sin, sizeof (sin));
sin.sin_family = AF_INET;
sin.sin_port = htons (SERV_PORT); //网络字节序的端口号
#if 0
sin.sin_addr.s_addr = inet_addr (SERV_IP_ADDR);
#else
if (inet_pton (AF_INET, SERV_IP_ADDR, (void *) &sin.sin_addr) != 1) {
perror ("inet_pton");
exit (1);
}
#endif
if (connect (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
perror ("connect");
exit (1);
}
printf ("Client staring...OK!\n");
/*3. 读写数据 */
char buf[BUFSIZ];
int ret = -1;
while (1) {
bzero (buf, BUFSIZ);
if (fgets (buf, BUFSIZ - 1, stdin) == NULL) {
continue;
}
do {
ret = write (fd, buf, strlen (buf));
} while (ret < 0 && EINTR == errno);
if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) { //用户输入了quit字符
printf ("Client is exiting!\n");
break;
}
}
/*4.关闭套接字 */
close (fd);
}
6、send()/write()
ssize_t send(int socket, const void *buffer, size_t length, int flags);
- 返回值:
- 成功:实际发送的字节数
- 失败:-1, 并设置errno
- 头文件:
#include <sys/socket.h>
buffer
: 发送缓冲区首地址length
: 发送的字节数flags
: 发送方式(通常为0)
ssize_t write(int fd, const void *buf, size_t count);
- send()比write多一个参数flags:
- flags:
- 一般填写0,此时与write()作用一样
- MSG_DONTWAIT:Enables nonblocking operation(非阻塞标志)
- MSG_OOB:用于发送TCP类型的带外数据(out-of-band)
- flags:
read()和write()经常会代替recv()和send(),通常情况下,看程序员的偏好
使用read()/write()和recv()/send()时最好统一
7、recv()/read()
ssize_t recv(int socket, const void *buffer, size_t length, int flags);
- 返回值:
- 成功:实际接收的字节数
- 失败:-1, 并设置errno
- 头文件:
#include <sys/socket.h>
- buffer : 发送缓冲区首地址
- length : 发送的字节数
- flags : 接收方式(通常为0)
ssize_t read(int fd, voide *buf, size_t count);
flags:
- 一般填写0,此时和read()作用相同
特殊标志:
- MSG_DONTWAIT:Enables nonblocking operation(非阻塞标志)
- MSG_OOB:用于发送TCP类型的带外数据(out-of-band)
- MSG_PEEK:
8、套接字的关闭
int close(int sockfd);
- 关闭双向通讯
int shutdown(int sockfd, int howto);
- TCP连接是双向的(是可读写的),当我们使用close时,会把读写通道都关闭,有时侯我们希望只关闭一个方向,这个时候我们可以使用shutdown。
- 针对不同的howto,系统回采取不同的关闭方式。
shutdown()的howto参数
- howto = 0
- 关闭读通道,但是可以继续往套接字写数据。
- howto = 1
- 和上面相反,关闭写通道。只能从套接字读取数据。
- howto = 2
- 关闭读写通道,和close()一样
9、TCP循环服务器模型
socket(...);
bind(...);
listen(...);
while(1)
{
accept(...);
process(...);
close(...);
}
TCP服务器一般很少用
10、TCP多线程服务器
socket(...);
bind(...);
listen(...);
while(1)
{
accpet(...);
if((pthread_create(...))!==-1) {
process(...);
close(...);
exit(...);
}
close(...);
}
多线程服务器是对多进程的服务器的改进
代码举例:net.h
#ifndef __MAKEU_NET_H__
#define __MAKEU_NET_H__
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#define SERV_PORT 5001
#define SERV_IP_ADDR "192.168.7.246"
#define BACKLOG 5
#define QUIT_STR "quit"
#endif
server.c
#include <pthread.h>
#include "net.h"
void cli_data_handle (void *arg);
int main (void)
{
int fd = -1;
struct sockaddr_in sin;
/* 1. 创建socket fd */
if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
perror ("socket");
exit (1);
}
/*优化4: 允许绑定地址快速重用 */
int b_reuse = 1;
setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));
/*2. 绑定 */
/*2.1 填充struct sockaddr_in结构体变量 */
bzero (&sin, sizeof (sin));
sin.sin_family = AF_INET;
sin.sin_port = htons (SERV_PORT); //网络字节序的端口号
/*优化1: 让服务器程序能绑定在任意的IP上 */
#if 1
sin.sin_addr.s_addr = htonl (INADDR_ANY);
#else
if (inet_pton (AF_INET, SERV_IP_ADDR, (void *) &sin.sin_addr) != 1) {
perror ("inet_pton");
exit (1);
}
#endif
/*2.2 绑定 */
if (bind (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
perror ("bind");
exit (1);
}
/*3. 调用listen()把主动套接字变成被动套接字 */
if (listen (fd, BACKLOG) < 0) {
perror ("listen");
exit (1);
}
printf ("Server starting....OK!\n");
int newfd = -1;
/*4. 阻塞等待客户端连接请求 */
/* 优化: 用多进程/多线程处理已经建立号连接的客户端数据 */
pthread_t tid;
struct sockaddr_in cin;
socklen_t addrlen = sizeof (cin);
while (1) {
if ((newfd = accept (fd, (struct sockaddr *) &cin, &addrlen)) < 0) {
perror ("accept");
exit (1);
}
char ipv4_addr[16];
if (!inet_ntop (AF_INET, (void *) &cin.sin_addr, ipv4_addr, sizeof (cin))) {
perror ("inet_ntop");
exit (1);
}
printf ("Clinet(%s:%d) is connected!\n", ipv4_addr, htons (cin.sin_port));
pthread_create (&tid, NULL, (void *) cli_data_handle, (void *) &newfd);
}
close (fd);
return 0;
}
void cli_data_handle (void *arg)
{
int newfd = *(int *) arg;
printf ("handler thread: newfd =%d\n", newfd);
//..和newfd进行数据读写
int ret = -1;
char buf[BUFSIZ];
while (1) {
bzero (buf, BUFSIZ);
do {
ret = read (newfd, buf, BUFSIZ - 1);
} while (ret < 0 && EINTR == errno);
if (ret < 0) {
perror ("read");
exit (1);
}
if (!ret) { //对方已经关闭
break;
}
printf ("Receive data: %s\n", buf);
if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) { //用户输入了quit字符
printf ("Client(fd=%d) is exiting!\n", newfd);
break;
}
}
close (newfd);
}
client.c
/*./client serv_ip serv_port */
#include "net.h"
void usage (char *s)
{
printf ("\n%s serv_ip serv_port", s);
printf ("\n\t serv_ip: server ip address");
printf ("\n\t serv_port: server port(>5000)\n\n");
}
int main (int argc, char **argv)
{
int fd = -1;
int port = -1;
struct sockaddr_in sin;
if (argc != 3) {
usage (argv[0]);
exit (1);
}
/* 1. 创建socket fd */
if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
perror ("socket");
exit (1);
}
port = atoi (argv[2]);
if (port < 5000) {
usage (argv[0]);
exit (1);
}
/*2.连接服务器 */
/*2.1 填充struct sockaddr_in结构体变量 */
bzero (&sin, sizeof (sin));
sin.sin_family = AF_INET;
sin.sin_port = htons (port); //网络字节序的端口号
#if 0
sin.sin_addr.s_addr = inet_addr (SERV_IP_ADDR);
#else
if (inet_pton (AF_INET, argv[1], (void *) &sin.sin_addr) != 1) {
perror ("inet_pton");
exit (1);
}
#endif
if (connect (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
perror ("connect");
exit (1);
}
printf ("Client staring...OK!\n");
/*3. 读写数据 */
char buf[BUFSIZ];
int ret = -1;
while (1) {
bzero (buf, BUFSIZ);
if (fgets (buf, BUFSIZ - 1, stdin) == NULL) {
continue;
}
do {
ret = write (fd, buf, strlen (buf));
} while (ret < 0 && EINTR == errno);
if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) { //用户输入了quit字符
printf ("Client is exiting!\n");
break;
}
}
/*4.关闭套接字 */
close (fd);
}
11、TCP多进程并发服务器
socket(...);
bind(...);
listen(...);
while(1){
accpet(...);
if(fork(...) == 0)
{
process(...);
close(...);
exit(...);
}
close(...);
}
TCP并发服务器的思想是每一个客户机的请求并不由服务器直接处理,而是由服务器创建一个子进程来处理。
代码举例:创建文件net.h
#ifndef __MAKEU_NET_H__
#define __MAKEU_NET_H__
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#define SERV_PORT 5001
#define SERV_IP_ADDR "192.168.7.246"
#define BACKLOG 5
#define QUIT_STR "quit"
#endif
server.c
#include <pthread.h>
#include <signal.h>
#include "net.h"
void cli_data_handle (void *arg);
void sig_child_handle(int signo)
{
if(SIGCHLD == signo) {
waitpid(-1, NULL, WNOHANG);
}
}
int main (void)
{
int fd = -1;
struct sockaddr_in sin;
signal(SIGCHLD, sig_child_handle);
/* 1. 创建socket fd */
if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
perror ("socket");
exit (1);
}
/*优化4: 允许绑定地址快速重用 */
int b_reuse = 1;
setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));
/*2. 绑定 */
/*2.1 填充struct sockaddr_in结构体变量 */
bzero (&sin, sizeof (sin));
sin.sin_family = AF_INET;
sin.sin_port = htons (SERV_PORT); //网络字节序的端口号
/*优化1: 让服务器程序能绑定在任意的IP上 */
#if 1
sin.sin_addr.s_addr = htonl (INADDR_ANY);
#else
if (inet_pton (AF_INET, SERV_IP_ADDR, (void *) &sin.sin_addr) != 1) {
perror ("inet_pton");
exit (1);
}
#endif
/*2.2 绑定 */
if (bind (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
perror ("bind");
exit (1);
}
/*3. 调用listen()把主动套接字变成被动套接字 */
if (listen (fd, BACKLOG) < 0) {
perror ("listen");
exit (1);
}
printf ("Server starting....OK!\n");
int newfd = -1;
/*4. 阻塞等待客户端连接请求 */
struct sockaddr_in cin;
socklen_t addrlen = sizeof (cin);
while(1) {
pid_t pid = -1;
if ((newfd = accept (fd, (struct sockaddr *) &cin, &addrlen)) < 0) {
perror ("accept");
break;
}
/*创建一个子进程用于处理已建立连接的客户的交互数据*/
if((pid = fork()) < 0) {
perror("fork");
break;
}
if(0 == pid) { //子进程中
close(fd);
char ipv4_addr[16];
if (!inet_ntop (AF_INET, (void *) &cin.sin_addr, ipv4_addr, sizeof (cin))) {
perror ("inet_ntop");
exit (1);
}
printf ("Clinet(%s:%d) is connected!\n", ipv4_addr, ntohs(cin.sin_port));
cli_data_handle(&newfd);
return 0;
} else { //实际上此处 pid >0, 父进程中
close(newfd);
}
}
close (fd);
return 0;
}
void cli_data_handle (void *arg)
{
int newfd = *(int *) arg;
printf ("Child handling process: newfd =%d\n", newfd);
//..和newfd进行数据读写
int ret = -1;
char buf[BUFSIZ];
while (1) {
bzero (buf, BUFSIZ);
do {
ret = read (newfd, buf, BUFSIZ - 1);
} while (ret < 0 && EINTR == errno);
if (ret < 0) {
perror ("read");
exit (1);
}
if (!ret) { //对方已经关闭
break;
}
printf ("Receive data: %s\n", buf);
if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) { //用户输入了quit字符
printf ("Client(fd=%d) is exiting!\n", newfd);
break;
}
}
close (newfd);
}
client.c
/*./client serv_ip serv_port */
#include "net.h"
void usage (char *s)
{
printf ("\n%s serv_ip serv_port", s);
printf ("\n\t serv_ip: server ip address");
printf ("\n\t serv_port: server port(>5000)\n\n");
}
int main (int argc, char **argv)
{
int fd = -1;
int port = -1;
struct sockaddr_in sin;
if (argc != 3) {
usage (argv[0]);
exit (1);
}
/* 1. 创建socket fd */
if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
perror ("socket");
exit (1);
}
port = atoi (argv[2]);
if (port < 5000) {
usage (argv[0]);
exit (1);
}
/*2.连接服务器 */
/*2.1 填充struct sockaddr_in结构体变量 */
bzero (&sin, sizeof (sin));
sin.sin_family = AF_INET;
sin.sin_port = htons (port); //网络字节序的端口号
#if 0
sin.sin_addr.s_addr = inet_addr (SERV_IP_ADDR);
#else
if (inet_pton (AF_INET, argv[1], (void *) &sin.sin_addr) != 1) {
perror ("inet_pton");
exit (1);
}
#endif
if (connect (fd, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
perror ("connect");
exit (1);
}
printf ("Client staring...OK!\n");
/*3. 读写数据 */
char buf[BUFSIZ];
int ret = -1;
while (1) {
bzero (buf, BUFSIZ);
if (fgets (buf, BUFSIZ - 1, stdin) == NULL) {
continue;
}
do {
ret = write (fd, buf, strlen (buf));
} while (ret < 0 && EINTR == errno);
if (!strncasecmp (buf, QUIT_STR, strlen (QUIT_STR))) { //用户输入了quit字符
printf ("Client is exiting!\n");
break;
}
}
/*4.关闭套接字 */
close (fd);
}