socket编程入门

来源:互联网 发布:python计算sin 编辑:程序博客网 时间:2024/05/21 09:13

Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求。一个Socket由:IP地址+端口号 所组成;

网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

现在编写两个程序(运行在同一台主机上),一个作为服务器端,一个作为客户端;思路如下:


以下是Windows下的Socket编程,需要的头文件有:

#include<stdio.h>#include<memory.h>#include<WinSock2.h>  //Socket所在的头文件
但是仅仅包含头文件还不可以,需要把链接文件包含进来:
#pragma comment (lib, "ws2_32.lib") 
否则程序无法运行!


服务器端:
0.定义两个socket标识:int listenFd,connectFd;一个用于监听是否有客户进行连接,一个用于和客户端进行连接


1.添加以下一个变量以及一个函数:

  WSADATA wsaData;  WSAStartup(0x0101, &wsaData);
WSAStartup函数是为了通知系统调用Socket的Dll(动态链接库文件),故WSAStartup应先于其他的winsock函数

如果不添加该函数,则容易导致程序无法运行;


2.初始化监听套接字:

 int listenFd=socket(AF_INET,SOCK_STREAM,0);
套接字实际是一个int整型,计算机中很多资源、程序都会用一个int进行表示,例如句柄、进程PID、Linux下的文件表示等(有不懂可以自行Google查阅)

#func解说:int sock=socket( protofamily, type, protocol );
  protofamily指定所使用的协议族,对TCP/IP协议族取值为AF_INET。type指定采用的  通信类型:流传输模式取值SOCK-STREAM;报文传输模式取值SOCK-DGRAM。protocol指  定所使用的传输协议,可默认为0; 

3.定义服务器的地址用于通信:struct sockaddr_in serverAddr;
  sockaddr_in是一个结构体:

struct sockaddr_in {u_char sin_len;           /* total length of the address */u_char sin_family;        /* family of the address */char sin_port;            /* protocol port number */struct in_addr sin_addr;  /* IPv4 address of computer *, 通常为INADDR_ANY/char sin_zero[8];         /* not used (set to zero) */};
而其中的in_addr也是一个结构体:如下

