欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C語言多線程服務(wù)器的實(shí)現(xiàn)實(shí)例

 更新時(shí)間:2021年02月25日 09:56:49   作者:sinkinben  
這篇文章主要介紹了C語言多線程服務(wù)器的實(shí)現(xiàn)實(shí)例,文章用實(shí)例講解的很清楚,有對(duì)這方面不太懂的同學(xué)可以參考下

本文基于 C 標(biāo)準(zhǔn)庫(kù)提供的網(wǎng)絡(luò)通信 API,使用 TCP ,實(shí)現(xiàn)一個(gè)簡(jiǎn)單的多線程服務(wù)器 Demo 。

首先要看 API

API

字節(jié)序轉(zhuǎn)換

函數(shù)原型:

#include <arpa/inet.h>
uint64_t htonll(uint64_t hostlonglong);
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint64_t ntohll(uint64_t netlonglong);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

h 表示 host, n 表示 network,這些函數(shù)的作用是把主機(jī)的字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)的字節(jié)序(即小端到大端的轉(zhuǎn)變)。

例如:

#include <arpa/inet.h>
#include <stdio.h>
int main()
{
 uint32_t host = 0x01020304; // high->low: 01 02 03 04
 uint32_t network = htonl(host); // high->low: 04 03 02 01
 printf("%p\n", network); // 0x4030201
}

socket

函數(shù)原型:

#include <sys/socket.h>
int socket(int domain, int type, int protocol);

建立一個(gè)協(xié)議族為 domain, 協(xié)議類型為 type, 協(xié)議編號(hào)為 protocol 的套接字文件描述符。如果函數(shù)調(diào)用成功,會(huì)返回一個(gè)標(biāo)識(shí)這個(gè)套接字的文件描述符,失敗的時(shí)候返回-1。

domain 的取值:

Name  Purpose    Man page
AF_UNIX, AF_LOCAL Local communication  unix(7)
AF_INET  IPv4 Internet protocols  ip(7)
AF_INET6  IPv6 Internet protocols  ipv6(7)
AF_IPX  IPX - Novell protocols
AF_NETLINK  Kernel user interface device netlink(7)
AF_X25  ITU-T X.25 / ISO-8208 protocol x25(7)
AF_AX25  Amateur radio AX.25 protocol
AF_ATMPVC  Access to raw ATM PVCs
AF_APPLETALK AppleTalk   ddp(7)
AF_PACKET  Low level packet interface packet(7)
AF_ALG  Interface to kernel crypto API

AF 是 Address Family 的縮寫,INET 是 Internet 的縮寫。某些地方可能會(huì)使用 PF,即 Protocol Family,應(yīng)該是同一個(gè)東西。

type 的取值:

SOCK_STREAM Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission mechanism may be supported.

SOCK_DGRAM Supports datagrams (connectionless, unreliable messages of a fixed maximum length).

SOCK_SEQPACKET Provides a sequenced, reliable, two-way connection-based data transmission path for datagrams of fixed maximum length; a consumer is required to read an entire packet with each input system call.

SOCK_RAW Provides raw network protocol access.

SOCK_RDM Provides a reliable datagram layer that does not guarantee ordering.

SOCK_PACKET Obsolete and should not be used in new programs; see packet(7).

type 常用的是 STREAMDGRAM ,根據(jù)描述,可以確定前者對(duì)應(yīng) TCP,而后者對(duì)應(yīng) UDP :

  • SOCK_STREAM 套接字表示一個(gè)雙向的字節(jié)流,與管道類似。流式的套接字在進(jìn)行數(shù)據(jù)收發(fā)之前必須已經(jīng)連接,連接使用 connect() 函數(shù)進(jìn)行。一旦連接,可以使用 read() 或者 write() 函數(shù)進(jìn)行數(shù)據(jù)的傳輸,流式通信方式保證數(shù)據(jù)不會(huì)丟失或者重復(fù)接收。
  • SOCK_DGRAMSOCK_RAW 這個(gè)兩種套接字可以使用函數(shù) sendto() 來發(fā)送數(shù)據(jù),使用 recvfrom() 函數(shù)接受數(shù)據(jù),recvfrom() 接受來自制定IP地址的發(fā)送方的數(shù)據(jù)。

對(duì)于第 3 個(gè)參數(shù) protocal,用于指定某個(gè)協(xié)議的特定類型,即 type 類型中的某個(gè)類型。通常某協(xié)議中只有一種特定類型,這 樣protocol 參數(shù)僅能設(shè)置為 0 ;但是有些協(xié)議有多種特定的類型,就需要設(shè)置這個(gè)參數(shù)來選擇特定的類型。

