socket

 

 

 

 

 

函数

应用程序调用socket函数来创建一个能够进行网络通信的套接字

头文件

#include <sys/types.h>

#include <sys/socket.h>

函数原型 int socket(int domain, int type, int protocol);

第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置AF_INET;

第二个参数指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM、原始套接字SOCK_RAW(WinSock接口并不适用某种特定的协议去封装它,而是由程序自行处理数据包以及协议首部);

第三个参数指定应用程序所使用的通信协议。此参数可以指定单个协议系列中的不同传输协议。在Internet通讯域中,此参数一般取值为0,系统会根据套接字的类型决定应使用的传输层协议。

该函数如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET。套接字描述符是一个整数类型的值。每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系。该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找到其对应的套接字数据结构。每个进程在自己的进程空间里都有一个套接字描述符表但是套接字数据结构都是在操作系统的内核缓冲里。

创建流套接字的例子:

struct protoent *ppe;

ppe=getprotobyname("tcp");

SOCKET ListenSocket=socket(PF_INET,SOCK_STREAM,ppe->p_proto);

 

 

 

 

C#语言中的SOCKET

所谓Socket通常也称作“套接字”,应用程序通常通过“套接字”向网络发出请求或者应答网络请求。根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。

服务器监听:是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。

客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。

连接确认:是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

Socket通信以其传输速度快且稳定的优点在程序开发中应用非常的广泛,下面的范例就简要的介绍了Socket在C#中应用。

public class XmlSocket

{

//异步socket诊听

// Incoming data from client.从客户端传来的数据

public static string data = null;

// Thread signal.线程 用一个指示是否将初始状态设置为终止的布尔值初始化 ManualResetEvent 类的新实例。

public static ManualResetEvent allDone = new ManualResetEvent(false);

//static void Main(string[] args)

//{

// StartListening();

//}

public static void StartListening()

{

// Data buffer for incoming data. 传入数据缓冲

byte[] bytes = new Byte[1024];

// Establish the local endpoint for the socket. 建立本地端口

// The DNS name of the computer

// running the listener is “*******”.

IPAddress ipAddress;

String ipString = ConfigurationManager.AppSettings.Get(“SocketIP”);

if (ipString==null || ipString ==String.Empty) {

  IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());

  ipAddress = ipHostInfo.AddressList[0];

}

else

{

  ipAddress = IPAddress.Parse(ipString);

}

int port;

String portString = ConfigurationManager.AppSettings.Get(“SocketPort”);

if (portString==null || portString==String.Empty)

{

  port = 11001;

}

else

{

  port = int.Parse(portString);

}

IPEndPoint localEndPoint = new IPEndPoint(ipAddress,port);

// Create a TCP/IP socket.

Socket listener = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);

// Bind the socket to the local endpoint and listen for incoming connections.绑定端口和数据

try

{

  listener.Bind(localEndPoint);

  listener.Listen(100);

  while (true)

  {

    // Set the event to nonsignaled state.设置无信号状态的事件

    allDone.Reset();

    // Start an asynchronous socket to listen for connections.重新 启动异步连接

    listener.BeginAccept(new AsyncCallback(AcceptCallback),listener);

    // Wait until a connection is made before continuing.等待连接创建后继续

    allDone.WaitOne();

  }

}

catch (Exception e)

{

//

}

}

public static void AcceptCallback(IAsyncResult ar)

{

try

{

// Signal the main thread to continue.接受回调方法 该方法的此节向主应用程序线程发出信号,

//让它继续处理并建立与客户端的连接

allDone.Set();

// Get the socket that handles the client request.获取客户端请求句柄

Socket listener = (Socket)ar.AsyncState;

Socket handler = listener.EndAccept(ar);

// Create the state object.

StateObject state = new StateObject();

state.workSocket = handler;

handler.BeginReceive(state.buffer,0,StateObject.BufferSize,0,

new AsyncCallback(ReadCallback),state);

}

catch (Exception e)

{

//

}

}

