c--socket通信TCP篇


程序調處來時候還是很高興的。server向client發消息,client向server發消息。

//s.c

 1 #include <sys/socket.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <stdio.h>
 5 #include <linux/in.h>  
 6 #include <sys/types.h>  
 7     
 8 #define PORT 7891
 9 
10 int main()
11 {
12     int socketfd,accsocfd;
13     struct sockaddr_in s_addr,r_addr;
14     socklen_t len; 
15     int recdata;
16     char buf[1024];
17     memset(buf,0x00,sizeof(buf));
18     //創建套接字
19     if(-1 == (socketfd = socket(AF_INET,SOCK_STREAM,0))){
20             printf("socketfd is created failed!\n");
21             return -1;        
22     }        ;    
23      printf("socket create success!\n"); 
24      
25     //將本地協議地址與sockfd綁定
26     memset(&s_addr,0x00,sizeof(s_addr));
27     s_addr.sin_family = PF_INET;
28     s_addr.sin_port = htons(PORT);
29     s_addr.sin_addr.s_addr = htons(INADDR_ANY);//inet_addr_any 一個服務器可能有多個網卡,隨便從中選1個
30     if(-1 == bind(socketfd,(struct sockaddr*)&s_addr,sizeof(s_addr))){
31         printf("bind failed!\n");            
32         return -1;
33     }
34     printf("bind suc!\n");
35     
36     //監聽本地端口
37     if(-1 == listen(socketfd,10)){
38         printf("listen failed!\n");
39         return -1;
40         }
41     printf("listen suc!\n");
42     
43     while(1){
44             len = sizeof(struct sockaddr);
45             accsocfd = accept(socketfd,(struct sockaddr *)&r_addr,&len);
46             if(-1 == accsocfd){
47                     printf("accept failed!\n");
48                     return -1;
49                 }
50                 printf("accept suc !\nServer get connect from %x port is %x",ntohl(r_addr.sin_addr.s_addr),ntohl(r_addr.sin_port));
51         
52         
53         //向客服端發送數據
54         if(-1 == write(accsocfd,"this is first data from sr!\n",50)){
55             printf("write failed!\n");
56             return -1;
57         }
58         printf("write suc!\n");
59         
60           
61          printf("*********************\n");
62 
63        char recvBuf[100];
64  
65     if(-1 ==recv(accsocfd, recvBuf, 100, 0)){
66             printf("recv failed!\n");
67             return -1;
68         }
69     printf("recv suc!\n");
70     printf("recvBuf  = [%s]\n",recvBuf);
71         printf("recvBuf len is = [%d]\n",strlen(recvBuf));
72         
73         close(accsocfd);
74         
75     }
76     close(socketfd);
77     return 0;
78 }

//c.c

#include<sys/socket.h>
#include<string.h>
#include<linux/in.h>
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>
/*流程:

TCP:(面向連接、可靠)

服務器端 WSAStartup->socket->bind->listen->accept->recv/send->closesocket->WSACleanup
客戶端:WSAStartup->socket->connect->recv/send->closesocket->WSACleanup
*/
#define PORT 7891
int main()
{
    int csocfd;
    int recdata;    
    char buf[1024];
    memset(buf,0x00,sizeof(buf));
    struct sockaddr_in mysockaddr;
    //創建套接字
    if(-1 == (csocfd = socket(PF_INET,SOCK_STREAM,0))){
            printf("csocfd failed!\n");
            return -1;
    }
    printf("csocfd suc!\n");
    
    //設置服務器的地址

    memset(&mysockaddr,0x00,sizeof(mysockaddr));
    mysockaddr.sin_family = PF_INET;
    mysockaddr.sin_port = htons(PORT);
    inet_pton(AF_INET,"172.19.230.113",&mysockaddr.sin_addr.s_addr);
//s_add.sin_addr.s_addr= inet_addr("172.19.230.113"); /* ip轉換為4字節整形,使用時需要根據服務端ip進行更改 */  


    //connect to the sr
if(-1 == connect(csocfd,(struct sockaddr*)&mysockaddr,sizeof(mysockaddr))){
            printf("connect failed!\n");
            return -1;
        }
    printf("connect suc!\n");
    
    
    if(-1 == (recdata = read(csocfd,buf,sizeof(buf)))){
            printf("read failed!\n");
            return -1;
    }
    printf("read suc!\n");
    buf[recdata ] = '\0';
    printf("recdata  = [%s]\n",buf);
    printf("recdata len is = [%d]\n",recdata);
    
    
    //memcpy(sendbuf,"this is data from cl!\n",);
    if(-1 == send(csocfd, "this is data from cl!", 100, 0)){
            printf("send faile\n");
            return -1;
        }
    printf("send suc!\n");
    
    
    close(csocfd);
    
    
}

