概念:
TCP網絡程式設計有兩種模式,一種是伺服器模式,另一種是用戶端模式。
(1)伺服器模式建立一個服務程式,等待用戶端使用者的連接配接,接收到使用者的連接配接請求後,根據使用者的請求進行處理;
(2)用戶端模式則根據目的伺服器的位址和端口進行連接配接,向伺服器發送請求并對伺服器的響應進行資料處理。
伺服器端的程式設計模式:
流程主要分為
(1)套接字初始化(socket())
(2)套接字與端口的綁定(bind())
(3)設定伺服器的偵聽連接配接(listen())
(4)接受用戶端連接配接(accept())
(5)接收和發送資料(read()、write())
(6)并進行資料處理及處理完畢的套接字關閉(close())。
用戶端的程式設計模式:
用戶端模式分為
(1)套接字初始化(socket())
(2)連接配接伺服器(connect())
(3)讀寫網絡資料(read()、write())
(4)并進行資料處理和最後的套接字關閉(close())。
用戶端與伺服器的互動過程:
用戶端與伺服器在連接配接、讀寫資料、關閉過程中有互動過程。

函數介紹:
1、socket()函數介紹
如果函數調用成功,會傳回一個表示這個套接字的檔案描述符,失敗的時候傳回–1。
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
(1)domain即協定域,又稱為協定族(family),AF_INET
(2)type指定socket類型。
常用的socket類型有:SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等。
(3)protocol:故名思意,就是指定協定,一般設定為0
舉例:int sock=socket(AF_INET,SOCK_STREAM,0)
2、綁定一個位址端口對bind()
在伺服器設計中,建立套接字檔案描述符成功後,需要對套接字進行位址和端口的綁定,才能進行資料的接收和發送操作。
bind()函數介紹
bind()函數将長度為addlen的struct sockadd類型的參數my_addr與sockfd綁定在一起,将sockfd綁定到某個端口上。
綁定的函數原型如下:
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
3、接受一個網絡請求accept()
當一個用戶端的連接配接請求到達伺服器主機偵聽的端口時,此時用戶端的連接配接會在隊列中等待,直到使用伺服器處理接收請求。
函數accept()成功執行後,會傳回一個新的套接字檔案描述符來表示用戶端的連接配接,用戶端連接配接的資訊可以通過這個新描述符來獲得。
accept()函數的原型如下:
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
第一個參數為伺服器的socket描述字
第二個參數為指向struct sockaddr *的指針,用于傳回用戶端的協定位址
第三個參數為指向協定位址長度指針。
如果accpet成功,那麼其傳回值是由核心自動生成的一個全新的描述字,代表與傳回客戶的TCP連接配接;否則傳回-1;
注意:
1.accept的第一個參數為伺服器的socket描述字,是伺服器開始調用socket()函數生成的,稱為監聽socket描述字,一個伺服器通常僅僅隻建立一個監聽socket描述字,它在該伺服器的生命周期内一直存在。
2.而accept函數傳回的是已連接配接的socket描述字。核心為每個由伺服器程序接受的客戶連接配接建立了一個已連接配接socket描述字,當伺服器完成了對某個客戶的服務,相應的已連接配接socket描述字就被關閉。
4、連接配接目标網絡伺服器connect()
用戶端在建立套接字之後,不需要進行位址綁定就可以直接連接配接伺服器。連接配接伺服器的函數為connect(),此函數連接配接指定參數的伺服器,例如IP位址、端口等。
connect()函數的原型如下。
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, struct sockaddr* addr, int addrlen);
基于TCP協定的用戶端伺服器:
用戶端:
//TCPclient.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<pthread.h>
#include<string.h>
#include<arpa/inet.h>
#define PORT 8000
int main()
{
int sockfd , ret;
struct sockaddr_in server_addr;
char buf[32] = {0};
sockfd = socket(PF_INET , SOCK_STREAM , 0); //建立一個socket
if(sockfd == -1)
{
perror("socket");
exit(1);
}
int length = sizeof(server_addr);
//bind的參數 第二個是結構體 下面是結構體
server_addr.sin_family = PF_INET; //PF_INET 位址族
server_addr.sin_port = PORT; //端口号
server_addr.sin_addr.s_addr = inet_addr("192.168.1.10"); //寫伺服器位址
ret = connect(sockfd , (struct sockaddr *)&server_addr , length);
if(-1 == ret)
{
perror("connect");
exit(1);
}
while(1)
{
scanf("%s", buf);
ret = send(sockfd , buf , strlen(buf) , 0);
if(-1 == ret)
{
perror("send");
}
memset(buf , 0 ,sizeof(buf));
}
return 0;
}
伺服器:
//TCPserver.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<pthread.h>
#include<string.h>
#define PORT 8000
void *ClientHandle(void *arg)
{
pthread_detach( pthread_self() ); //線程分離
int ret;
int fd = *(int *)arg;
char buf[32]={0};
ret = recv(fd , buf , sizeof(buf) , 0);
if(-1 == ret)
{
perror("recv");
}
printf("recv from %d client %s!\n" , fd , buf);
memset(buf , 0 , sizeof(buf) );
}
int main()
{
int sockfd,ret;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int fd[1000] = {0} , i=0;
pthread_t tid;
//采用IPV4協定 流式套接字
sockfd = socket(PF_INET , SOCK_STREAM , 0); //建立一個socket
if(-1 == sockfd)
{
perror("socket");
exit(1);
}
int length = sizeof(server_addr);
//bind的參數 第二個是結構體 下面是結構體
server_addr.sin_family = PF_INET; //PF_INET 位址族
server_addr.sin_port = PORT; //端口号
server_addr.sin_addr.s_addr = inet_addr("192.168.1.10"); //ip位址
ret = bind(sockfd , (struct sockaddr *)&server_addr , length); //綁定
if(-1 == ret)
{
perror("bind");
exit(1);
}
ret = listen(sockfd ,5); //監聽
if(-1 == ret)
{
perror("listen");
exit(1);
}
while(1)
{
memset( &client_addr , 0 , sizeof(client_addr) );
fd[i] = accept(sockfd , (struct sockaddr *)&client_addr , &length);
if(-1 == fd[i])
{
perror("accept");
exit(1);
}
printf("accept client port %d fd %d\n",client_addr.sin_port,fd[i]);
//加線程 循環一次就建立一個線程 線程id 屬性内容 函數名 &fd[i]
ret = pthread_create(&tid , NULL , ClientHandle , &fd[i]);
if(ret != 0)
{
perror("pthread_create");
}
i++;
//等待線程結束 回收資源 但是會阻塞
//pthread_join();
}
return 0;
}