/// <summary>

/// 与接受回调方法一样,读取回调方法也是一个 AsyncCallback 委托。

/// 该方法将来自客户端套接字的一个或多个字节读入数据缓冲区,然后再次调用 BeginReceive 方法,直到客户端发送的数据完成为止。

/// 从客户端读取整个消息后,在控制台上显示字符串,并关闭处理与客户端的连接的服务器套接字。

/// </summary>

/// <param name=”ar”>IAsyncResult 委托</param>

public static void ReadCallback(IAsyncResult ar)

{

try

{

String content = String.Empty;

// Retrieve the state object and the handler socket创建自定义的状态对象 from the asynchronous state object.

StateObject state = (StateObject)ar.AsyncState;

Socket handler = state.workSocket;//处理的句柄

// Read data from the client socket. 读出

int bytesRead = handler.EndReceive(ar);

if (bytesRead > 0)

{

//业务代码

String result = DoSomeThing(…);

String len = Encoding.UTF8.GetBytes(result).Length.ToString().PadLeft(8,’0′);

log.writeLine(len);

Send(len + result,handler);

}

}

catch (Exception e)

{

//

}

}

private static void Send(String data,Socket handler)

{

try

{

// Convert the string data to byte data using UTF8 encoding.

byte[] byteData = Encoding.UTF8.GetBytes(data);

// Begin sending the data to the remote device.

handler.BeginSend(byteData,0,byteData.Length,0,

new AsyncCallback(SendCallback),handler);

}

catch (Exception e)

{

//

}

}

/// <summary>

/// 发送

/// </summary>

/// <param name=”ar”></param>

private static void SendCallback(IAsyncResult ar)

{

try

{

// Retrieve the socket from the state object.

Socket handler = (Socket)ar.AsyncState;

// Complete sending the data to the remote device.向远端发送数据

int bytesSent = handler.EndSend(ar);

StateObject state = new StateObject();

state.workSocket = handler;

handler.BeginReceive(state.buffer,0,StateObject.BufferSize,0,new AsyncCallback(ReadCallback),state);

handler.Shutdown(SocketShutdown.Both);

handler.Close();

}

catch (Exception e)

{

//

}

}

public static void StopListening()

{

allDone.Close();

log.close();

}

/// <summary>

/// 具体处理业务的方法

/// </summary>

/// <returns></returns>

private static string DoSomething(int i)

{

//具体业务代码,返回需要返回的字符串信息

}

/// <summary>

/// 写日志方法

/// </summary>

/// <param name=”strLog”>;写入内容</param>

public static void WriteLog(string strLog)

{

//写入日志代码

}

}

/// 线程执行体,转发消息

/// </summary>

/// <param name=”obj”>;传递给线程执行体的用户名,用以与用户通信 </param>

private void ThreadFunc(object obj)

