TCP客户端Linux网络编程设计详解
一、TCP 客户端设计流程
TCP客户端模式的程序设计流程主要分为:套接字初始化( socket()函数),连接目标网络服务器 (connect()函数),向服务器端写入数据(write()函数)

1、socket() 函数
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
函数socket()的参数domain用于设置网络通信的域,函数socket()根据这个参数选择通信协议的族。通信协议族在文件sys/socket.h 中定义。


int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
2、connect() 函数原型
#include <sys/types.h> /* See NOTES */#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
(1)包裹函数
//connect包裹函数
void Connect(int fd, const struct sockaddr*addr,socklen_t addrlen)
{int ret;while (1){ret = connect(fd, addr, addrlen);if (-1 == ret){dbgout("connect error\n");perror("eror");continue;}break;}
}
3、数据的发送
1) write()函数用于发送数据,函数原型如下:
#include <sys/types.h>
#include <sys/socket.h>ssize_t write(int sockfd, const void *buf, size_t len);
参数:
sockfd:TCP Socket 描述符。
buf:要发送的数据缓冲区。
count:要发送的字节数。
示例代码:
write(fd, "hello", sizeof("hello"));
2) send()函数, 原型如下:
#include <sys/types.h>
#include <sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t len, int flags);
参数:
sockfd:TCP Socket 描述符。
buf:要发送的数据缓冲区。
len:要发送的字节数。
flags:可选的标志参数,用于控制发送行为,如 MSG_DONTWAIT、MSG_NOSIGNAL 等。
示例代码:
send(fd, "hello", sizeof("hello"), MSG_DONTWAIT)
4、数据的接收
读取客户端的数据可以使用以下三个函数:
1)read() 数据接收函数
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
read()函数从套接字sockfd中接收数据放到缓冲区buf中,buf的长度为len。 第1个参数sockfd是套接口文件描述符,它是由系统调用socket()返回的。第2个参数buf是一个指针, 指向接收网络数据的缓冲区。第3个参数len表示接收缓冲区的大小,以字节为单位。
使用示例代码:
char buf[1024];
//读取数据
memset(buf, 0, sizeof(buf));
int ret;
ret = read(fd, buf, sizeof(buf)-1); //阻塞
//如果主动断开了连接,read函数解除阻塞,并且返回0
if (0 == ret)
{close(cli_fd);return NULL;
}printf("cli: %d, %s\n", cli_fd, buf)
2)recv() 数据接收函数
recv()函数用于接收数据,函数原型如下:
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
前三个跟read函数一致,recv()函数的参数flags用于设置接收数据的方式,可选择的值及含义在下表中列出(flags 的值可以是表中值的按位或生成的复合值)
上表中的值的具体说明:
MSG_DONTWAIT:这个标志将单个IO操作设为非阻塞方式,而不需要在套接字 上打开非阻塞标志,执行IO操作,然后关闭非阻塞标志。
MSG_ERRQUEUE:该错误的传输依赖于所使用的协议。
MSG_OOB:这个标志可以接收带外数据,而不是接收一般数据。
MSG_PEEK:这个标志用于查看可读的数据,在recv()函数执行后,内核不会将这些数据丢弃。 MSG_TRUNC:在接收数据后,如果用户的缓冲区大小不足以完全复制缓冲区中的数据,则将 数据截断,仅靠复制用户缓冲区大小的数据。其他的数据会被丢弃。
MSG_WAITALL:这个标志告诉内核在没有读到请求的字节数之前不使读操作返回。
示例代码:
char buf[1024];
//读取数据
memset(buf, 0, sizeof(buf));
int ret;
ret = recv(cli_fd, buf, sizeof(buf)-1,MSG_DONTWAIT);
if (0 == ret)
{close(cli_fd);return NULL;
}printf("cli: %d, %s\n", cli_fd, buf)
3)recvfrom() 数据接收函数
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen)
第四个参数保存接收的客户端的相关信息,但是因为TCP是基于连接的传输协议,在调用accept函数 时就能够获取客户端的信息,所以recvfrom一般用于udp通信。
7、示例TCP客户端编程代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>#define dbgout(arg...) \do{ \char b__[1024]; \sprintf(b__, arg);\fprintf(stdout, "[%s,%s,%d] %s", __FILE__, __func__, __LINE__, b__); \} while (0)//connect包裹函数
void Connect(int fd, const struct sockaddr*addr,socklen_t addrlen)
{int ret;while (1){ret = connect(fd, addr, addrlen);if (-1 == ret){dbgout("connect error\n");perror("eror");continue;}break;}
}int main(int argc, char* argv[])
{//初始化一个socket套接字int fd;fd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == fd){dbgout("socket error\n");perror("erro");return 0;}//连接服务器struct sockaddr_in serv;serv.sin_family = AF_INET;serv.sin_addr.s_addr = inet_addr("172.29.98.213");serv.sin_port = htons(8888);//和服务器建立连接Connect(fd, (struct sockaddr*)&serv, sizeof(struct sockaddr));char buf[128];while (1){//向服务端发送数据write(fd, "hello", 6);//读取服务端发送的数据memset(buf, 0, sizeof(buf));int ret = read(fd, buf, sizeof(buf) - 1);if (ret > 0){dbgout("read: %s\n", buf);}sleep(1);}close(fd);return 0;
}