bind

函數(shù)原型:

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

如果函數(shù)執(zhí)行成功,返回值為 0,否則為 SOCKET_ERROR 。

參數(shù):

  • sockfd 是一個(gè)有效的 socket 描述符(函數(shù) socket() 的有效返回值)。
  • addrlen 是第二個(gè)參數(shù) addr 結(jié)構(gòu)體的長(zhǎng)度。
  • addr 是一個(gè) sockaddr 結(jié)構(gòu)體指針,包含 IP 和端口等信息。

sockaddr 的結(jié)構(gòu)如下:

struct sockaddr {
 sa_family_t sa_family;
 char sa_data[14];
};
// sa_familt_t 是無符號(hào)整型,Ubuntu 下是 unsigned short int

sockaddr 的存在是為了統(tǒng)一地址結(jié)構(gòu)的表示方法 ,統(tǒng)一接口函數(shù),使得不同的地址結(jié)構(gòu)可以被 bind(), connect(), recvfrom(), sendto() 等函數(shù)調(diào)用。但一般的編程中并不直接對(duì)此數(shù)據(jù)結(jié)構(gòu)進(jìn)行操作,而使用另一個(gè)與之等價(jià)的數(shù)據(jù)結(jié)構(gòu) sockaddr_in :

struct sockaddr_in {
 short int sin_family; /* Address family */
 unsigned short int sin_port; /* Port number */
 struct in_addr sin_addr; /* Internet address */
 unsigned char sin_zero[8]; /* Same size as struct sockaddr */
};

各字段解析:

  • sin_family :指代協(xié)議族,在 socket 編程中有 3 個(gè)取值 AF_INET, AF_INET6, AF_UNSPEC .
  • sin_port :存儲(chǔ)端口號(hào)(使用網(wǎng)絡(luò)字節(jié)順序)
  • sin_addr :存儲(chǔ)IP地址,使用 in_addr 這個(gè)數(shù)據(jù)結(jié)構(gòu)
  • sin_zero :是為了讓 sockaddrsockaddr_in 兩個(gè)數(shù)據(jù)結(jié)構(gòu)保持大小相同而保留的空字節(jié)。

in_addr 的結(jié)構(gòu)如下:

typedef uint32_t in_addr_t;
struct in_addr{
 in_addr_t s_addr;
};

listen

int listen(int sockfd, int backlog);

返回值:無錯(cuò)誤,返回 0,否則 -1 。

作用:listen 函數(shù)使用主動(dòng)連接套接字變?yōu)楸贿B接套接口,使得一個(gè)進(jìn)程可以接受其它進(jìn)程的請(qǐng)求,從而成為一個(gè)服務(wù)器進(jìn)程。在 TCP 服務(wù)器編程中 listen 函數(shù)把進(jìn)程變?yōu)橐粋€(gè)服務(wù)器,并指定相應(yīng)的套接字變?yōu)楸粍?dòng)連接。

listen 函數(shù)一般在調(diào)用 bind 之后,調(diào)用 accept 之前調(diào)用。

backlog 參數(shù)指定連接請(qǐng)求隊(duì)列的最大個(gè)數(shù)。

accept

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

接受連接請(qǐng)求,成功返回一個(gè)新的套接字描述符 newfd ,失敗返回-1。返回值 newfd 與參數(shù) sockfd 是不同的,newfd 專門用于與客戶端的通信,而 sockfd 是專門用于 listen 的 socket 。

addraddrlen 都是指針,用于接收來自客戶端的 addr 的信息。

inet_addr

函數(shù)原型:

in_addr_t inet_addr(const char *cp);

將一個(gè)點(diǎn)分十進(jìn)制的 IP 字符串轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序的 uint32_t 。

例子

int main()
{
 const char *ip = "127.0.0.1"; // 7f.00.00.01
 printf("%p\n", inet_addr(ip)); // 0x0100007f
}

send

#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);

其中 send(fd, buf, len, flags)sendto(fd, buf, len, flags, NULL, 0) 等價(jià)。

recv

#include <sys/types.h>
#include <sys/socket.h>
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);

其中 recv(fd, buf, len, flags)recvfrom(fd, buf, len, flags, NULL, 0) 等價(jià)。

connect

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

成功返回 0 ,失敗返回 -1 。

sockfd 是客戶端進(jìn)程創(chuàng)建的,用于與服務(wù)端通信的 socket ; addr 是目標(biāo)服務(wù)器的 IP 地址和端口。

多線程服務(wù)器

本次實(shí)現(xiàn)的場(chǎng)景如下:

  • 客戶端可以具有多個(gè),客戶端主動(dòng)連接服務(wù)器,允許每個(gè)客戶端發(fā)送 msg 到服務(wù)器,并接受來自服務(wù)器的信息。
  • 服務(wù)端對(duì)于每個(gè)申請(qǐng)連接到客戶端,創(chuàng)建一個(gè)線程處理請(qǐng)求。對(duì)于客戶端發(fā)送過來的 msg,然后服務(wù)器把 msg 加上一些其他字符串,發(fā)送回客戶端。

server

#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#define PORT 8887
#define QUEUE 10
const char *pattern = "Hello, I am the server. Your msg is received, which is: %s";

typedef struct
{
 struct sockaddr_in addr;
 socklen_t addr_len;
 int connectfd;
} thread_args;

void *handle_thread(void *arg)
{
 thread_args *targs = (thread_args *)arg;
 pthread_t tid = pthread_self();
 printf("tid = %u and socket = %d\n", tid, targs->connectfd);
 char send_buf[BUFSIZ] = {0}, recv_buf[BUFSIZ] = {0};
 while (1)
 {
 int len = recv(targs->connectfd, recv_buf, BUFSIZ, 0);
 printf("[Client %d] %s", targs->connectfd, recv_buf);
 
 if (strcmp("q\n", recv_buf) == 0)
  break;
 
 sprintf(send_buf, pattern, recv_buf);
 send(targs->connectfd, send_buf, strlen(send_buf), 0);

 memset(send_buf, 0, BUFSIZ), memset(recv_buf, 0, BUFSIZ);
 }
 close(targs->connectfd);
 free(targs);
 pthread_exit(NULL);
}

