LinuxC网络编程
文章目錄
- 一、socket
- 二、bind
- 三、setsockopt
- 四、listen(TCP服務端)
- 五、accept(TCP服務端)
- 六、connect(TCP客戶端)
- 七、send&recv(TCP)
- 八、sendto&recvfrom(UDP)
- 九、shutdown
- 十、epoll_create
- 十一、epoll_ctl
- 十二、epoll_wait
- 十三、循環服務器模型(TCP)
- 十四、循環服務器模型(UDP)
- 十五、并發服務器模型(TCP多線程)
- 十六、并發服務器模型(TCP多進程)
一、socket
#include <sys/types.h> #include <sys/socket.h>int socket(int domain, int type, int protocol);功能:創建一個套接字文件,然后以文件形式來操作通信,不過套接字文件沒有文件名。
參數:
- domain:用于指定協議族是IPV4還是IPV6,分別對應參數AF_INET和參數AF_INET6。
- type:套接字類型,用于進一步指定使用協議族中的哪個子協議來通信:
 ①如果指定為SOCK_STREAM,表示使用TCP協議;
 ②如果指定為SOCK_DGRAM,表示使用UDP協議;
 ③如果指定為SOCK_RDM,表示使用原始網絡通信,即IP協議;
 ④如果指定為SOCK_NONBLOCK,表示將socket返回的文件描述符指定為非阻塞的,它可以與前面的宏進行或運算。
- protocol:表示傳輸協議,一般情況下可以直接寫0,有操作系統自動推演出應該使用什么協議。
返回值:成功返回套接字描述符,失敗返回-1并設置errno。
二、bind
#include <sys/types.h> #include <sys/socket.h>#include <netinet/in.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);struct sockaddr_in {short int sin_family; /* Internet地址族 */unsigned short int sin_port; /* 端口號 */struct in_addr sin_addr; /* IP地址 */unsigned char sin_zero[8]; /* 填0 */ };struct in_addr {unsigned long s_addr; /* IPV4的32位IP地址 */ };功能:將指定了通信協議的套接字文件與IP以及端口綁定起來。
參數:
- sockfd:套接字的文件描述符。
- addr:指定要綁定的參數信息。編程中一般并不直接針對sockaddr數據結構操作,而是使用與sockaddr等價的sockaddr_in數據結構。
- addrlen:第二個參數的結構的長度。
此外,由于網絡字節序有一般采用大端(高尾端)排序方式,所以從主機向網絡發送和從網絡向主機接收時要進行字節序的轉換:
#include <arpa/inet.h>uint32_t htonl(uint32_t hostlong); /* host to network short */ uint16_t htons(uint16_t hostshort); /* host to network short */ uint32_t ntohl(uint32_t netlong); /* network to host long */ uint16_t ntohs(uint16_t netshort); /* network to host short */同時,IP地址的格式也要在字符串和32位整數之間相互轉化:
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>int inet_aton(const char *cp, struct in_addr *inp); in_addr_t inet_addr(const char *cp); in_addr_t inet_network(const char *cp); char *inet_ntoa(struct in_addr in); struct in_addr inet_makeaddr(in_addr_t net, in_addr_t host); in_addr_t inet_lnaof(struct in_addr in); in_addr_t inet_netof(struct in_addr in);三、setsockopt
#include <sys/types.h> #include <sys/socket.h>int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen); int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);如果是服務器主動關閉連接,默認需要等待2MSL才能將端口資源釋放,這會導致此端口短時間內無法重用,于是我們通過下面這段函數將該socket設置為可重用:
/* 設置套接字為可重用 */ int option = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));四、listen(TCP服務端)
#include <sys/types.h> #include <sys/socket.h>int listen(int sockfd, int backlog);功能:將套接字文件描述符,從主動文件描述符變為被動文件描述符,然后用于被動監聽客戶的連接。
參數:
- sockfd:socket返回的套接字文件描述符。
- backlog:指定的隊列容量,這個隊列用于記錄正在連接,但是還沒連接完成的客戶,一般將隊列容量指定為2、3即可。這個容量并沒有什么統一的設定值,一般來說只要小于30即可。
五、accept(TCP服務端)
#include <sys/types.h> #include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);#define _GNU_SOURCE #include <sys/socket.h>int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);功能:被動監聽客戶發起三次握手的連接請求,三次握手成功,即建立連接成功。accept被動監聽客戶連接的過程其實也是監聽客戶上線的過程。對于那些只連接了一般,還未連接完成的客戶,會被記錄到未完成隊列中,隊列的容量由listen函數的第二個參數backlog來指定。服務端調用accept函數監聽連接,客戶端調用connect來請求連接。一旦連接成功,服務器這邊的TCP協議會記錄客戶的IP和端口,如果是跨網通信的,記錄IP的就是客戶所在的路由器的公網IP。
參數:
- sockfd:一個已經通過listen函數轉化的被動套接字文件描述符。
- addr:用于記錄發起連接請求的那個客戶端的IP和端口。
- addrlen:用一個變量記錄第二個參數的長度,addrlen對應這個變量的地址。
返回值:如果執行成功返回新的套接字文件描述符,否則返回-1并設置errno。
六、connect(TCP客戶端)
#include <sys/types.h> #include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);功能:向服務器主動發起連接請求,即主動發起三次握手。
參數:
- sockfd:socket函數所返回的套接字文件描述符。
- addr:用于設置你所要連接的服務器的IP和端口。如果只是純粹的局域網內部通信的話,IP就是局域網IP,但如果是跨網通信的話,IP必須是服務器所在的路由器的公網IP。為了方便操作,在應用層我們還是使用struct sockaddr_in來設置,然后傳遞給connect時在強制轉換為struct sockaddr。
- addrlen:第二個參數所指定的結構體變量的大小。
七、send&recv(TCP)
#include <sys/types.h> #include <sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t len, int flags); ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);ssize_t recv(int sockfd, void *buf, size_t len, int flags); ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);功能:向對方發送數據或從對方接收數據。
參數:
- sockfd:用于通信的通信描述符。通信描述符在客戶端對應socket函數生成的套接字描述符,在服務端則是accept返回的套接字描述符。
- buf:應用緩存,存放要發送的或待接收的數據,一般情況下為一個結構體。
- flags:一般置為0,表示阻塞發送或阻塞接收,如果想要非阻塞讀取或發送則置為MSG_DONTWAIT。
八、sendto&recvfrom(UDP)
#include <sys/types.h> #include <sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t len, int flags); ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);ssize_t recv(int sockfd, void *buf, size_t len, int flags); ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);功能:發送或接收數據,最后兩個參數均為NULL時功能與send和recv相同。
參數:
- sockfd:socket返回的套接字。對于UDP來說,套接字描述符直接用于通信。
- buf:存放數據的應用緩存。
- len:應用緩存的大小。
- flags:一般寫0,表示阻塞發送數據。
- dest_addr&src_addr:IP地址以及端口等套接字信息。由于UDP不面向連接,無法自動記錄對方的套接字信息,所以每次數據的發送都需要指定套接字信息。
- addrlen:套接字信息結構體的大小。
返回值:成功返回發送或接收到的字節數,失敗返回-1并設置errno。
九、shutdown
#include <sys/socket.h>int shutdown(int sockfd, int how);功能:可以按照要求關閉連接,而且不管有多少個描述符指向同一個連接,shutdown可以一次性將其全部關閉。
參數:
- sockfd:服務端使用accept函數返回的描述符。
- how:如何斷開連接。SHUT_RD只斷開讀連接,SHUT_WR只斷開寫連接,SHUT_RDWR將讀、寫連接全部斷開。
shutdown與close的區別:
十、epoll_create
#include <sys/epoll.h>int epoll_create(int size); int epoll_create1(int flags);功能:epoll_create()函數用于創建一個epoll實例,它實際上是由一個紅黑樹來實現的。
參數:
- size:用于告訴內核這個epoll需要關注的描述符的大致數目而不是最大個數。實際上Linux在2.6.8版本以后會忽略這個參數,但這個參數必須大于零。當容量不足時epoll會自動擴容。
返回值:返回創建的epoll紅黑樹的文件描述符。
十一、epoll_ctl
#include <sys/epoll.h>int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);typedef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64; } epoll_data_t;struct epoll_event {uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */ };功能:用于操作創建的epoll實例,主要包括增加、修改和刪除三種操作。
參數:
- epfd:epoll的文件描述符。
- op:對epoll執行的操作,主要包括:
 ①EPOLL_CTL_ADD,向epoll中注冊新的文件描述符。
 ②EPOLL_CTL_MOD,修改已注冊文件描述符的監聽事件。
 ③EPOLL_CTL_DEL,從epoll中刪除文件描述符。
- fd:操作關聯的文件描述符。
- event:告訴內核要監聽什么事件,實際上epoll的紅黑樹每個結點上都對應一個epoll_event類型的結構體。結構體中的成員events主要包括以下宏:
 ①EPOLLIN:表示對應的文件描述符可以讀。
 ②EPOLLOUT:表示對應的文件描述符可以寫。
 ③EPOLLPRI:表示對應的文件描述符有緊急數據或者帶外數據到來。
 ④EPOLLERR:表示對應的文件描述符發生錯誤。
 ⑤EPOLLHUP:表示對應的文件描述符被掛斷。
 ⑥EPOLLET:將epoll設置為邊緣觸發模式。
 ⑦EPOLLONESHOT:只監聽一次事件,當監聽完這次事件后,如果還需要繼續監聽這個socket的話,需要將它再次加入到epoll隊列里。
返回值:執行成功返回0,否則返回-1并設置errno的值。
十二、epoll_wait
#include <sys/epoll.h>int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); int epoll_pwait(int epfd, struct epoll_event *events, int maxevents, int timeout, const sigset_t *sigmask);功能
參數
- epfd:epoll的文件描述符。
- events:一個結構體指針,當監聽的文件描述符發生改變時,內核會把發生了改變的epoll_event拷貝到這個參數里。
- maxevents:數組的容量。
- timeout:超時信息:
 ①為-1時表示永久阻塞。
 ②為0時表示立即返回。
 ③取>0時表示監聽到目標數目的文件描述符時再返回。
返回值:執行成功返回發出請求的文件描述符的個數,執行失敗返回-1并設置error。
十三、循環服務器模型(TCP)
服務端 server.c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <errno.h>#include <stdio.h> #include <stdlib.h> #include <string.h>#define PORT 2015 #define IP "192.168.179.129" #define MAX_SIZE 1024int main() {int sockfd;//服務器套接字描述符int client_sockfd;//通信套接字描述符char buffer[MAX_SIZE];//數據緩沖區struct sockaddr_in server_addr;//服務器套接字信息struct sockaddr_in client_addr;//客戶端套接字信息/* 獲取服務器套接字文件描述符 */sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1) {perror("socket error");exit(1);}/* 初始化服務器套接字信息 */memset(&server_addr, 0, sizeof(struct sockaddr_in));server_addr.sin_family = AF_INET;//使用IPv4server_addr.sin_port = htons(PORT);//對端口進行字節序轉化server_addr.sin_addr.s_addr = inet_addr(IP);//對IP地址進行格式轉化/* 因為最后需要服務器主動關閉連接,所以要設置服務器套接字為可重用 */int option = 1;setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));/* 綁定服務器套接字信息 */if (bind(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr_in)) < 0) {perror("bind error");exit(1);}/* 將服務器套接字轉化為被動監聽 */if (listen(sockfd, 3) < 0) {perror("listen error!");exit(1);}/* 與客戶端進行串行連接 */while(1) {printf("[server] Server is waiting······\n");/* 等待客戶端的連接 */int len = sizeof(struct sockaddr_in);//通信套接字結構體長度memset(&client_addr, 0, sizeof(struct sockaddr_in));if ((client_sockfd = accept(sockfd, (struct sockaddr *)(&client_addr), &len)) < 0) {perror("accept error");exit(1);}printf("[server] Client's port is %d, ip is %s\n", ntohs(client_addr.sin_port), inet_ntoa(client_addr.sin_addr));/* 接收客戶端的消息 */memset(buffer, 0, MAX_SIZE);recv(client_sockfd, buffer, sizeof(buffer), 0);printf("[server] Client's message:%s\n", buffer);/* 向客戶端發送消息 */send(client_sockfd, "I have received your message.", 30, 0);/* 關閉連接 */shutdown(client_sockfd, SHUT_RDWR);}return 0; }客戶端 client.c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <errno.h>#include <stdio.h> #include <stdlib.h> #include <string.h>#define PORT 2015 #define IP "192.168.179.129" #define MAX_SIZE 1024int main() {int sockfd;//客戶端套接字描述符char buffer[MAX_SIZE];//收發數據緩沖區struct sockaddr_in server_addr;//服務器套接字描述符/* 獲取客戶端套接字文件描述符 */if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("socket error");exit(1);}/* 初始化服務器套接字信息 */memset(&server_addr, 0, sizeof(struct sockaddr_in));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(PORT);server_addr.sin_addr.s_addr = inet_addr(IP);/* 向服務器發送連接請求 */if (connect(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr_in)) < 0) {perror("connect error");exit(1);}printf("[client] Connect successfully.\n");/* 向服務器發送消息 */printf("[client] Please input your message>>>");memset(buffer, 0, MAX_SIZE);scanf("%[^\n]%*c", buffer);send(sockfd, buffer, strlen(buffer), 0);/* 接收服務端的消息 */memset(buffer, 0, MAX_SIZE);recv(sockfd, buffer, sizeof(buffer), 0);printf("[client] Server's message:%s\n", buffer);return 0; }運行結果
十四、循環服務器模型(UDP)
服務端 server.c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <errno.h>#include <stdio.h> #include <stdlib.h> #include <string.h>#define CLIENT_PORT 2015 #define SERVER_PORT 2025 #define IP "192.168.179.129" #define MAX_SIZE 1024int main() {int sockfd;//服務端套接字描述符char buffer[MAX_SIZE];//收發數據緩沖區struct sockaddr_in client_addr;//客戶端套接字信息struct sockaddr_in server_addr;//服務端套接字信息/* 獲取服務端套接字描述符 */if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("socket error");exit(1);}/* 初始化服務端套接字信息 */memset(&server_addr, 0, sizeof(struct sockaddr_in));server_addr.sin_family = AF_INET;server_addr.sin_port = (SERVER_PORT);server_addr.sin_addr.s_addr = inet_addr(IP);/* 綁定接收端信息 */if (bind(sockfd, (struct sockaddr *) &server_addr, sizeof(struct sockaddr_in)) < 0) {perror("bind error");exit(1);}/* 接收消息 */while (1) {/* 初始化客戶端套接字信息 */memset(&client_addr, 0, sizeof(struct sockaddr_in));client_addr.sin_family = AF_INET;client_addr.sin_port = (CLIENT_PORT);client_addr.sin_addr.s_addr = inet_addr(IP);/* 收發消息 */printf("[server] Srever is waiting······\n");int len = sizeof(struct sockaddr_in);memset(buffer, 0, sizeof(buffer));recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *) &client_addr, &len);printf("[server] Client's port is %d, ip is %s.\n", ntohs(client_addr.sin_port), inet_ntoa(client_addr.sin_addr));printf("[server] Client's message:%s\n", buffer);memset(buffer, 0, sizeof(buffer));sendto(sockfd, "I have received your message.", 30, 0, (struct sockaddr *) &client_addr, sizeof(struct sockaddr_in));}return 0; }客戶端 client.c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <errno.h>#include <stdio.h> #include <stdlib.h> #include <string.h>#define CLIENT_PORT 2015 #define SERVER_PORT 2025 #define IP "192.168.179.129" #define MAX_SIZE 1024int main() {int sockfd;//客戶端套接字描述符char buffer[MAX_SIZE];//收發數據緩沖區struct sockaddr_in client_addr;//客戶端套接字信息struct sockaddr_in server_addr;//服務端套接字信息/* 獲取客戶端套接字描述符 */if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("socket error");exit(1);}/* 初始化服務端套接字信息 */memset(&client_addr, 0, sizeof(struct sockaddr_in));client_addr.sin_family = AF_INET;client_addr.sin_port = (CLIENT_PORT);client_addr.sin_addr.s_addr = inet_addr(IP);/* 綁定接收端信息 */if (bind(sockfd, (struct sockaddr *) &client_addr, sizeof(struct sockaddr_in)) < 0) {perror("bind error");exit(1);}/* 接收消息 */while (1) {/* 初始化服務端套接字信息 */memset(&server_addr, 0, sizeof(struct sockaddr_in));server_addr.sin_family = AF_INET;server_addr.sin_port = (SERVER_PORT);server_addr.sin_addr.s_addr = inet_addr(IP);/* 收發消息 */printf("[client] Please input your message>>>");memset(buffer, 0, sizeof(buffer));scanf("%[^\n]%*c", buffer);sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr *) &server_addr, sizeof(struct sockaddr_in));int len = sizeof(struct sockaddr_in);recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *) &server_addr, &len);printf("[client] Server's port is %d, ip is %s.\n", ntohs(server_addr.sin_port), inet_ntoa(server_addr.sin_addr));printf("[client] Server's message:%s\n", buffer);memset(buffer, 0, sizeof(buffer));}return 0; }運行結果
十五、并發服務器模型(TCP多線程)
服務端 server.c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <pthread.h> #include <errno.h>#include <stdio.h> #include <stdlib.h> #include <string.h>#define PORT 2015 #define IP "192.168.179.129" #define MAX_SIZE 1024void *recv_thread_fun(void *arg);int main() {int server_sockfd;//服務器套接字描述符int client_sockfd;//通信套接字描述符pthread_t recv_thread_id;struct sockaddr_in server_addr;//服務器套接字信息struct sockaddr_in client_addr;//客戶端套接字信息/* 獲取服務器套接字文件描述符 */server_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (server_sockfd == -1) {perror("socket error");exit(1);}/* 初始化服務器套接字信息 */memset(&server_addr, 0, sizeof(struct sockaddr_in));server_addr.sin_family = AF_INET;//使用IPv4server_addr.sin_port = htons(PORT);//對端口進行字節序轉化server_addr.sin_addr.s_addr = inet_addr(IP);//對IP地址進行格式轉化/* 因為最后需要服務器主動關閉連接,所以要設置服務器套接字為可重用 */int option = 1;setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));/* 綁定服務器套接字信息 */if (bind(server_sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr_in)) < 0) {perror("bind error");exit(1);}/* 將服務器套接字轉化為被動監聽 */if (listen(server_sockfd, 3) < 0) {perror("listen error!");exit(1);}/* 與客戶端進行串行連接 */while(1) {printf("[server] Server is waiting······\n");/* 等待客戶端的連接 */int len = sizeof(struct sockaddr_in);//通信套接字結構體長度memset(&client_addr, 0, sizeof(struct sockaddr_in));if ((client_sockfd = accept(server_sockfd, (struct sockaddr *)(&client_addr), &len)) < 0) {perror("accept error");exit(1);}printf("[server] Client %d port is %d, ip is %s.\n", client_sockfd,ntohs(client_addr.sin_port), inet_ntoa(client_addr.sin_addr));/* 創建子線程用于收發消息 */if (pthread_create(&recv_thread_id, NULL, recv_thread_fun, (void *) &client_sockfd) != 0) {perror("pthread create error");exit(1);}}return 0; }/* 子線程運行函數 */ void *recv_thread_fun(void *arg) {int client_sockfd = *((int *) arg);char buffer[MAX_SIZE];//數據緩沖區pthread_detach(pthread_self());//將本線程轉換為分離態,由系統自動回收資源while (1) {/* 接收客戶端的消息 */memset(buffer, 0, MAX_SIZE);if (recv(client_sockfd, buffer, sizeof(buffer), 0) == 0) {/* 監測客戶端的退出 */printf("[server] Client %d has exited.\n", client_sockfd);pthread_exit(NULL);}printf("[server] Client %d message:%s\n", client_sockfd, buffer);/* 向客戶端發送消息 */send(client_sockfd, "I have received your message.", 30, 0);} }客戶端 client.c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <errno.h>#include <stdio.h> #include <stdlib.h> #include <string.h>#define PORT 2015 #define IP "192.168.179.129" #define MAX_SIZE 1024int main() {int client_sockfd;//客戶端套接字描述符char buffer[MAX_SIZE];//收發數據緩沖區struct sockaddr_in server_addr;//服務器套接字描述符/* 獲取客戶端套接字文件描述符 */if((client_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("socket error");exit(1);}/* 初始化服務器套接字信息 */memset(&server_addr, 0, sizeof(struct sockaddr_in));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(PORT);server_addr.sin_addr.s_addr = inet_addr(IP);/* 向服務器發送連接請求 */if (connect(client_sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr_in)) < 0) {perror("connect error");exit(1);}printf("[client] Connect successfully.\n");while (1) {/* 向服務器發送消息 */printf("[client] Please input your message>>>");memset(buffer, 0, MAX_SIZE);scanf("%[^\n]%*c", buffer);if (!buffer[0]) break;//輸入空代表結束通信send(client_sockfd, buffer, strlen(buffer), 0);/* 接收服務端的消息 */memset(buffer, 0, MAX_SIZE);recv(client_sockfd, buffer, sizeof(buffer), 0);printf("[client] Server's message:%s\n", buffer);}/* 關閉連接 */shutdown(client_sockfd, SHUT_RDWR);return 0; }運行結果
十六、并發服務器模型(TCP多進程)
服務端 server.c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <errno.h>#include <stdio.h> #include <stdlib.h> #include <string.h>#define SERVER_PORT 1521 #define IP "0.0.0.0" #define MAX_SIZE 1024int main() {int server_sockfd;//服務器套接字描述符int client_sockfd;//通信套接字描述符struct sockaddr_in server_addr;//服務器套接字信息struct sockaddr_in client_addr;//客戶端套接字信息/* 獲取服務器套接字文件描述符 */server_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (server_sockfd == -1) {perror("socket error");exit(1);}/* 初始化服務器套接字信息 */memset(&server_addr, 0, sizeof(struct sockaddr_in));server_addr.sin_family = AF_INET;//使用IPv4server_addr.sin_port = htons(SERVER_PORT);//對端口進行字節序轉化server_addr.sin_addr.s_addr = inet_addr(IP);//對IP地址進行格式轉化/* 因為最后需要服務器主動關閉連接,所以要設置服務器套接字為可重用 */int option = 1;setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));/* 綁定服務器套接字信息 */if (bind(server_sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr_in)) < 0) {perror("bind error");exit(1);}/* 將服務器套接字轉化為被動監聽 */if (listen(server_sockfd, 3) < 0) {perror("listen error!");exit(1);}/* 與客戶端進行串行連接 */while(1) {printf("[server] Server is waiting······\n");/* 等待客戶端的連接 */int len = sizeof(struct sockaddr_in);//通信套接字結構體長度memset(&client_addr, 0, sizeof(struct sockaddr_in));if ((client_sockfd = accept(server_sockfd, (struct sockaddr *)(&client_addr), &len)) < 0) {perror("accept error");exit(1);}printf("[server] Client %d port is %d, ip is %s.\n", client_sockfd,ntohs(client_addr.sin_port), inet_ntoa(client_addr.sin_addr));int pid = fork();//創建子進程用于收發消息if (pid > 0) {close(client_sockfd);} else if (pid == 0) {char buffer[1024];while (1) {/* 接收客戶端的消息 */memset(buffer, 0, MAX_SIZE);if (recv(client_sockfd, buffer, sizeof(buffer), 0) == 0) {/* 監測客戶端的退出 */printf("[server] Client %d has exited.\n", client_sockfd);exit(0);}printf("[server] Client %d message:%s\n", client_sockfd, buffer);/* 向客戶端發送消息 */send(client_sockfd, "I have received your message.", 30, 0);}} else {perror("fork error");exit(1);}}return 0; }客戶端 client.c
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <errno.h>#include <stdio.h> #include <stdlib.h> #include <string.h>#define PORT 1521 #define IP "120.78.188.0" #define MAX_SIZE 1024int main() {int client_sockfd;//客戶端套接字描述符char buffer[MAX_SIZE];//收發數據緩沖區struct sockaddr_in server_addr;//服務器套接字描述符/* 獲取客戶端套接字文件描述符 */if((client_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("socket error");exit(1);}/* 初始化服務器套接字信息 */memset(&server_addr, 0, sizeof(struct sockaddr_in));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(PORT);server_addr.sin_addr.s_addr = inet_addr(IP);/* 向服務器發送連接請求 */if (connect(client_sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr_in)) < 0) {perror("connect error");exit(1);}printf("[client] Connect successfully.\n");while (1) {/* 向服務器發送消息 */printf("[client] Please input your message>>>");memset(buffer, 0, MAX_SIZE);scanf("%[^\n]%*c", buffer);if (!buffer[0]) break;//輸入空代表結束通信send(client_sockfd, buffer, strlen(buffer), 0);/* 接收服務端的消息 */memset(buffer, 0, MAX_SIZE);recv(client_sockfd, buffer, sizeof(buffer), 0);printf("[client] Server's message:%s\n", buffer);}/* 關閉連接 */shutdown(client_sockfd, SHUT_RDWR);return 0; }運行結果
總結
以上是生活随笔為你收集整理的LinuxC网络编程的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 程序员常用的画图软件推荐
- 下一篇: linux at命令关机,Linux
