一文學(xué)會(huì) | linux socket編程----TCP
TCP 是基于連接的數(shù)據(jù)流的協(xié)議,先建立連接再進(jìn)行通信,而且在通信過程中會(huì)檢查數(shù)據(jù)是否發(fā)送成功。優(yōu)點(diǎn)就是保證數(shù)據(jù)的完整性和準(zhǔn)確性,缺點(diǎn)就是效率較低。
TCP的實(shí)現(xiàn):
服務(wù)器
1. 創(chuàng)建一個(gè)socket
int socket(int domain, int type, int protocol);
2. 準(zhǔn)備通信地址
struct sockaddr_in // ipv4地址結(jié)構(gòu)體{ short sin_family; // 保存地址協(xié)議類型 AF_INET short sin_port; // 保存端口號 struct in_addr sin_addr; // 保存你需要綁定的ip地址 } struct in_addr{ in_addr_t s_addr; //最終存放大端序ipv4地址的變量 }
在網(wǎng)絡(luò)通信中,本地通常使用小端格式存放數(shù)據(jù),網(wǎng)絡(luò)路由通常使用大端格式存放數(shù)據(jù),所以需要格式的轉(zhuǎn)換:
本地轉(zhuǎn)網(wǎng)絡(luò):
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
網(wǎng)絡(luò)轉(zhuǎn)本地:
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
3. 綁定ip和端口號
int bind(int sockfd, const struct sockaddr *addr, ?socklen_t addrlen);
4. 監(jiān)聽客戶端的連接
int listen(int sockfd, int backlog); // 注:該函數(shù)不阻塞
5. 等待客戶端連接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); // 注:阻塞函數(shù),有客戶端連接才返回
6. 與客戶端進(jìn)行通信(read/write recv/send)
// 接收ssize_t recv(int sockfd, void *buf, size_t len, int flags);// 發(fā)送ssize_t send(int sockfd, const void *buf, size_t len, int flags);
7. 不再通信關(guān)閉新socket描述符,不在監(jiān)聽關(guān)閉監(jiān)聽的socket描述符
int close(int fd);
示例代碼
int main(){ int tcpsock; int newsock; int ret; char buf[100]; // 定義ipv4地址結(jié)構(gòu)體變量 struct sockaddr_in bindaddr; bzero(&bindaddr, sizeof(bindaddr)); bindaddr.sin_family = AF_INET; bindaddr.sin_port = htons(10000); //服務(wù)器自己的端口號 bindaddr.sin_addr.s_addr = inet_addr("192.168.11.3"); //服務(wù)器自己的ip struct sockaddr_in clientaddr; bzero(&clientaddr, sizeof(clientaddr)); int addrsize = sizeof(clientaddr); // 創(chuàng)建套接字 tcpsock = socket(AF_INET, SOCK_STREAM, 0); if(tcpsock == -1) { perror("創(chuàng)建套接字失敗!"); return -1; } //綁定ip和端口號 ret = bind(tcpsock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)); if(ret == -1) { perror("綁定失敗"); return -1; } //監(jiān)聽 ret = listen(tcpsock, 5); if(ret == -1) { perror("監(jiān)聽失敗"); return -1; } // printf("服務(wù)器在沒有客戶端連接的情況下,阻塞在accept!\n"); // 接受客戶端的連接請求 newsock = accept(tcpsock, (struct sockaddr *)&clientaddr, &addrsize); if(newsock == -1) { perror("接受客戶端的連接請求失敗"); return -1; } // printf("服務(wù)器的代碼中產(chǎn)生的舊套接字:%d\n", tcpsock); // printf("服務(wù)器的代碼中產(chǎn)生的新套接字:%d\n", newsock); // 讀取客戶端發(fā)送過來的信息 while(1) { bzero(buf, 100); read(newsock, buf, 100); printf("客戶端發(fā)送過來的信息:%s\n", buf); }}
客戶端
1. 創(chuàng)建一個(gè)socket
2. 準(zhǔn)備通信地址
3. 連接服務(wù)器
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);// 和 bind()的參數(shù)一樣
4. 與服務(wù)器進(jìn)行通信
5. 不再通信關(guān)閉socket描述符
示例代碼
int main(){ int tcpsock; int ret; char buf[100]; // 定義ipv4地址結(jié)構(gòu)體變量 struct sockaddr_in bindaddr; bzero(&bindaddr, sizeof(bindaddr)); bindaddr.sin_family = AF_INET; bindaddr.sin_port = htons(10086); // 自己指定一個(gè)端口號 bindaddr.sin_addr.s_addr = inet_addr("192.168.11.3"); // 綁定自己的ip struct sockaddr_in serveraddr; bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(10000); // 服務(wù)器端口號 serveraddr.sin_addr.s_addr = inet_addr("192.168.11.3"); // 服務(wù)器的ip // 創(chuàng)建套接字 tcpsock = socket(AF_INET, SOCK_STREAM, 0); if(tcpsock == -1) { perror("創(chuàng)建套接字失敗!\n"); return -1; } // 連接服務(wù)器 ret = connect(tcpsock, (struct sockaddr *)&serveraddr, sizeof(serveraddr)); if(ret == -1) { perror("連接服務(wù)器失敗"); return -1; } // 發(fā)送信息給服務(wù)器 while(1) { bzero(buf, 100); printf("請輸入要發(fā)送給服務(wù)器的信息!\n"); scanf("%s", buf); write(tcpsock, buf, strlen(buf)); }}