前言:在學習網絡socket編程之前,我們最好了解一下socket的基礎知識,以及TCP,UDP協議是怎樣的?網上有大量的文章分析SOCKET的,感覺這位網友分析的特別好,強烈推薦:點擊打開鏈接
1.什麼是socket?
socket起源於Unix,而Unix/Linux基本哲學之一就是“一切皆文件”,都可以用“打開open –> 讀寫write/read –> 關閉close”模式來操作。Socket就是該模式的一個實現, socket即是一種特殊的文件,一些socket函數就是對其進行的操作(讀/寫IO、打開、關閉).Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議.
門面模式:我的理解就是系統對外界提供單一的接口,外部不需要了解內部的實現。
2.socket在哪裏?
一張圖說明一切:
3.TCP/IP,UDP分別是什麼?
TCP/IP(Transmission Control Protocol/Internet Protocol):傳輸控制協議/網間協議,是一個工業標準的協議集,它是爲廣域網(WANs)設計的,
TCP/IP協議是一個協議簇,裏面包括很多協議的,UDP只是其中的一個。之所以命名爲TCP/IP協議,因爲TCP,IP協議是兩個很重要的協議,就用他兩命名了.
tcp是面向連接的協議,也就是說,在收發數據前,必須和對方建立可靠的連接。比如打電話。
UDP(User Data Protocol,用戶數據報協議)是與TCP相對應的協議。它是屬於TCP/IP協議族中的一種.UDP是一個非連接的協議,
傳輸數據之前源端和終端不建立連接。比如發短信。
4.Socket編程的基本流程
一張圖一目瞭然:
服務器端先初始化Socket,然後與端口綁定(bind),對端口進行監聽(listen),調用accept阻塞,等待客戶端連接。在這時如果有個客戶端初始化一個Socket,然後連接服務器(connect),如果連接成功,這時客戶端與服務器端的連接就建立了。客戶端發送數據請求,服務器端接收請求並處理請求,然後把迴應數據發送給客戶端,客戶端讀取數據,最後關閉連接,一次交互結束。
實例:
server.c
/*********************************************************************************
* Copyright: (C) 2017 zoulei
* All rights reserved.
*
* Filename: server.c
* Description: This file
*
* Version: 1.0.0(2017年06月18日)
* Author: zoulei <[email protected]>
* ChangeLog: 1, Release initial version on "2017年06月18日 18時13分42秒"
*
********************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define SERV_PORT 9998
#define MAXLINE 4096
int main(int argc, char** argv)
{
int socket_fd, connect_fd;
struct sockaddr_in servaddr; /* 服務器端網絡地址結構體 */
char buf[MAXLINE],sendbuf[MAXLINE];
int len;
/*創建服務器端套接字--IPv4協議,面向連接通信,TCP協議*/
if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
/*初始化*/
memset(&servaddr, 0, sizeof(servaddr));/*數據初始化-清零 */
servaddr.sin_family = AF_INET; /*設置IPv4通信*/
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址設置成INADDR_ANY,讓系統自動獲取本機的IP地址。
servaddr.sin_port = htons(SERV_PORT);//設置服務器端口爲SERV_PORT
/*將本地地址綁定到所創建的套接字上*/
if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) <0)
{
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
/*開始監聽是否有客戶端連接*/
if( listen(socket_fd, 10) <0)
{
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("waiting for client's connection......\n");
/*阻塞直到有客戶端連接,不然多浪費CPU資源*/
if((connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) <0)
{
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
exit(1);
}
/*接受客戶端傳過來的數據*/
while((len= recv(connect_fd, buf, MAXLINE, 0))>0)
{
buf[len] = '\0';
printf("receive message from client: %s\n", buf);
/*向客戶端發送迴應數據*/
printf("send message to client: \n");
fgets(sendbuf, 4096, stdin);
if( send(connect_fd, sendbuf, strlen(sendbuf), 0) < 0)
{
printf("send messaeg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
}
close(connect_fd);
close(socket_fd);
}
client.c:
/*********************************************************************************
* Copyright: (C) 2017 zoulei
* All rights reserved.
*
* Filename: client.c
* Description: This file
*
* Version: 1.0.0(2017年06月12日)
* Author: zoulei <[email protected]>
* ChangeLog: 1, Release initial version on "2017年06月12日 20時06分47秒"
*
********************************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdlib.h>
#define MAXLINE 1024
#define SERV_PORT 9998
int main(int argc, char *argv[])
{
char sendbuf[MAXLINE],receivebuf[MAXLINE];
struct sockaddr_in servaddr;
int client_sockfd;
int rec_len;
/* 判斷命令端輸入的參數是否正確 */
if( argc != 2)
{
printf("usage: ./client <ipaddress>\n");
exit(0);
}
/* 創建客戶端套接字--IPv4協議,面向連接通信,TCP協議*/
if((client_sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
{
perror("socket");
exit(0);
}
/* 初始化 */
memset(&servaddr,0,sizeof(servaddr)); /* 數據初始化-清零 */
servaddr.sin_family = AF_INET; /* 設置IPv4通信 */
servaddr.sin_port = htons(SERV_PORT);/* 設置服務器端口號 */
/* IP地址轉換函數,將點分十進制轉換爲二進制 */
if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
{
printf("inet_pton error for %s\n",argv[1]);
exit(0);
}
/* 將套接字綁定到服務器的網絡地址上*/
if( connect(client_sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))<0)
{
perror("connected failed");
exit(0);
}
/* 循環發送接收數據,send發送數據,recv接收數據 */
while(1)
{
printf("send msg to server: \n");
fgets(sendbuf, 1024, stdin);
/* 向服務器端發送數據 */
if( send(client_sockfd, sendbuf, strlen(sendbuf), 0) < 0)
{
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
/* 接受服務器端傳過來的數據 */
if((rec_len = recv(client_sockfd,receivebuf, MAXLINE,0)) == -1)
{
perror("recv error");
exit(1);
}
receivebuf[rec_len]='\0';
printf("Response from server: %s\n",receivebuf);
}
/* 關閉套接字 */
close(client_sockfd);
return 0;
}
測試結果:
客服端:
服務器端: