一、服務器
1、首先知道linux中網絡地址的表示,有兩個重要的數據類型: sockaddr和 sockaddr_in
struct sockaddr{
unsigned short sa_fmaily; //address族,AF_xxx
char sa_data[14];//14 bytes的協議地址
}
sa_fmaily中 AF_INET代表IPV4協議;sa_data中包含了一些遠程電腦的地址,端口和套接字的數目
struct sockaddr_in{
short int sin_fmaily;//Internet地址族
unsigned short int sin_port; //端口號
struct int_addr sin_addr; //Internet地址
unsigned char sin_zero[8]; //添0,(和struct sockaddr一樣大小)
}
使用sockaddr_in的時候要把sin_zero全部置爲0
2、建立socket鏈接
包含頭文件<sys/types.h> <sys/socket.h>
int socket(int family, int type , int protocol); family代表協議或地址族;type代表套接字的類型,對於TCP爲 SOCK_STREAM,對於UDP爲SOCK_DGRAM,原始套接字爲SOCK_RAW;
protocol表示使用的協議號。用0制定默認的family和type的默認協議號
返回值:建立成功返回非負值,否則-1; 錯誤原因EPROTONOSUPPORT:表示申請的服務或制定的協議無效;EMFILE:應用程序的描述符表已經滿了;NFILE:內部系統文件已滿;ENOBUFS:系統沒有可用的緩衝空間
3、綁定本地地址
一個很重要的函數bind(),可以制定一個套接字使用的地址和端口,socket建立的一個socketfd綁定上一個本地的地址和端口;當需要進行端口監聽(listen)操作需要經過這一步;當已經有一個鏈接的時候就不要這一步;
頭文件<sys/types.h> <sys/socket.h>
int bind(int sockfd, struct sockaddr *sa, int addrlen);
參數:sockfd,是由socket()函數返回的套接字描述符; sa,是一個指向struct sockaddr的指針,包含有關地址的有關信息,例如名稱,端口,IP地址;
addrlen,是套接字地址接口的長度,可以設置爲sizeof(struct sockaddr);
成功返回 0 ,否則-1,常見錯誤是EADDRINUSER,表示端口已經被佔用
例如:struct sockaddr_in server_addr;
server_addr.sin_addr.s_addr=htonl(INADDRY_ANY);存儲是需要綁定的IP地址,也可以制定一個特定的IP用下面的語句是:server_addr.sin_addr.s_addr=inet_addr("192.268.1.1");
server_addr.sin_port=htons(portnumber);存儲要綁定的端口號。
4、字節順序轉換
htonl()和htons()函數作用是進行網絡字節順序的轉換;“HOST to Network Short”主機字節順序轉換爲網絡字節順序----HTONLS ; "HTONL"-----“Host to Network Short”是無符號整型操作
5、IP地址轉換
其中一個陌生的函數:inet_addr(); 包含的頭文件是<arpa/inet.h>;
server_addr.sin_addr.s_addr=inet_addr("192.168.1.1");
如果想把struct in_addr裏存儲的網絡地址顯示出來,可以使用函數inet_ntoa();
char *inet_ntoa(struct in_addr inaddr);這個函數可以將一個32爲的網絡字節順序的struct in_addr結構體轉換爲相應的點分十進制字符串;
6、Listen函數
當調用bind()函數,將一個套接字綁定到某個端口之後,就可以通過Listen()函數來接受客戶端提出的連接請求。
頭文件<sys/socket.h>
int listen(int sockfd, int backlog); backlog是未經過處理的連接請求隊列可以容納的最大數目
7、等待連接
<sys/socket.h>
int accept(int sockfd, void *addr, int *addrlen);
addr:一般是一個指向struct sockaddr_in 結構的指針,將在accept()函數調用返回後填入遠程連接過來的計算機的信息,比如遠程計算機的IP地址和端口
例如:new_fd=accept(sockfd, (struct sockaddr*)&client_addr,&sin_size);
8、數據通信
socket本質就是文件描述符,因此凡是基於文件描述符的I/O函數都可以用於數據通信。如read,write,put,get等
但是read,write函數並不是針對套接字而設計,雖然也能傳送數據但是缺少對網絡的控制選項。
<1>其中send(),recv()函數是最基本的
int send(int sockfd, const void*msg, int len, int flags);
int recv( int sockfd, void*buf, int len, unsigned int flags);
msg是一個指針,指向想發送或存儲信息的地址
len是發送信息的長度, flags是發送或者接受的標記,一般都設置爲0.
<2>是sendto()和recvfrom()函數
用於進行無連接的UDP通信時使用的。
int sendto(int sockfd,const void*msg, int len, unsigned int flags, const struct sockaddr*to , int tolen);
int recvfrom(int sockfd,void*buf,int len, unsigned int flags, struct sockaddr *from, int *fromlen);
9、關閉套接字close(new_fd);
二、客戶端
1、DNS(Domain Name Service)域名服務,實際上IP地址都是很難記憶的,通常是藉助DNS服務,有了它可以通過一個可讀性非常強的因特網名字得到這個名字所代表的IP地址,轉換爲IP地址後可以使用標準的套接字函數。
struct hostent *gethostbyname(const char*name);
它返回了一個指向struct hostent的指針,
struct hostent{
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
}
h_name是這個主機的正式名稱;h_aliases是一個以NULL結尾的數組,存儲了主機的備用名稱;h_addrtype是返回地址的類型一般來說是AF_INET; h_length是地址的字節長度; h_addr_list是一個以0結尾的數組,存儲了主機的網絡地址;h_addr是h_addr_list數組的第一個成員。
host=gethostbyname(argv[1]);
server_addr.sin_addr=*((struct in_addr*)host->h_addr);
其中h_addr是char**類型的,所以在賦值的時候還得做一個指針的類型轉換;
2、連接服務器
<sys/types.h> <sys/socket.h>
int connect(int sockfd, struct sockaddr* serv_addr, int addrlen);
參數serv_addr是一個存儲遠程計算機的IP地址和端口信息的sockaddr結構;
參數addrlen是serv_addr結構體所佔的內存大小;
connect()函數調用成功返回0,錯誤返回-1;
例如客戶端代碼:這是一個函數,傳遞過來一個IP,可以用#define IP 192.168.1.111; buffer爲一個帶有數據的緩衝; BMPsize爲這個buffer的有效數據長度。
#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 MAXSIZE 1400*1200
int sendData(char *ip,void *buffer,long BMPsize){
int sockfd ,n;
//void *buffer=malloc(MAXSIZE);
printf("the ip is %s\n",ip);
printf("the BMPsize is %ld",BMPsize);
struct sockaddr_in servaddr;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0){
printf("create socket error!!\n");
return -1;
}
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(6666);
if(inet_pton(AF_INET,ip,&servaddr.sin_addr)<=0){
printf("inet_pton error for %s!!\n",ip);
return -1;
}
if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0){
printf("connect error !!!!\n");
return -1;
}
printf("start to send data to servr:\n");
if(send(sockfd,buffer,BMPsize,0)<0){
printf("send data error!\n");
return -1;
}
close(sockfd);
return 0;
}
另一端我們可以用java或者C的socket編程來接收數據,我是用的一段Java代碼如下:
package cn.edu;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class pic {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
FileOutputStream fos=null;
DataInputStream dis=null;
byte[] buffer =new byte[1024*1024];
ServerSocket serverSocket=new ServerSocket(6666);
Socket socket=serverSocket.accept();
System.out.println("connect success!\n");
dis=new DataInputStream(socket.getInputStream());
fos=new FileOutputStream(new File("D:/rec.jpg"));
int length=0;
while((length=dis.read(buffer,0,buffer.length))>0){
fos.write(buffer, 0, length);
fos.flush();
}
//int length=dis.read(buffer,0,buffer.length);
//fos.write(buffer, 0,length );
serverSocket.close();
dis.close();
fos.close();
}
}
這邊我傳遞是圖片數據,當然傳遞任何的文件都可以!!!