Linux-TCP/IP socket程式設計
伺服器
1、建立連線
socket(),分配檔案描述符,即監聽套接字
bind(),將套接字與本地IP地址和埠繫結
listen(),監聽特定埠,可設定監聽連線最大個數
accept(),阻塞等待客戶端連線
2、資料收發
read()/recv()阻塞等待客戶端傳送資料,收到資料後從read()/recv()返回資料和資料數量
write()/send()將處理結果傳送給客戶端,然後繼續呼叫read()/recv()等待客戶端請求
3、關閉連線
當read()/recv()返回0的時候,說明客戶端發來FIN資料包,即關閉連線,呼叫close()關閉連線套接字和監聽套接字
當客戶端呼叫close()關閉連線套接字時也會受到客戶端發開的斷開連線請求
客戶端
1、建立連線
socket(),分配檔案描述符
connect(),向伺服器傳送建立連線請求
2、資料收發
write()/send(),將資料傳送給伺服器
read()/recv(),阻塞等待伺服器應答回覆資料
3、關閉連線
當沒有資料傳送的時候,呼叫close()關閉連線套接字,即關閉連線,向伺服器傳送FIN資料報
或者write()/send()傳送0位元組給伺服器表示斷開連線
TCP通訊過程
圖片轉載自:https://blog.csdn.net/upupday19/article/details/78916142
示例程式碼
標頭檔案:std.h
#ifndef _STD_ #define _STD_ // 標頭檔案 // #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <dirent.h> #include <sys/types.h> #include <grp.h> #include <string.h> #include <time.h> #include <string.h> #include <errno.h> //tcp// #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h> //錯誤提示// #define err_exit(function) \ do { \ fprintf(stderr, "in %s at %s %d:\n%s : %s\n", __FUNCTION__, __FILE__, __LINE__ - 1, function, strerror(errno)); \ exit(EXIT_FAILURE); \ } while(0) #endif
伺服器程式碼:test_tcp_server.c
//伺服器端//
#include "std.h"
int main(void)
{
//定義伺服器監聽套接字和連線套接字
int listenfd = -1;
int connfd = -1;
struct sockaddr_in server, client;//定義伺服器對應的套接字地址
char buf[BUFSIZ];
int ret=-1;
//初始化套接字地址結構體
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;//IPv4
server.sin_port = htons(888);//設定監聽埠
//server.sin_addr.s_addr = inet_addr("192.168.1.50");//誰的地址?
//server.sin_addr.s_addr = inet_addr("0");//?
//server.sin_addr.s_addr = INADDR_ANY;//?
server.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY接收任意IP的連線請求
//建立套接字
if (0 > (listenfd = socket(AF_INET, SOCK_STREAM, 0)))
err_exit("socket");
//繫結套接字和本地IP地址和埠
if (0 > bind(listenfd, (struct sockaddr *)&server, sizeof(server)))
err_exit("bind");
//設定listen_fd為監聽描述符
listen(listenfd, 1024);//1024為最大連線個數
printf("listen...\n");
//accept阻塞等待客戶端請求
memset(&client, 0, sizeof(client));
socklen_t len = sizeof(client);
if (0 > (connfd = accept(listenfd, (struct sockaddr *)&client, &len)))
err_exit("accept");
printf("client's ip is: %s, port is: %d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
fflush(stdout);
while(1)
{
//讀取客戶端發來的資訊
memset(buf, 0, sizeof(buf));
if (0 > (ret = recv(connfd, buf, BUFSIZ, 0)))
err_exit("recv");
else if (0 == ret)//客戶端關閉連線請求斷開或傳送0位元組資料會走這裡
{
printf("client quit!\n");
//關閉連線套接字
close(connfd);
//關閉監聽套接字
close(listenfd);
exit(0);
}
buf[ret] = '\0';
printf("recv client: %s\n", buf);
//向客戶端傳送資訊
send(connfd, "ok", sizeof("ok"), 0);
}
}
客戶端程式碼:test_tcp_client.c
//客戶端//
#include "std.h"
int main(void)
{
int connfd = -1;//定義客戶端套接字
struct sockaddr_in server;//定義伺服器的套接字地址
char buf[BUFSIZ];//stdlib.h定義BUFSIZ=8192
int ret=-1;
//初始化伺服器套接字地址
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;//IPv4
server.sin_port = htons(888);//伺服器埠
server.sin_addr.s_addr = inet_addr("192.168.1.50");//伺服器IP地址
//建立套接字
if (0 > (connfd = socket(AF_INET, SOCK_STREAM, 0)))
err_exit("socket");
//傳送連線請求
if (0 > connect(connfd, (struct sockaddr *)&server, sizeof(server)))
err_exit("connect");
printf("connect success!\n");
//輸入並向伺服器傳送字串
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
send(connfd, buf, strlen(buf)-1, 0);//-1目的是去除\n字元
//從伺服器接收資料
if (0 > (ret = recv(connfd, buf, BUFSIZ, 0)))
err_exit("recv");
else if (0 == ret) {
printf("server quit!\n");
close(connfd);
}
buf[ret] = '\0';
printf("recv server: %s\n", buf);
//memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
send(connfd, buf, strlen(buf)-1, 0);
close(connfd);
exit(0);
}
擴充套件
select,poll,epoll實現機制區別:https://www.cnblogs.com/aspirant/p/9166944.html