學習筆記也附上吧

0.socket編程的目的是為了解決網絡上不同主機上的進程之間通信問題
網絡中的數據傳輸實際上是一種I/O操作
socket描述符可以同文件操作符進行比較,可以用read、write、close等操作,socket代表通信管道的一個端點

1.C/S模式(以面向連接為例子)
服務器工作過程:
打開一個通信通道,並告訴本地主機,服務器開了一特定端口接受客服請求。
等待客戶請求。
接收到客戶請求之后發送應答信號,創建一個新線程處理請求。
服務完成之后關閉通信通道和線程
繼續等待客戶請求。
客服端的工作過程:
打開一通信通道,連接到服務器的制定端口。
向服務器發送請求,並等待接受應答。
根據需要繼續發送請求
請求結束后關閉通信信道
2. Socket 類型常用的有2種:流式:SOCK_STREAM 數據報:SOCK_DGRAM
主機字節序數據轉換成網絡字節序數據
uint32_t htonl(uint32_t hostint32);
uint16_t htons(uint16_t hostint16);
網絡字節序數據轉換成主機字節序數據
uint32_t ntohl(uint32_t netint32);
uint16_t ntohs(uint16_t netint16);

3.創建套接字
int socket(int family, int type,intprotocol);
功能:創建一個用於網絡通信的I/O描述符(套接字)
參數:family:協議族 AF_INET,AF_INET6,AF_LOCAL,AF_ROUTE,AF_KEY
type: 套接字類型 套接字類型
protocol 協議類別 0,IPPROTO_TCP,IPPROTO_UDP,IPPROTO_SCTP
返回值:套接字
特點:(1)使用socket創建套接字時,系統不會分配端口 (2)使用socket創建的是主動套接字,但作為服務器,
需要被動等待別人的連接
頭文件:#include<sys/socket.h>
示例:
int sockfd = 0;
sockfd = socket(AF_INET,SOCK_STREAM,0);
if( sockfd< 0){"failed !";exit(-1);}
4.服務器
4.1 綁定
int bind(int sockfd,const struct socketaddr *myaddr,socklen_t addlen);
功能:將本地協議地址與sockfd綁定
參數:sockfd ... myaddr:指向特定於協議的地址結構指針 addrlen:該地址結構的長度
頭文件:<sys/socket.h>
備注: 二者的占用的內存大小是一致的,因此可以互相轉化,從這個意義上說,他們並無區別。
sockaddr常用於bind、connect、recvfrom、sendto等函數的參數,指明地址信息。是一種通用的套接字地址。而sockaddr_in 是internet環境下套接字的地址形式。

//usr/include/bits/socket.h
struct sockaddr{
sa_family_t sa_family; //2字節 /* 協議族 */
char sa_data[14]; //14字節 地址+端口號*/
};
// sockaddr_in是在頭文件 /usr/include/netinet/in.h 中定義的
typedef uint32_t in_addr_t;
struct in_addr{
in_addr_t s_addr; //4字節
};
struct sockaddr_in{
sa_family_t sin_family; //2字節 /* 協議族 */
in_port_t sin_port; //2字節 /* Port number. 端口號 */
structin_addr sin_addr; //4字節/* Internet address. IP地址 */
unsigned char sin_zero[8]; //8字節 /* Pad to size of `struct sockaddr'. 用於填充的0字節 */
};
示例:
int sockfd;
struct sockadd_in mysock;
sockfd = socket(AF_INET,SOCKET_STREAM,0);

bzero(&mysock,sizeof(mysock));
mysock.sin_family = AF_INET;
mysock.sin_port = htons(800);
mysock.sin_addr.saddr = inet_addr("192.168.2.45") ;
bind(sockfd,(struct sockaddr *)&mysock,sizeof(struct sockaddr));

//htons()作用是將端口號由主機字節序轉換為網絡字節序的整數值。(host to net)
//inet_addr()作用是將一個IP字符串轉化為一個網絡字節序的整數值,用於sockaddr_in.sin_addr.s_addr。
//inet_ntoa()作用是將一個sin_addr結構體輸出成IP字符串(network to ascii)
4.2 監聽
int listen(int sockfd, int backlog);
功能:將套接字由主動修改為被動 使操作系統為該套接字設置一個連接隊列,用來記錄所有連接到該套接字的連接
參數: sockfd:socket監聽套接字 sockfd:socket監聽套接字
返回值: 0 成功
頭文件: <sys/socket.h>
示例:
int err_log;
err_log = listen(sockfd,10);
4.3 從連接隊列中取出一個已經建立的連接
int accept(int sockfd,struct sockaddr*cliaddr, socklen_t*addrlen);
功能:從已連接隊列中取出一個已經建立的連接,如果沒有任何連接可用,則進入睡眠等待
參數:sockfd:socket監聽套接字
cliaddr:存放客服端套接字的地址結構,傳出參數
addrlen:套接字地址結構長度
返回值:已連接的套接字(一個新的套接字,原先的套接字仍在監聽)
頭文件: <sys/socket.h>
示例:
struct sockaddr_in c_addr;
struct sockadd_in mysock;
int sockfd;
char buf[BUFLEN];
int newfd;
/*建立socket*/
sockfd = socket(AF_INET, SOCK_STREAM, 0);