int main()
{
 int listenfd = socket(AF_INET, SOCK_STREAM, 0);
 printf("server is listening at socket fd = %d\n", listenfd);
 struct sockaddr_in addr;
 addr.sin_family = AF_INET;
 addr.sin_port = htons(PORT);
 addr.sin_addr.s_addr = htonl(INADDR_ANY);

 if (bind(listenfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
 {
 perror("bind error\n");
 exit(-1);
 }

 if (listen(listenfd, QUEUE) == -1)
 {
 perror("listen error\n");
 exit(-1);
 }

 while (1)
 {
 thread_args *targs = malloc(sizeof(thread_args));
 targs->connectfd = accept(listenfd, (struct sockaddr *)&targs->addr, &targs->addr_len);
 // int newfd = accept(sockfd, NULL, NULL);
 pthread_t tid;
 pthread_create(&tid, NULL, handle_thread, (void *)targs);
 pthread_detach(tid);
 }
 close(listenfd);
}

client

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define PORT 8887
const char *target_ip = "127.0.0.1";

int main()
{
 int sockfd = socket(AF_INET, SOCK_STREAM, 0);
 printf("client socket = %d\n", sockfd);
 struct sockaddr_in addr;
 addr.sin_family = AF_INET;
 addr.sin_port = htons(PORT);
 addr.sin_addr.s_addr = inet_addr(target_ip);

 if (connect(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
 {
 perror("connect error\n");
 exit(-1);
 }

 char send_buf[BUFSIZ], recv_buf[BUFSIZ];
 while (fgets(send_buf, BUFSIZ, stdin) != NULL)
 {
 if (strcmp(send_buf, "q\n") == 0)
  break;

 send(sockfd, send_buf, strlen(send_buf), 0);
 printf("[Client] %s\n", send_buf);

 recv(sockfd, recv_buf, BUFSIZ, 0);
 printf("[Server] %s\n", recv_buf);

 memset(send_buf, 0, BUFSIZ), memset(recv_buf, 0, BUFSIZ);
 }
 close(sockfd);
 exit(0);
}

運(yùn)行結(jié)果

編譯:

gcc server.c -o server -lpthread
gcc client.c -o client

先運(yùn)行 server,后運(yùn)行多個(gè) client .

需要注意的是,這里的服務(wù)器,客戶端都是運(yùn)行在同一機(jī)器上的,所以客戶端使用的目標(biāo) IP 是 127.0.0.1 ,如果想進(jìn)一步更全面地測(cè)試,應(yīng)該把服務(wù)端運(yùn)行在一個(gè)云服務(wù)器上,然后開放 8887 端口,再進(jìn)行測(cè)試。

到此這篇關(guān)于C語言多線程服務(wù)器的實(shí)現(xiàn)實(shí)例的文章就介紹到這了,更多相關(guān)C語言多線程服務(wù)器的實(shí)現(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++ QT智能指針的使用詳解

    C++ QT智能指針的使用詳解

    這篇文章主要介紹了C++ QT智能指針的使用,Qt是一個(gè)跨平臺(tái)的C++框架,主要用來開發(fā)圖形用戶界面程序,也可以開發(fā)不帶界面的命令行程序,下面我們來了解QT智能指針是如何使用的
    2023-12-12
  • 關(guān)于C語言文件操作方法

    關(guān)于C語言文件操作方法

    這篇文章主要介紹了關(guān)于C語言文件操作方法的相關(guān)資料,需要的朋友可以參考下
    2018-03-03
  • C++中的Lambda表達(dá)式及表達(dá)式語句

    C++中的Lambda表達(dá)式及表達(dá)式語句

    這篇文章主要介紹了C++中的Lambda表達(dá)式及表達(dá)式語句,表達(dá)式這個(gè)概念在C++中屬于比較細(xì)節(jié)的知識(shí)了,很多時(shí)候我們只用知道怎么用,對(duì)于編譯器內(nèi)部怎么處理我們并不關(guān)心;并且關(guān)于左值和右值這個(gè)概念,也是C++比較深的一個(gè)小知識(shí)點(diǎn),需要的朋友可以參考一下
    2021-12-12
  • C語言詳解strcmp函數(shù)的分析及實(shí)現(xiàn)

    C語言詳解strcmp函數(shù)的分析及實(shí)現(xiàn)

    strcmp函數(shù)語法為“int strcmp(char *str1,char *str2)”,其作用是比較字符串str1和str2是否相同,如果相同則返回0,如果不同,前者大于后者則返回1,否則返回-1
    2022-05-05
  • 一篇文章徹底弄懂C++虛函數(shù)的實(shí)現(xiàn)機(jī)制

    一篇文章徹底弄懂C++虛函數(shù)的實(shí)現(xiàn)機(jī)制

    C++中的虛函數(shù)的作用主要是實(shí)現(xiàn)了多態(tài)的機(jī)制,基類定義虛函數(shù),子類可以重寫該函數(shù),在派生類中對(duì)基類定義的虛函數(shù)進(jìn)行重寫時(shí),需要在派生類中聲明該方法為虛方法,這篇文章主要給大家介紹了關(guān)于如何通過一篇文章徹底弄懂C++虛函數(shù)的實(shí)現(xiàn)機(jī)制,需要的朋友可以參考下
    2021-06-06
  • 關(guān)于C++中二分法詳解

    關(guān)于C++中二分法詳解

    大家好,本篇文章主要講的是關(guān)于C++中二分法詳解,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-02-02
  • C++設(shè)計(jì)模式之狀態(tài)模式

    C++設(shè)計(jì)模式之狀態(tài)模式

    這篇文章主要介紹了C++設(shè)計(jì)模式之狀態(tài)模式,本文講解了什么是狀態(tài)模式、狀態(tài)模式的使用場(chǎng)合、狀態(tài)模式的實(shí)現(xiàn)代碼等內(nèi)容,需要的朋友可以參考下
    2014-10-10
  • C++實(shí)現(xiàn)哈希散列表的示例

    C++實(shí)現(xiàn)哈希散列表的示例

    本文主要介紹了C++實(shí)現(xiàn)哈希散列表的示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • 探討:C++實(shí)現(xiàn)鏈?zhǔn)蕉鏄?用非遞歸方式先序,中序,后序遍歷二叉樹)

    探討:C++實(shí)現(xiàn)鏈?zhǔn)蕉鏄?用非遞歸方式先序,中序,后序遍歷二叉樹)

    本篇文章是對(duì)用C++實(shí)現(xiàn)鏈?zhǔn)蕉鏄?用非遞歸方式先序,中序,后序遍歷二叉樹)的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • c語言_構(gòu)建一個(gè)靜態(tài)二叉樹實(shí)現(xiàn)方法

    c語言_構(gòu)建一個(gè)靜態(tài)二叉樹實(shí)現(xiàn)方法

    下面小編就為大家?guī)硪黄猚語言_構(gòu)建一個(gè)靜態(tài)二叉樹實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05

最新評(píng)論