struct in_addr{    union{  struct{ u_char s_b1, s_b2, s_b3, s_b4;} s_un_b;            struct{u_short s_w1,s_w2;}  s_un_w;            u_long s_addr;          } s_un
如果看不懂可以暂时忽略,只要记得结构体sockaddr_in用来储存一个服务器或客户端的地址

4.服务器地址serverAddr的初始化:
  a.memset初始化为0:memset(&serverAddr,0,sizeof(serverAddr));
  b.指定serverAddr的协议族:sin_family赋值为AF_INET(表示TCP/IP);
  c.指定serverAddr的源地址:sin_addr.s_addr=htonl(INADDR_ANY);IP地址设置为系统自动获取本机IP地址
  d.指定serverAddr的端口:sin_port=htons(PORT);PORT=自定义端口

5.将服务器地址绑定到监听套接字上:
 bind(listenFd,(struct sockaddr *)&serverAddr,sizeof(serverAddr);
只有进行绑定服务器才会拥有一个地址,客户端才可以连接到服务器

  #func解说:bind ( socket, addr, addrlen )
  socket是所用套接字的描述符;addr是一个结构,它指定将要赋给套接字的地址;addrlen指出地址的长度。

6.开始监听是否有客户端进行连接:
 listen(listenFd,10);

  #func解说:listen ( socket, queuesize )
  socket是所用套接字的描述符,queuesize指定请求队列的长度。

7.在一个死循环中,如果有客户端连接,则连接套接字进行连接
connectFd=accept(listenFd,(struct sockaddr *)NULL,NULL);

  #func解说: accept ( socket, caddress, caddresslen )
  socket是所用套接字的描述符;caddress是sockaddr_in结构类型的地址,填入已建立连接的客户地址,caddresslen是指向一个整数的指针,指定地址长度。accept为该连接创建一个新的套接字,并将这个新套接字的描述符返回给调用者。

8.接受客户端传过来的数据:
 int n=recv(connectFd,buff,MAXLINE,0);

  #func解说:recv ( socket, buffer, length, flags )
  socket是所用套接字的描述符, buffer指定用来存放接收数据的内存地址, 1ength指定这个缓冲区的大小,flags允许调用者控制一些细节。

9.向客户端发送回应数据:
send(conncetFd,MESSAGE,Len,0);

  #func解说:send ( socket, data, length, flags )
  socket是所用套接字的描述符,data是待发送数据在内存中的地址,1ength是一个整数,指定数据的字节数,参数flags包含请求特殊选项的比特位。

10.关闭连接
closesocket(connectFd);closesocket(listenFd);

  #func解说:close (socket)
  socket是所用套接字的描述符,需要先关闭连接套接字,再关闭监听套接字

完整的代码如下:其中的检测错误下面会进行说明:

#include<stdio.h>#include<memory.h>#include<WinSock2.h>#pragma comment (lib, "ws2_32.lib") #define MPORT 8000#define MAXLEN 2048void main(){char buff[MAXLEN];int listenFd, conncetFd;sockaddr_in serverAddr;WSADATA wsaData;WSAStartup(0x0101, &wsaData);//初始化SocketlistenFd = socket(AF_INET, SOCK_STREAM, 0);if (listenFd < 0){printf("Error at socket():%ld\n", WSAGetLastError());exit(0);}//初始化memset(&serverAddr, 0, sizeof(serverAddr));serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(MPORT);//设置端口 serverAddr.sin_addr.s_addr =INADDR_ANY;//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址//将本地地址绑定到所创建的套接字上if (bind(listenFd, (struct sockaddr *)&serverAddr, sizeof(sockaddr)) < 0){printf("Error at bind():%ld\n", WSAGetLastError());exit(0);}//开始监听是否有客户端连接if (listen(listenFd, 10) < 0){printf("Error at listen():%ld\n", WSAGetLastError());exit(0);}printf("=======wait for client=========\n");while (1){//阻塞直到有客户端连接,不然多浪费CPU资源if ((conncetFd = accept(listenFd, (struct sockaddr *)NULL, NULL)) < 0){printf("Error at accept():%ld\n", WSAGetLastError());continue;}//接受客户端传过来的数据int n = recv(conncetFd, buff, MAXLEN, 0);if(n>=0) buff[n] = '\0';printf("%s\n", buff);//向客户端发送回应数据send(conncetFd, "Hello,This message is frome Server\n", 8, 0);closesocket(conncetFd);}closesocket(listenFd);exit(0);}

关于以上的异常判断,每执行一个函数,socket()、bind()、listen()等都会返回一个int整型,如果函数运行出错,则返回-1,成功则会返回一个不同的整数(具体返回的整数可以看上面的#func函数解说);

所以,当函数返回-1的时候,调用该函数WSAGetLastError()可以打印出错信息(一个整型),你可以通过查找该整型得到解决方法


客户端:与服务器端比较类似,如下:

0.定义一个socket标记:int connectFd;


1.添加以下一个变量以及一个函数:WSAStartup应先于其他的winsock函数
  

WSADATA wsaData;WSAStartup(0x0101, &wsaData)

2.初始化监听套接字:
connectFd=socket(AF_INET,SOCK_STREAM,0);
  
  #func解说:与服务器端相同

3.定义服务器的地址用于通信:struct sockaddr_in serverAddr;

4.服务器地址serverAddr的初始化:
  a.memset初始化为0:memset(&serverAddr,0,sizeof(serverAddr));
  b.指定serverAddr的协议族:sin_family赋值为AF_INET(TCP/IP);
  c.指定serverAddr的源地址:sin_addr.s_addr=inet_addr("127.0.0.1");IP地址设置为本机IP地址
  d.指定serverAddr的端口:sin_port=htons(PORT);PORT=自定义端口


5.连接服务器:
connect(connectFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr));

  #func解说:与服务器端相同

6.如果连接服务器成功,则可以发送数据:
sendLen = send(connect_sockFd, mess, strlen(mess), 0)
  
  #func解说:返回发送的数据长度(sendLen),与服务器端相同


7.接收数据:
 recLen = recv(connectFd, recMessage, MAXLEN, 0)

  #func解说:与服务器端相同

8.关闭连接
closesocket(connectFd);

完整代码如下:

#include<stdio.h>#include<WinSock2.h>#include<memory.h>#pragma comment (lib, "ws2_32.lib") #define MPORT 8000#define MAXLEN 2048void main(){int connectFd;char rec[MAXLEN];int len=0;struct sockaddr_in serverAddr;WSADATA wsaData;WSAStartup(0x0101, &wsaData);if ((connectFd = socket(AF_INET, SOCK_STREAM, 0)) < 0){printf("Error at socket():%ld\n", WSAGetLastError());exit(0);}memset(&serverAddr, 0, sizeof(serverAddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");serverAddr.sin_port = htons(MPORT);if ((connect(connectFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) )< 0){printf("Error at connect():%ld\n", WSAGetLastError());exit(0);}printf("conncect succeed\n");int temp;if ((temp = send(connectFd, "Hello world", strlen("Hello world"), 0)) < 0){printf("send error\n");}if ((len = recv(connectFd, rec, MAXLEN, 0) )< 0){printf("Error at recv():%ld\n", WSAGetLastError());exit(0);}rec[len] = '\0';printf("%s\n", rec);closesocket(connectFd);}


以上就是Socket编程的基本思路,如果想了解每个函数具体的作用,可以参考:http://download.csdn.net/detail/u012336923/8135589





0 0