日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

C语言网络编程:TCP客户端实现

發(fā)布時(shí)間:2023/11/27 生活经验 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C语言网络编程:TCP客户端实现 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

          • 客戶端通信步驟
          • 為什么客戶端沒有bind和listen
          • 客戶端connect函數(shù)介紹
          • 局域網(wǎng)內(nèi)客戶端和服務(wù)器通信代碼實(shí)例

客戶端通信步驟

根據(jù)基本TCP網(wǎng)絡(luò)通信編程模型

我們可以知道客戶端的實(shí)現(xiàn)主要有幾個(gè)步驟

  1. socket創(chuàng)建客戶端通信的套接字文件,并指定通信的協(xié)議族和數(shù)據(jù)類型
  2. 使用connect主動(dòng)向服務(wù)器發(fā)起連接請(qǐng)求,與服務(wù)器的accept實(shí)現(xiàn)三次握手建立連接。連接成功之后客戶端可以通過socket返回的文件描述符進(jìn)行通信 ,服務(wù)器使用accept返回的通信文件描述符進(jìn)行通信
  3. 使用send/recv和服務(wù)器 發(fā)送接收數(shù)據(jù)進(jìn)行通信
  4. 通信結(jié)束之后斷開連接close或者shutdown
為什么客戶端沒有bind和listen

關(guān)于bind函數(shù)的功能,是為了綁定ip和端口號(hào),即通信使用固定的ip和端口號(hào)進(jìn)行通信。這樣的要求僅僅是針對(duì)tcp服務(wù)器而言的,對(duì)于多個(gè)客戶端來說,這樣的bind操作是不需要的。如果指定bind也是可以通信,但是并不合理。在通信過程中只需要使用自己的ip和默認(rèn)分配的端口號(hào)即可。客戶端默認(rèn)分配的端口范圍為49152~65535

關(guān)于listen函數(shù),對(duì)于客戶端程序,它永遠(yuǎn)是主動(dòng)連接請(qǐng)求的發(fā)起者,沒有被動(dòng)監(jiān)聽別人連接的需求。所以這里不需要將主動(dòng)通信文件描述符轉(zhuǎn)換為被動(dòng)通信文件描述符。

客戶端connect函數(shù)介紹
  • 頭文件:#include <sys/socket.h>
  • 函數(shù)使用:int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
  • 函數(shù)功能: 向服服務(wù)器發(fā)起主動(dòng)連接請(qǐng)求
  • 返回值:成功0;失敗 -1,并設(shè)置errno。
  • 函數(shù)參數(shù):
    a. sockfd socket返回的套接字文件描述符
    b. addrlen addr結(jié)構(gòu)體的大小
    c. addr 用于設(shè)置想要連接的服務(wù)器的ip和端口號(hào)
    注意:如果局域網(wǎng)內(nèi)部通信,則ip和端口號(hào)可以直接填寫服務(wù)器的ip和端口號(hào),但是如果是跨網(wǎng)通信,IP必須是服務(wù)器所在路由器的公網(wǎng)ip
    為了方便設(shè)置struct sockaddr結(jié)構(gòu)體的內(nèi)容,我們同樣使用sockaddr_in結(jié)構(gòu)體進(jìn)行設(shè)置
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(6789);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");cfd = connect(sockfd,(struct sockaddr *)&addr,sizeof(addr));
    
局域網(wǎng)內(nèi)客戶端和服務(wù)器通信代碼實(shí)例