bzero(&mysock,sizeof(mysock));
mysock.sin_family = AF_INET;
mysock.sin_port = htons(800);
mysock.sin_addr.saddr = inet_addr("192.168.2.45") ;
/*把地址和本地端口幫定到套接字上*/
bind(sockfd,(struct sockaddr *)&mysock,sizeof(struct sockaddr));
/*偵聽本地端口*/
listen(sockfd,11) ;
while(1){
newfd = accept(sockfd,(struct sockaddr*) &c_addr, &sizeof(c_addr));//生成一個新的套接字
printf("當前處理的是%s: %d\n",inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port));

備注:accept()函數 准備好了,系統調用accept()會有點古怪的地方的!你可以想象發生 這樣的事情:有人從很遠的地方通過一個你在偵聽 (listen()) 的端口連接 (connect()) 到你的機器。它的連接將加入到等待接受 (accept()) 的隊列 中。你調用 accept() 告訴它你有空閑的連接。它將返回一個新的套接字文 件描述符!這樣你就有兩個套接字了,原來的一個還在偵聽你的那個端口, 新的在准備發送 (send()) 和接收 ( recv()) 數據。這就是這個過程!
5.客服端(需要知道服務器的ip及端口號)
5.1 int connect(int sockfd,const struct sockaddr *addr,socklen_t len);
功能:主動跟服務器建立鏈接,建立鏈接之后傳送數據(tcp協議)
部分參數說明:addr 需要鏈接的服務器地址結構
返回值:0 成功
頭文件:#include <sys/socket.h>
示例:int sockfd;
int resCon;
//創建套結字
sockfd = socket(AF_INET,SOCKET_STREAM,0);
if(sockfd <0){ ... }
struct sockadd_in mysock;
memset(&mysock,0x00,sizeof(mysock));
mysock.sin_family = AF_INET;
mysock.sin_port = htons(8999);

mysock.sin_addr.saddr = inet_addr("192.168.2.45");
//這里也可以用inet_pton來轉換 inet_pton(AF_INET,"192.168.2.45",&mysock.sin_addr.saddr);
//inet_pton:inet_pton 是inet_addr的擴展,將“點分十進制” -> “二進制整數”,int inet_pton(int af, const char *src, void *dst);反轉inet_ntop

//主動連接服務器
resCon = connect(sockfd,(struct sockadd *)&mysock,sizeof(mysock));
if(resCon){...}


6.數據傳送
6.1 通信
#define MAXLEN 512
while(1){
char send_buf[512];
char recv_buf[512];
memset(send_buf,0x00,sizeof(send_buf));
memset(recv_buf,0x00,sizeof(recv_buf));

//從流中讀取MAXLEN-1個數據存入send_buf中
fgets(send_buf,MAXLEN,stdin);

write(sockfd,send_buf,strlen(send_buf));
read(sockfd,recv_buf,MAXLEN);
/*
recv和send
  recv和send函數提供了和read和write差不多的功能.但是他們提供了第四個參數來控制讀寫操作。
int recv(int sockfd,void *buf,int len,int flags)
int send(int sockfd,void *buf,int len,int flags)
前面的三個參數和read,write相同,第四個參數能夠是0或是以下的組合

*/




printf("recv_buf = [%s]",recv_buf);

}

close(sockfd);
6.2 發送數據與接受數據
ssize_t send(int sockfd, const void* buf,size_t nbytes, int flags);
功能:用於發送數據,不可以用tcp發送0長度的數據
部分參數說明;buf 待發送的數據緩存,nbytes發送的長度,flags套接字的標志,通常為0
返回值:成功發送的字節數

ssize_t recv(int sockfd, void *buf,size_t nbytes, int flags);
功能:接受網絡數據
部分參數說明:buf 指向接受網絡數據的緩沖區,nbytes 緩沖區的大小
7.關閉連接
關閉一個代表已連接套接字將導致另一端接收到一個0長度的數據包。
做服務器時:(1)關閉socket創建的監聽套接字將導致服務器無法繼續接受新的連接,但不會影響已經建立的連接
(2)關閉accept返回的已連接套接字將導致它所代表的連接被關閉,但不會影響服務器的監聽





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM