Linux下Socket編程

一、基本socket函數
Linux系統是通過提供套接字(socket)來進行網絡編程的。網絡的socket數據傳輸是一種特殊的I/O,socket也是一種文件描述符。socket也有一個類似於打
開文件的函數:socket(),調用socket(),該函數返回一個整型的socket的描述符,隨後的連接建立、數據傳輸等操作也都是通過該socket實現。
1、socket函數
syntax:
   int socket(int domain, int type, int protocol);
功能說明:
   調用成功,返回socket文件描述符;失敗,返回-1,並設置errno
參數說明:
  domain指明所使用的協議族,通常爲PF_INET,表示TCP/IP協議;
  type參數指定socket的類型,基本上有三種:數據流套接字、數據報套接字、原始套接字
  protocol通常賦值"0"。
  兩個網絡程序之間的一個網絡連接包括五種信息:通信協議、本地協議地址、本地主機端口、遠端主機地址和遠端協議端口。socket數據結構中包含這五種信息。
2、bind函數
syntax:  
   int bind(int sock_fd,struct sockaddr_in *my_addr, int addrlen);
功能說明:
   將套接字和指定的端口相連。成功返回0,否則,返回-1,並置errno.
參數說明:
    sock_fd是調用socket函數返回值,
  my_addr是一個指向包含有本機IP地址及端口號等信息的sockaddr類型的指針;
  struct sockaddr_in結構類型是用來保存socket信息的:
  struct sockaddr_in {
  short int sin_family;
  unsigned short int sin_port;
  struct in_addr sin_addr;
  unsigned char sin_zero[8];
  };
    addrlen爲sockaddr的長度。
3、connect函數
syntax:  
    int connect(int sock_fd, struct sockaddr *serv_addr,int addrlen);
功能說明:
   客戶端發送服務請求。成功返回0,否則返回-1,並置errno。
參數說明:
   sock_fd 是socket函數返回的socket描述符;serv_addr是包含遠端主機IP地址和端口號的指針;addrlen是結構sockaddr_in的長度。
4、listen函數
syntax:
   int listen(int sock_fd, int backlog);
功能說明:
   等待指定的端口的出現客戶端連接。調用成功返回0,否則,返回-1,並置errno.
參數說明:
   sock_fd 是socket()函數返回值;
   backlog指定在請求隊列中允許的最大請求數
5、accecpt函數
syntax:  
   int accept(int sock_fd, struct sockadd_in* addr, int addrlen);
功能說明:
   用於接受客戶端的服務請求,成功返回新的套接字描述符,失敗返回-1,並置errno。
參數說明:
   sock_fd是被監聽的socket描述符,
   addr通常是一個指向sockaddr_in變量的指針,
   addrlen是結構sockaddr_in的長度。
6、write函數
syntax:
    ssize_t write(int fd,const void *buf,size_t nbytes)
功能說明:
    write函數將buf中的nbytes字節內容寫入文件描述符fd.成功時返回寫的字節數.失敗時返回-1. 並設置errno變量.
    在網絡程序中,當我們向套接字文件描述符寫時有倆種可能:
      1)write的返回值大於0,表示寫了部分或者是全部的數據.
      2)返回的值小於0,此時出現了錯誤.需要根據錯誤類型來處理.
        如果錯誤爲EINTR表示在寫的時候出現了中斷錯誤.
        如果錯誤爲EPIPE表示網絡連接出現了問題.
7、read函數
syntax:
    ssize_t read(int fd,void *buf,size_t nbyte)
函數說明:
    read函數是負責從fd中讀取內容.當讀成功時,read返回實際所讀的字節數,如果返回的值是0 表示已經讀到文件的結束了,小於0表示出現了錯誤.
    如果錯誤爲EINTR說明讀是由中斷引起的,
    如果錯誤是ECONNREST表示網絡連接出了問題.
8、close函數
syntax:
int close(sock_fd);
說明:
當所有的數據操作結束以後,你可以調用close()函數來釋放該socket,從而停止在該socket上的任何數據操作:
函數運行成功返回0,否則返回-1