一下代碼是在本機(jī)測(cè)試,大家可以測(cè)試局域網(wǎng)內(nèi)以及不同網(wǎng)段的通信,不過需要注意IP地址的填寫,跨網(wǎng)段通信中ip的填寫需要保證是服務(wù)器所在路由器的公網(wǎng)ip。
server.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>#define IP "192.168.102.174"
#define PORT 7000typedef struct data {char name[30];unsigned int num;
}Data;
void print_err(char *str, int line, int err_no) {printf("%d, %s :%s\n",line,str,strerror(err_no));_exit(-1);
}
int cfd = -1;//子線程,處理數(shù)據(jù)的接收
void *receive(void *pth_arg) {int ret = 0;Data stu_data = {0};while(1) {bzero(&stu_data, sizeof(stu_data));ret = recv(cfd, &stu_data, sizeof(stu_data),0);	if (-1 == ret) {print_err("recv failed",__LINE__,errno);}else if (ret > 0)printf("student number = %d student name = %s \n",ntohl(stu_data.num),stu_data.name);}
}//接收SIGINT信號(hào),從而關(guān)閉通信描述符。
void sig_fun(int signo) {if (signo == SIGINT) {//close(cfd);shutdown(cfd,SHUT_RDWR);_exit(0);}
}int main()
{int skfd = -1, ret = -1;skfd = socket(AF_INET, SOCK_STREAM, 0);if ( -1 == skfd) {print_err("socket failed",__LINE__,errno);}struct sockaddr_in addr;addr.sin_family = AF_INET; //設(shè)置tcp協(xié)議族addr.sin_port = htons(PORT); //設(shè)置端口號(hào)addr.sin_addr.s_addr = inet_addr(IP); //設(shè)置ip地址ret = bind(skfd, (struct sockaddr*)&addr, sizeof(addr));if ( -1 == ret) {print_err("bind failed",__LINE__,errno);}ret = listen(skfd, 3);if ( -1 == ret ) {print_err("listen failed", __LINE__, errno);}/*被動(dòng)監(jiān)聽客戶端發(fā)起的tcp連接請(qǐng)求,三次握手后連接建立成功*/struct sockaddr_in caddr = {0};int csize = sizeof(caddr);cfd = accept(skfd, (struct sockaddr*)&caddr, &csize);if (-1 == cfd) {print_err("accept failed", __LINE__, errno);}//打印連接后接收到的客戶端發(fā)送過來的ip和端口號(hào)printf("cport = %d, caddr = %s\n", ntohs(caddr.sin_port),inet_ntoa(caddr.sin_addr));創(chuàng)建子線程用于接收數(shù)據(jù)pthread_t id;ret = pthread_create(&id,NULL,receive,NULL);if (-1 == ret) print_err("pthread_create failed", __LINE__, errno);//用于處理信號(hào)函數(shù),處理SIGINT的信號(hào)signal(SIGINT,sig_fun);Data std_data = {0};while (1) {bzero(&std_data, sizeof(std_data));printf("stu name:\n");scanf("%s",std_data.name);//對(duì)于int型的需要將主機(jī)端序轉(zhuǎn)換為網(wǎng)絡(luò)端序,這里轉(zhuǎn)成long型。printf("stu num:\n");scanf("%d",&std_data.num);std_data.num = htonl(std_data.num);將數(shù)據(jù)std_data強(qiáng)制類型轉(zhuǎn)換后發(fā)送ret = send(cfd, (void *)&std_data,sizeof(std_data),0);if ( -1 == ret) {print_err("accept failed", __LINE__, errno);}	}return 0;
}

client.c
客戶端的代碼和服務(wù)器端主要差異在連接上,客戶端只需要初始化一下socket即可主動(dòng)發(fā)起連接請(qǐng)求,服務(wù)端在初始化socket之后需要綁定ip和端口號(hào),同時(shí)將主動(dòng)通信的文件描述符轉(zhuǎn)為被動(dòng)通信的文件描述符。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>#define IP "192.168.102.174"
#define PORT 7000typedef struct data {char name[30];unsigned int num;
}Data;
void print_err(char *str, int line, int err_no) {printf("%d, %s :%s\n",line,str,strerror(err_no));_exit(-1);
}
int skfd = -1;
void *receive(void *pth_arg) {int ret = 0;Data stu_data = {0};while(1) {bzero(&stu_data, sizeof(stu_data));ret = recv(skfd, &stu_data, sizeof(stu_data),0);	if (-1 == ret) {print_err("recv failed",__LINE__,errno);}else if (ret > 0)printf("student number = %d student name = %s \n",ntohl(stu_data.num),stu_data.name);}
}void sig_fun(int signo) {if (signo == SIGINT) {//close(cfd);shutdown(skfd,SHUT_RDWR);_exit(0);}
}int main()
{int ret = -1;skfd = socket(AF_INET, SOCK_STREAM, 0);if ( -1 == skfd) {print_err("socket failed",__LINE__,errno);}/*這里需要填寫服務(wù)器的端口號(hào)和IP地址,此時(shí)客戶端會(huì)自己分配端口號(hào)和自己的IP發(fā)送給服務(wù)器*/struct sockaddr_in addr;addr.sin_family = AF_INET; //設(shè)置tcp協(xié)議族addr.sin_port = htons(PORT); //設(shè)置服務(wù)器端口號(hào)addr.sin_addr.s_addr = inet_addr(IP); //設(shè)置服務(wù)器ip地址,如果是跨網(wǎng),則需要填寫服務(wù)器所在路由器的公網(wǎng)ipret = connect(skfd,(struct sockaddr *)&addr,sizeof(addr));if ( -1 == ret ) {print_err("connect failed", __LINE__, errno);}pthread_t id;pthread_create(&id,NULL,receive,NULL);Data std_data = {0};while (1) {bzero(&std_data, sizeof(std_data));printf("stu name:\n");scanf("%s",std_data.name);printf("stu num:\n");scanf("%d",&std_data.num);std_data.num = htonl(std_data.num);ret = send(skfd, (void *)&std_data,sizeof(std_data),0);if ( -1 == ret) {print_err("send failed", __LINE__, errno);}	}return 0;
}

編譯gcc server.c -o server -pthread
gcc client.c -o client -pthread
運(yùn)行如下:

總結(jié)

以上是生活随笔為你收集整理的C语言网络编程:TCP客户端实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。