{

//通过转发表得到当前用户套接字

Socket clientSkt = _transmit_tb[obj] as Socket;

//主循环

while (true)

{

try

{ //接受第一个数据包。

//由于程序逻辑结构简单,所以在这里对客户机发送的第一个包内容作逐一判断,

//这里的实现不够优雅,但不失为此简单模型的一个解决之道。

byte[] packetBuff = new byte[_maxPacket];

clientSkt.Receive(packetBuff);

string _str = Encoding.Unicode.GetString(packetBuff).TrimEnd(‘\0′);

//如果是发给不在线好友的信息

if (_str.StartsWith(“cmd::FriendMessage”))

{

string UserName = _str.Substring(“cmd::FriendMessage”.Length,20).Trim();

string MessageS = _str.Substring(“cmd::FriendMessage”.Length + 20,_str.Length - ”cmd::FriendMessage”.Length - 20);

SaveMessage(obj as string,UserName,MessageS);

continue;

}

//如果是离线请求

if (_str.StartsWith(“cmd::RequestLogout”))

{

_transmit_tb.Remove(obj);

UpdateFriendList((string)obj,false,”");

// string svrlog = string.Format(“[系统消息]用户 {0} 在 {1} 已断开… 当前在线人数: {2}\r\n\r\n”,obj,DateTime.Now,_transmit_tb.Count);

// Console.WriteLine(svrlog);

//向所有客户机发送系统消息

//foreach (DictionaryEntry de in _transmit_tb)

//{

// string _clientName = de.Key as string;

// Socket _clientSkt = de.Value as Socket;

// _clientSkt.Send(Encoding.Unicode.GetBytes(svrlog));

//}

Thread.CurrentThread.Abort();

}

//如果是请求好友列表

if (_str.StartsWith(“cmd::RequestFriendList”))

{

SerializeFriendList(obj,clientSkt);

// 将该用户不在线时的信息发送给用户

DataTable TabMessage = ReadMessage(obj as string);

if (TabMessage != null)

{

foreach (DataRow myrow in TabMessage.Rows)

{

if (myrow["SendUserName"].ToString() == ”System::Message”)

{

clientSkt.Send(Encoding.Unicode.GetBytes(myrow["Message"].ToString()));

}

else

{

clientSkt.Send(Encoding.Unicode.GetBytes(“cmd::FriendMessage” +myrow["SendUserName"].ToString().PadRight(20,’ ’) + myrow["Message"].ToString()));

}

}

} //这里不需要再继续接受后继数据包了,跳出当前循环体

continue;

}

////如果是请求好友列表

//if (_str.StartsWith(“cmd::RequestOnLineList”))

//{

// byte[] onlineBuff = SerializeOnlineList();

// //先发送响应信号,用户客户机的判断

// clientSkt.Send(Encoding.Unicode.GetBytes(“cmd::RequestOnLineList”));

// clientSkt.Send(onlineBuff);

// //这里不需要再继续接受后继数据包了,跳出当前循环体。

// continue;

//} //查找用户

if (_str.StartsWith(“Find::FindFriend”))

{

DataTable TabFind = TabUser.Clone();

DataRow [] FindRow =null ;

string UserName = _str.Substring(“Find::FindFriend”.Length,_str.Length - ”Find::FindFriend”.Length);

if (UserName.Equals(“Find::WhoOnLine”))

{ //看谁在线

FindRow = TabUser.Select(“ ZX = 1″);

}

else//精确查找

{

FindRow = TabUser.Select(“UserName = ‘” + UserName + ”‘”);

}

foreach (DataRow myrow in FindRow)

{

TabFind.ImportRow(myrow);

}

clientSkt.Send(Encoding.Unicode.GetBytes(“Find::FindFriend”));

IFormatter format = new BinaryFormatter();

MemoryStream stream = new MemoryStream();

format.Serialize(stream,TabFind);

stream.Position = 0;

byte[] ret = new byte[_maxPacket];

int count = 0;

count = stream.Read(ret,0,_maxPacket);

while (count >0)

{

clientSkt.Send(ret);

count = stream.Read(ret,0,_maxPacket);

}

clientSkt.Send(Encoding.Unicode.GetBytes(“Find::FindFriendEnd”));

stream.Close();

TabFind = null;

FindRow = null; //这里不需要再继续接受后继数据包了,跳出当前循环体。

continue;

} //请求添加好友

if (_str.StartsWith(“Find::AddFriendAsk”))

{

string UserName = _str.Substring(“Find::AddFriendAsk”.Length,_str.Length - ”Find::AddFriendAsk”.Length);

//通过转发表查找接收方的套接字

if (_transmit_tb.Count != 0 && _transmit_tb.ContainsKey(UserName))

{

Socket receiverSkt = _transmit_tb[UserName] as Socket;

receiverSkt.Send(Encoding.Unicode.GetBytes(“Find::AddFriendAsk” + obj as string));

}

//这里不需要再继续接受后继数据包了,跳出当前循环体。

continue;

}

//回复答应添加好友

if (_str.StartsWith(“Find::AddFriendYes”))

{

string UserName = _str.Substring(“Find::AddFriendYes”.Length,_str.Length - ”Find::AddFriendYes”.Length);

//// 保存数据

DataTable TabmyFriend = new DataTable() ;

//保存该用户

TabmyFriend.ReadXml(MyPath + ”\\UserFriend\\” + obj as string + ”.xml”);

DataRow newRow = TabmyFriend.NewRow();

newRow["UserName"] = UserName;

TabmyFriend.Rows.Add(newRow);

TabmyFriend.WriteXml(MyPath + ”\\UserFriend\\” + obj as string + ”.xml”,XmlWriteMode.WriteSchema,false);

//保存其好友

TabmyFriend = new DataTable();

TabmyFriend.ReadXml(MyPath + ”\\UserFriend\\” + UserName + ”.xml”);

DataRow newRow1 = TabmyFriend.NewRow();

newRow1["UserName"] = obj as string;

TabmyFriend.Rows.Add(newRow1);

TabmyFriend.WriteXml(MyPath + ”\\UserFriend\\” + UserName + ”.xml”,XmlWriteMode.WriteSchema,false);

TabmyFriend = null;

SerializeFriendList(obj,clientSkt);

//”开始”按钮事件

private void button1_Click(object sender,System.EventArgs e) {

//取得预保存的文件名

string fileName=textBox3.Text.Trim();

//远程主机

string hostName=textBox1.Text.Trim();

//端口

int port=Int32.Parse(textBox2.Text.Trim());

//得到主机信息

IPHostEntry ipInfo=Dns.GetHostByName(hostName);

//取得IPAddress[]

IPAddress[] ipAddr=ipInfo.AddressList;

//得到ip

IPAddress ip=ipAddr[0];

//组合出远程终结点

IPEndPoint hostEP=new IPEndPoint(ip,port);

//创建Socket 实例

Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);

try

{

//尝试连接

socket.Connect(hostEP);

}

catch(Exception se)

{

MessageBox.Show(“连接错误”+se.Message,”提示信息

,MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);

}

//发送给远程主机的请求内容串

string sendStr=”GET / HTTP/1.1\r\nHost: ” + hostName +

“\r\nConnection: Close\r\n\r\n”;

//创建bytes字节数组以转换发送串

byte[] bytesSendStr=new byte[1024];

//将发送内容字符串转换成字节byte数组

bytesSendStr=Encoding.ASCII.GetBytes(sendStr);

try

{

//向主机发送请求

socket.Send(bytesSendStr,bytesSendStr.Length,0);

}

catch(Exception ce)

{

MessageBox.Show(“发送错误:”+ce.Message,”提示信息

,MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);

}

//声明接收返回内容的字符串

string recvStr=”";

//声明字节数组,一次接收数据的长度为1024字节

byte[] recvBytes=new byte[1024];

//返回实际接收内容的字节数

int bytes=0;

//循环读取,直到接收完所有数据

while(true)

{

bytes=socket.Receive(recvBytes,recvBytes.Length,0);

//读取完成后退出循环

if(bytes〈=0)

break;

//将读取的字节数转换为字符串

recvStr+=Encoding.ASCII.GetString(recvBytes,0,bytes);

}

//将所读取的字符串转换为字节数组

byte[] content=Encoding.ASCII.GetBytes(recvStr);

try

{

//创建文件流对象实例

FileStream fs=new FileStream(fileName,FileMode.OpenOrCreate,FileAccess.ReadWrite);

//写入文件

fs.Write(content,0,content.Length);

}

catch(Exception fe)

{

MessageBox.Show(“文件创建/写入错误:”+fe.Message,”提示信息”,MessageBoxButtons.RetryCancel,MessageBoxIcon.Information);

}

//禁用Socket

socket.Shutdown(SocketShutdown.Both);

//关闭Socket

socket.Close();

}

}

posted @ 2017-05-05 11:59  我叫阿良❤善良的良  阅读(119)  评论(0)    收藏  举报