二、socket編程的其他函數說明
1、 網絡字節順序及其轉換函數
1) 網絡字節順序
每一臺機器內部對變量的字節存儲順序不同,而網絡傳輸的數據是一定要統一順序的。所以對內部字節表示順序與網絡字節順序不同的機器,
一定要對數據進行轉換,從程序的可移植性要求來講,就算本機的內部字節表示順序與網絡字節順序相同也應該在傳輸數據以前先調用數據轉換函數,
以便程序移植到其它機器上後能正確執行。真正轉換還是不轉換是由系統函數自己來決定的。
2) 有關的轉換函數
* unsigned short int htons(unsigned short int hostshort):
主機字節順序轉換成網絡字節順序,對無符號短型進行操作4bytes
* unsigned long int htonl(unsigned long int hostlong):
主機字節順序轉換成網絡字節順序,對無符號長型進行操作8bytes
* unsigned short int ntohs(unsigned short int netshort):
網絡字節順序轉換成主機字節順序,對無符號短型進行操作4bytes
* unsigned long int ntohl(unsigned long int netlong):
網絡字節順序轉換成主機字節順序,對無符號長型進行操作8bytes
注:以上函數原型定義在netinet/in.h裏
2、IP地址轉換
有三個函數將數字點形式表示的字符串IP地址與32位網絡字節順序的二進制形式的IP地址進行轉換
(1) unsigned long int inet_addr(const char * cp):該函數把一個用數字和點表示的IP地址的字符串轉換成一個無符號長整型,如:struct sockaddr_in ina
ina.sin_addr.s_addr=inet_addr("202.206.17.101")
該函數成功時:返回轉換結果;失敗時返回常量INADDR_NONE,該常量=-1,二進制的無符號整數-1相當於255.255.255.255,這是一個廣播地址,所以在程序中調用iner_addr()時,一定要人爲地對調用失敗進行處理。由於該函數不能處理廣播地址,所以在程序中應該使用函數inet_aton()。
(2)int inet_aton(const char * cp,struct in_addr * inp):此函數將字符串形式的IP地址轉換成二進制形式的IP地址;成功時返回1,否則返回0,轉換後的IP地址存儲在參數inp中。
(3) char * inet_ntoa(struct in-addr in):將32位二進制形式的IP地址轉換爲數字點形式的IP地址,結果在函數返回值中返回,返回的是一個指向字符串的指針。
3、字節處理函數
Socket地址是多字節數據,不是以空字符結尾的,這和C語言中的字符串是不同的。Linux提供了兩組函數來處理多字節數據,一組以b(byte)開頭,是和BSD系統兼容的函數,另一組以mem(內存)開頭,是ANSI C提供的函數。
以b開頭的函數有:
(1) void bzero(void * s,int n):將參數s指定的內存的前n個字節設置爲0,通常它用來將套接字地址清0。
(2) void bcopy(const void * src,void * dest,int n):從參數src指定的內存區域拷貝指定數目的字節內容到參數dest指定的內存區域。
(3) int bcmp(const void * s1,const void * s2,int n):比較參數s1指定的內存區域和參數s2指定的內存區域的前n個字節內容,如果相同則返回0,否則返回非0。
注:以上函數的原型定義在strings.h中。
以mem開頭的函數有:
(1) void * memset(void * s,int c,size_t n):將參數s指定的內存區域的前n個字節設置爲參數c的內容。
(2) void * memcpy(void * dest,const void * src,size_t n):功能同bcopy(),區別:函數bcopy()能處理參數src和參數dest所指定的區域有重疊的情況,memcpy()則不能。
(4) int memcmp(const void * s1,const void * s2,size_t n):比較參數s1和參數s2指定區域的前n個字節內容,如果相同則返回0,否則返回非0。
注:以上函數的原型定義在string.h中。

二、程序說明
本使用tcp協議進行通信,服務端進行監聽,在收到客戶端的連接後,發送數據給客戶端;客戶端在接受到數據後打印出來,然後關閉。
1、client.c

#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main()
{
int cfd;
int recbytes;
int sin_size;
char buffer[1024]={0};   
struct sockaddr_in s_add,c_add;
unsigned short portnum=0x8888; 

printf("Hello,welcome to client !\r\n");

cfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == cfd)
{
    printf("socket fail ! \r\n");
    return -1;
}
printf("socket ok !\r\n");

bzero(&s_add,sizeof(struct sockaddr_in));
s_add.sin_family=AF_INET;
s_add.sin_addr.s_addr= inet_addr("192.168.1.2");
s_add.sin_port=htons(portnum);
printf("s_addr = %#x ,port : %#x\r\n",s_add.sin_addr.s_addr,s_add.sin_port);


if(-1 == connect(cfd,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
{
    printf("connect fail !\r\n");
    return -1;
}
printf("connect ok !\r\n");

if(-1 == (recbytes = read(cfd,buffer,1024)))
{
    printf("read data fail !\r\n");
    return -1;
}
printf("read ok\r\nREC:\r\n");

buffer[recbytes]='\0';
printf("%s\r\n",buffer);

getchar();
close(cfd);
return 0;
}
2、server.c

#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main()
{
int sfp,nfp;
struct sockaddr_in s_add,c_add;
int sin_size;
unsigned short portnum=0x8888;

printf("Hello,welcome to my server !\r\n");
sfp = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sfp)
{
    printf("socket fail ! \r\n");
    return -1;
}
printf("socket ok !\r\n");


bzero(&s_add,sizeof(struct sockaddr_in));
s_add.sin_family=AF_INET;
s_add.sin_addr.s_addr=htonl(INADDR_ANY);
s_add.sin_port=htons(portnum);

if(-1 == bind(sfp,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
{
    printf("bind fail !\r\n");
    return -1;
}
printf("bind ok !\r\n");

if(-1 == listen(sfp,5))
{
    printf("listen fail !\r\n");
    return -1;
}
printf("listen ok\r\n");

while(1)
{
sin_size = sizeof(struct sockaddr_in);

nfp = accept(sfp, (struct sockaddr *)(&c_add), &sin_size);
if(-1 == nfp)
{
    printf("accept fail !\r\n");
    return -1;
}
printf("accept ok!\r\nServer start get connect from %#x : %#x\r\n",ntohl(c_add.sin_addr.s_addr),ntohs(c_add.sin_port));


if(-1 == write(nfp,"hello,welcome to my server \r\n",32))
{
    printf("write fail!\r\n");
    return -1;
}
printf("write ok!\r\n");
close(nfp);

}
close(sfp);
return 0;
}

在cygwin下,使用gcc命令編譯如下:
gcc -o server server.c
gcc -o client client.c
然後運行程序:
./server
./client

server執行效果如下:
Hello,welcome to my server !
socket ok !
bind ok !
listen ok
accept ok!
Server start get connect from 0xc0a80102 : 0xc927
write ok!
client執行效果如下:
Hello,welcome to client !
socket ok !
s_addr = 0x201a8c0 ,port : 0x8888
connect ok !
read ok
REC:
hello,welcome to my server
本文歡迎轉載,轉載請註明出處和作者

出處:http://blog.sina.com.cn/staratsky

作者:流星

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章