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