一、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;
}