Linux TCP套接字程式設計

2021-10-05 01:40:25 字數 4160 閱讀 9563

服務端程式設計的步驟如下:

建立服務端套接字。

繫結套接字到乙個ip位址和乙個埠上(使用bind函式)。

將套接字設定為監聽模式等待連線請求(使用函式listen),這個套接字就是監聽套接字了。

請求到來後,接受連線請求。返回乙個新的對應此連線的套接字。

用返回的新的套接字和客戶端進行通訊,即傳送或接收資料(使用send 或者 recv 函式),通訊結束就關閉這個新建立的套接字(使用函式 closesocket)。

監聽套接字繼續處於監聽狀態,等待其他客戶端的連線請求。

如果要退出伺服器程式,則先關閉監聽套接字(使用函式closesocket)。

客戶端程式設計的步驟如下:

建立客戶端套接字(使用函式 socket)。

向伺服器發出連線請求(使用函式 connect)。

和伺服器端進行通訊,即傳送或接收資料(使用函式 send 或 recv)。

如果要關閉客戶端程式,則先關閉套接字(使用函式closesocket)。

當使用函式 socket 建立套接字時,預設都是阻塞模式阻塞模式是指套接字在執行操作時,呼叫函式在沒有完成操作之前不會立即返回。也就是說,呼叫 api 函式時,不能立即完成,執行緒需要處於等待狀態,直到操作完成。但並不是呼叫所有的api函式都會阻塞,例如 bind(),listen(),即使用阻塞套接字當引數傳入,函式也會立即返回,不需要等待。

以下 linsock api 使用阻塞套接字當引數呼叫函式時,會發生阻塞。

接收連線函式

函式accept從請求連線佇列中接收乙個客戶端連線。若請求隊列為空,則函式就會阻塞,執行緒進入睡眠狀態。

傳送函式

函式send、sendto 都是傳送資料函式。如果套接字緩衝區沒有可用空間,函式就會阻塞,執行緒就會睡眠,直到緩衝區有空間。

接收函式

函式recv 用來接收資料。如果此時套接字緩衝區沒有資料可讀,則函式阻塞,呼叫執行緒在資料到來前處於睡眠狀態。

連線函式

函式 connect 用於向對方傳送連線請求。發出連線時,直到收到伺服器的應答或超時才會返回。

下面就用乙個簡單的伺服器客戶機聊天程式(非阻塞套接字版)來進行演示tcp通訊。

#include #include #include #include #include #include #include "unistd.h"

#include "errno.h"

#include int main()

printf("info: socket ok!\n");

int on = 1;

setsockopt(sfp, sol_socket, so_reuseaddr, &on, sizeof(on)); // 允許位址可立即重用

bzero(&saddr, sizeof(struct sockaddr_in));

saddr.sin_family = af_inet;

saddr.sin_addr.s_addr = htonl(inaddr_any); // inaddr_any = 0.0.0.0

saddr.sin_port = htons(portnum); // htons 主機位元組序轉換成網路位元組序

if (-1 == bind(sfp, (struct sockaddr *)(&saddr), sizeof(struct sockaddr)))

printf("info: bind ok!\n");

if (-1 == listen(sfp, 5)) // 5是最大連線數

printf("info: listen ok!\n");

while (1)

printf("info: accept ok!\n");

printf("info: server start to connect from ip=%s, port=%d\n",

inet_ntoa(caddr.sin_addr),

ntohs(caddr.sin_port)); // inet_ntoa 將二進位制位址轉換成點分十進位制位址

if (-1 == write(nfp, "hello, client, you are welcome!\n", 32))

printf("info: write ok!\n");

close(nfp);

puts("continue to listen(y/n)?");

char ch[2];

scanf("%s", ch, 2);

if (ch[0] != 'y')

break;

}printf("info: bye!\n");

close(sfp);

return 0;

}

服務端先新建乙個監聽套接字,然後等待客戶端的連線請求,阻塞在accept函式處,一旦有客戶端連線請求來了,就返回乙個新的套接字。這個套接字就和客戶端進行通訊,通訊完畢後關掉這個套接字。而監聽套接字根據使用者的輸入繼續監聽或退出。

#include #include #include #include #include #include #include #include #include #include #include #include "unistd.h"

#include const int buffer_size = 1023;

// 將套接字設定為非阻塞

int setnonblocking(int fd)

// 非阻塞連線,time是超時時間。

int unblockconnect(const char* ip, int port, int time)

else if (einprogress != errno) // 連線失敗

else if (einprogress == errno) // 連線中

// 呼叫select函式判斷連線是否成功

// 如果套接字是 writable,那麼套接字連線成功

fd_set readfds;

fd_set writefds;

struct timeval timeout;

fd_zero(&readfds);

fd_set(sockfd, &writefds);

// 設定超時時間

timeout.tv_sec = time;

timeout.tv_usec = 0;

ret = select(sockfd + 1, null, &writefds, null, &timeout);

if (ret < 0)

if (!fd_isset(sockfd, &writefds))

// 如果套接字描述符可讀或可寫,則我們用getsockopt來得到套接字上待處理的錯誤

// 如果連線建立成功,這個錯誤值將是0。

int error = 0;

socklen_t length = sizeof(error);

if (getsockopt(sockfd, sol_socket, so_error, &error, &length) < 0)

if (0 != error)

printf("info: connection ready after select with the socket:%d\n",sockfd);

fcntl(sockfd, f_setfl, fdopt); // 將套接字恢復成原來屬性

printf("info: connect ok!\n");

int recvbytes;

int sinsize;

char buf[1024] = ;

recvbytes = read(sockfd, buf, 1024); // 接收資料

if (-1 == recvbytes)

printf("info: read ok!\n");

buf[recvbytes] = '\0';

printf("%s\n", buf);

return sockfd;

}int main()

close(sockfd);

return 0;

}

套接字程式設計

1 ipv4 通用套接字的位址結構 struct socketaddr2 ipv6 3 值 結果引數 有點迷糊 套接字位址結構大小作為乙個指標傳給核心的原因 當函式被核心呼叫時,大小結構是乙個值 value 告訴核心該結構的大小,核心在寫該結構時不會越界。當函式返回時,結構大小是乙個結果 resul...

套接字程式設計

例1 建立sockaddr in結構體變數,並繫結套接字 建立套接字 int serv sock socket af inet,sock stream,ipproto tcp 建立sockaddr in結構體變數 struct sockaddr in serv addr memset serv ad...

python套接字程式設計 python套接字程式設計

服務端 usr bin python coding utf 8 import socket s socket.socket host socket.gethostname port 12345 s.bind host,port s.listen 5 while true c,addr s.accep...