linux下TCP通信简单实例
linux下TCP通信簡單實例
基于TCP(面向連接)的socket編程,分為服務器端和客戶端
服務器端的流程如下:
(1)創建套接字(socket)
(2)將套接字綁定到一個本地地址和端口上(bind)
(3)將套接字設為監聽模式,準備接收客戶端請求(listen)
(4)等待客戶請求到來;當請求到來后,接受連接請求,返回一個新的對應于此次連接的套接字(accept)
(5)用返回的套接字和客戶端進行通信(send/recv)
(6)返回,等待另一個客戶請求。
(7)關閉套接字。
客戶端的流程如下:
(1)創建套接字(socket)
(2)向服務器發出連接請求(connect)
(3)和服務器端進行通信(send/recv)
(4)關閉套接字
下面通過一個具體例子講解一下具體的過程和相關的函數,環境是suse Linux。
服務器端:
#include <stdio.h> ? ?
#include <stdlib.h> ? ?
#include <strings.h> ? ?
#include <sys/types.h> ? ?
#include <sys/socket.h> ? ?
#include <memory.h> ? ?
#include <unistd.h> ? ?
//#include <linux/in.h> ? ?
#include <netinet/in.h> ? ?
//#include <linux/inet_diag.h> ? ?
#include <arpa/inet.h> ? ?
? ??
#include <signal.h> ? ?
? ??
/** ?
? 關于 sockaddr ?sockaddr_in ?socketaddr_un說明 ?
? http://maomaozaoyue.blog.sohu.com/197538359.html ?
? */ ? ?
? ??
#define PORT ? ?11910 ? //定義通信端口 ? ?
#define BACKLOG 5 ? ? ? //定義偵聽隊列長度 ? ?
#define buflen ?1024 ? ?
? ??
void process_conn_server(int s); ? ?
void sig_pipe(int signo); ? ?
? ??
int ss,sc; ?//ss為服務器socket描述符,sc為某一客戶端通信socket描述符 ? ?
? ??
int main(int argc,char *argv[]) ? ?
{ ? ?
? ??
? ? struct sockaddr_in server_addr; //存儲服務器端socket地址結構 ? ?
? ? struct sockaddr_in client_addr; //存儲客戶端 socket地址結構 ? ?
? ??
? ? int err; ? ?//返回值 ? ?
? ? pid_t pid; ?//分叉進行的ID ? ?
? ??
? ? /*****************socket()***************/ ? ?
? ? ss = socket(AF_INET,SOCK_STREAM,0); //建立一個序列化的,可靠的,雙向連接的的字節流 ? ?
? ? if(ss<0) ? ?
? ? { ? ?
? ? ? ? printf("server : server socket create error\n"); ? ?
? ? ? ? return -1; ? ?
? ? } ? ?
? ? //注冊信號 ? ?
? ? sighandler_t ret; ? ?
? ? ret = signal(SIGTSTP,sig_pipe); ? ?
? ? if(SIG_ERR == ret) ? ?
? ? { ? ?
? ? ? ? printf("信號掛接失敗\n"); ? ?
? ? ? ? return -1; ? ?
? ? } ? ?
? ? else ? ?
? ? ? ? printf("信號掛接成功\n"); ? ?
? ??
? ??
? ? /******************bind()****************/ ? ?
? ? //初始化地址結構 ? ?
? ? memset(&server_addr,0,sizeof(server_addr)); ? ?
? ? server_addr.sin_family = AF_INET; ? ? ? ? ? //協議族 ? ?
? ? server_addr.sin_addr.s_addr = htonl(INADDR_ANY); ? //本地地址 ? ?
? ? server_addr.sin_port = htons(PORT); ? ?
? ??
? ? err = bind(ss,(struct sockaddr *)&server_addr,sizeof(sockaddr)); ? ?
? ? if(err<0) ? ?
? ? { ? ?
? ? ? ? printf("server : bind error\n"); ? ?
? ? ? ? return -1; ? ?
? ? } ? ?
? ??
? ? /*****************listen()***************/ ? ?
? ? err = listen(ss,BACKLOG); ? //設置監聽的隊列大小 ? ?
? ? if(err < 0) ? ?
? ? { ? ?
? ? ? ? printf("server : listen error\n"); ? ?
? ? ? ? return -1; ? ?
? ? } ? ?
? ??
? ? /****************accept()***************/ ? ?
? ? /** ?
? ? 為類方便處理,我們使用兩個進程分別管理兩個處理: ?
? ? 1,服務器監聽新的連接請求;2,以建立連接的C/S實現通信 ?
? ? 這兩個任務分別放在兩個進程中處理,為了防止失誤操作 ?
? ? 在一個進程中關閉 偵聽套接字描述符 另一進程中關閉 ?
? ? 客戶端連接套接字描述符。注只有當所有套接字全都關閉時 ?
? ? 當前連接才能關閉,fork調用的時候父進程與子進程有相同的 ?
? ? 套接字,總共兩套,兩套都關閉掉才能關閉這個套接字 ?
? ? */ ? ?
? ??
? ? for(;;) ? ?
? ? { ? ?
? ? ? ? socklen_t addrlen = sizeof(client_addr); ? ?
? ? ? ? //accept返回客戶端套接字描述符 ? ?
? ? ? ? sc = accept(ss,(struct sockaddr *)&client_addr,&addrlen); ?//注,此處為了獲取返回值使用 指針做參數 ? ?
? ? ? ? if(sc < 0) ?//出錯 ? ?
? ? ? ? { ? ?
? ? ? ? ? ? continue; ? //結束此次循環 ? ?
? ? ? ? } ? ?
? ? ? ? else ? ?
? ? ? ? { ? ?
? ? ? ? ? ? printf("server : connected\n"); ? ?
? ? ? ? } ? ?
? ??
? ? ? ? //創建一個子線程,用于與客戶端通信 ? ?
? ? ? ? pid = fork(); ? ?
? ? ? ? //fork 調用說明:子進程返回 0 ;父進程返回子進程 ID ? ?
? ? ? ? if(pid == 0) ? ? ? ?//子進程,與客戶端通信 ? ?
? ? ? ? { ? ?
? ? ? ? ? ? close(ss); ? ?
? ? ? ? ? ? process_conn_server(sc); ? ?
? ? ? ? } ? ?
? ? ? ? else ? ?
? ? ? ? { ? ?
? ? ? ? ? ? close(sc); ? ?
? ? ? ? } ? ?
? ? } ? ?
} ? ?
? ??
/** ?
? 服務器對客戶端連接處理過程;先讀取從客戶端發送來的數據, ?
? 然后將接收到的數據的字節的個數發送到客戶端 ?
? */ ? ?
? ??
//通過套接字 s 與客戶端進行通信 ? ?
void process_conn_server(int s) ? ?
{ ? ?
? ? ssize_t size = 0; ? ?
? ? char buffer[buflen]; ?//定義數據緩沖區 ? ?
? ? for(;;) ? ?
? ? { ? ?
? ? ? ? //等待讀 ? ?
? ? ? ? for(size = 0;size == 0 ;size = read(s,buffer,buflen)); ? ?
? ? ? ? //輸出從客戶端接收到的數據 ? ?
? ? ? ? printf("%s",buffer); ? ?
? ??
? ? ? ? //結束處理 ? ?
? ? ? ? if(strcmp(buffer,"quit") == 0) ? ?
? ? ? ? { ? ?
? ? ? ? ? ? close(s); ? //成功返回0,失敗返回-1 ? ?
? ? ? ? ? ? return ; ? ?
? ? ? ? } ? ?
? ? ? ? sprintf(buffer,"%d bytes altogether\n",size); ? ?
? ? ? ? write(s,buffer,strlen(buffer)+1); ? ?
? ? } ? ?
} ? ?
void sig_pipe(int signo) ? ?
{ ? ?
? ? printf("catch a signal\n"); ? ?
? ? if(signo == SIGTSTP) ? ?
? ? { ? ?
? ? ? ? printf("接收到 SIGTSTP 信號\n"); ? ?
? ? ? ? int ret1 = close(ss); ? ?
? ? ? ? int ret2 = close(sc); ? ?
? ? ? ? int ret = ret1>ret2?ret1:ret2; ? ?
? ? ? ? if(ret == 0) ? ?
? ? ? ? ? ? printf("成功 : 關閉套接字\n"); ? ?
? ? ? ? else if(ret ==-1 ) ? ?
? ? ? ? ? ? printf("失敗 : 未關閉套接字\n"); ? ?
? ??
? ? ? ? exit(1); ? ?
? ? } ? ?
} ? ?
客戶端:
#include <stdio.h> ? ?
? ? #include <strings.h> ? ?
? ? #include <unistd.h> ? ?
? ? #include <sys/types.h> ? ?
? ? #include <sys/socket.h> ? ?
? ? //#include <linux/in.h> ? ?
? ? #include <stdlib.h> ? ?
? ? #include <memory.h> ? ?
? ? #include <arpa/inet.h> ? ?
? ? #include <netinet/in.h> ? ?
? ? ? ??
? ? #include <signal.h> //添加信號處理 ?防止向已斷開的連接通信 ? ?
? ? ? ??
? ? /** ?
? ? ? 信號處理順序說明:在Linux操作系統中某些狀況發生時,系統會向相關進程發送信號, ?
? ? ? 信號處理方式是:1,系統首先調用用戶在進程中注冊的函數,2,然后調用系統的默認 ?
? ? ? 響應方式,此處我們可以注冊自己的信號處理函數,在連接斷開時執行 ?
? ? ? */ ? ?
? ? ? ??
? ? ? ??
? ? #define PORT ? ?11910 ? ?
? ? #define Buflen ?1024 ? ?
? ? ? ??
? ? void process_conn_client(int s); ? ?
? ? void sig_pipe(int signo); ? ?//用戶注冊的信號函數,接收的是信號值 ? ?
? ? ? ??
? ? int s; ?//全局變量 , 存儲套接字描述符 ? ?
? ? ? ??
? ? int main(int argc,char *argv[]) ? ?
? ? { ? ?
? ? ? ??
? ? ? ? sockaddr_in server_addr; ? ?
? ? ? ? int err; ? ?
? ? ? ? sighandler_t ret; ? ?
? ? ? ? char server_ip[50] = ""; ? ?
? ? ? ? int port = 0; ?
? ? ? ? strcpy(server_ip, argv[1]); ?
? ? ? ? port = atoi(argv[2]); ?
??
? ? ? ? /********************socket()*********************/ ? ?
? ? ? ? s= socket(AF_INET,SOCK_STREAM,0); ? ?
? ? ? ? if(s<0) ? ?
? ? ? ? { ? ?
? ? ? ? ? ? printf("client : create socket error\n"); ? ?
? ? ? ? ? ? return 1; ? ?
? ? ? ? } ? ?
? ? ? ? printf("client : socket fd = %d\n", s); ??
? ? ? ? //信號處理函數 ?SIGINT 是當用戶按一個 Ctrl-C 建時發送的信號 ? ?
? ? ? ? ret = signal(SIGTSTP,sig_pipe); ? ?
? ? ? ? if(SIG_ERR == ret) ? ?
? ? ? ? { ? ?
? ? ? ? ? ? printf("信號掛接失敗\n"); ? ?
? ? ? ? ? ? return -1; ? ?
? ? ? ? } ? ?
? ? ? ? else ? ?
? ? ? ? ? ? printf("信號掛接成功\n") ; ? ?
? ? ? ??
? ? ? ??
? ? ? ? /*******************connect()*********************/ ? ?
? ? ? ? //設置服務器地址結構,準備連接到服務器 ? ?
? ? ? ? memset(&server_addr,0,sizeof(server_addr)); ? ?
? ? ? ? server_addr.sin_family = AF_INET; ? ?
? ? ? ? server_addr.sin_port = htons(PORT); ? ?
? ? ? ? server_addr.sin_addr.s_addr = htonl(INADDR_ANY); ? ?
? ? ? ??
? ? ? ? /*將用戶數入對額字符串類型的IP格式轉化為整型數據*/ ? ?
? ? ? ? //inet_pton(AF_INET,argv[1],&server_addr.sin_addr.s_addr); ? ?
? ? ? ? printf("please input server ip address : \n"); ? ?
? ? ? ? read(0,server_ip,50); ? ?
? ? ? ? //err = inet_pton(AF_INET,server_ip,&server_addr.sin_addr.s_addr); ? ?
? ? ? ? server_addr.sin_addr.s_addr = inet_addr(server_ip); ? ?
? ? ? ??
? ? ? ? err = connect(s,(struct sockaddr *)&server_addr,sizeof(sockaddr)); ? ?
? ? ? ? if(err == 0) ? ?
? ? ? ? { ? ?
? ? ? ? ? ? printf("client : connect to server\n"); ? ?
? ? ? ? } ? ?
? ? ? ? else ? ?
? ? ? ? { ? ?
? ? ? ? ? ? printf("client : connect error\n"); ? ?
? ? ? ? ? ? return -1; ? ?
? ? ? ? } ? ?
? ? ? ? //與服務器端進行通信 ? ?
? ? ? ? process_conn_client(s); ? ?
? ? ? ? close(s); ? ?
? ? ? ??
? ? } ? ?
? ? void process_conn_client(int s) ? ?
? ? { ? ?
? ? ? ??
? ? ? ? ssize_t size = 0; ? ?
? ? ? ? char buffer[Buflen]; ? ?
? ? ? ??
? ? ? ? for(;;) ? ?
? ? ? ? { ? ?
? ? ? ? ? ? memset(buffer,'\0',Buflen); ? ?
? ? ? ? ? ? /*從標準輸入中讀取數據放到緩沖區buffer中*/ ? ?
? ? ? ? ? ? size = read(0,buffer,Buflen); ? // 0,被默認的分配到標準輸入 ?1,標準輸出 ?2,error ? ?
? ? ? ? ? ? if(size > ?0) ? ?
? ? ? ? ? ? { ? ?
? ? ? ? ? ? ? ? //當向服務器發送 “quit” 命令時,服務器首先斷開連接 ? ?
? ? ? ? ? ? ? ? write(s,buffer,strlen(buffer)+1); ? //向服務器端寫 ? ?
? ? ? ??
? ? ? ? ? ? ? ? //等待讀取到數據 ? ?
? ? ? ? ? ? ? ? for(size = 0 ; size == 0 ; size = read(s,buffer,Buflen) ); ? ?
? ? ? ??
? ? ? ? ? ? ? ? write(1,buffer,strlen(buffer)+1); ? //向標準輸出寫 ? ?
? ? ? ? ? ? } ? ?
? ? ? ? } ? ?
? ? } ? ?
? ? ? ??
? ? void sig_pipe(int signo) ? ?//傳入套接字描述符 ? ?
? ? { ? ?
? ? ? ? printf("Catch a signal\n"); ? ?
? ? ? ? if(signo == SIGTSTP) ? ?
? ? ? ? { ? ?
? ? ? ??
? ? ? ? ? ? printf("接收到 SIGTSTP 信號\n"); ? ?
? ? ? ? ? ? int ret = close(s); ? ?
? ? ? ? ? ? if(ret == 0) ? ?
? ? ? ? ? ? ? ? printf("成功 : 關閉套接字\n"); ? ?
? ? ? ? ? ? else if(ret ==-1 ) ? ?
? ? ? ? ? ? ? ? printf("失敗 : 未關閉套接字\n"); ? ?
? ? ? ? ? ? exit(1); ? ?
? ? ? ? } ? ?
? ? } ?
總結
以上是生活随笔為你收集整理的linux下TCP通信简单实例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL笔记14:常用命令
- 下一篇: linux网络编程一:主机字节序与网络字